Repository: yudaocode/SpringBoot-Labs Branch: master Commit: 6c12efaed06d Files: 3513 Total size: 3.4 MB Directory structure: gitextract_g84ntzib/ ├── .gitignore ├── README.md ├── httpRequests/ │ ├── 2020-12-20T004250.200.json │ ├── 2020-12-20T004328.500.json │ ├── 2020-12-20T004337.200.json │ ├── 2020-12-20T004347.200.json │ ├── 2020-12-20T004347.500.json │ ├── 2020-12-20T004358.200.json │ ├── 2020-12-20T004358.500.json │ ├── 2020-12-20T004401.200.json │ ├── 2020-12-20T004401.500.json │ ├── 2020-12-20T004538.200.json │ ├── 2020-12-20T004547.500.json │ ├── 2020-12-20T004638.500.json │ ├── 2020-12-20T004645.500.json │ ├── 2020-12-20T004809.200.json │ ├── 2020-12-20T004813.500.json │ ├── 2020-12-20T010724.200.json │ ├── 2020-12-20T010738.200.json │ ├── 2020-12-20T010809.200.json │ ├── 2020-12-20T010823.500.json │ ├── 2020-12-20T010840.200.json │ ├── 2020-12-20T011020.500.json │ ├── 2020-12-20T011347.500.json │ ├── 2020-12-20T011526.200.json │ ├── 2020-12-20T011530.200.json │ ├── 2020-12-20T011541.400.json │ ├── 2020-12-20T011551.500.json │ ├── 2020-12-20T011556.200.json │ ├── 2020-12-20T011628-1.200.json │ ├── 2020-12-20T011628.200.json │ ├── 2020-12-20T011646.200.json │ ├── 2020-12-20T011650.200.json │ ├── 2020-12-20T011653.500.json │ ├── 2020-12-20T011818.200.json │ ├── 2020-12-20T011843.200.json │ ├── 2020-12-20T011847.500.json │ ├── 2020-12-20T012507.200.json │ ├── 2020-12-20T012510.200.json │ ├── 2020-12-20T012527.200.json │ ├── 2020-12-20T012540.400.json │ ├── 2020-12-20T012544.200.json │ ├── 2020-12-20T012703.500.json │ ├── 2020-12-20T012708.200.json │ ├── 2020-12-20T012710.200.json │ ├── 2020-12-20T013544.200.json │ ├── 2020-12-20T013552.200.json │ ├── 2020-12-20T013558.500.json │ ├── 2020-12-20T013845.200.json │ ├── 2020-12-20T014019.200.json │ ├── 2020-12-20T021415.200.json │ ├── http-client.cookies │ └── http-requests-log.http ├── lab-01-spring-security/ │ ├── lab-01-springsecurity-demo/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab01/ │ │ │ └── springsecurity/ │ │ │ ├── Application.java │ │ │ └── controller/ │ │ │ └── AdminController.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-01-springsecurity-demo-role/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab01/ │ │ └── springsecurity/ │ │ ├── Application.java │ │ ├── config/ │ │ │ └── SecurityConfig.java │ │ └── controller/ │ │ ├── DemoController.java │ │ └── TestController.java │ ├── pom.xml │ └── 《芋道 Spring Boot 安全框架 Spring Security 入门》.md ├── lab-02-spring-security-oauth/ │ ├── authorization-code-server/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── labs/ │ │ │ └── lab01/ │ │ │ ├── Application.java │ │ │ ├── authorization/ │ │ │ │ └── OAuth2AuthorizationServer.java │ │ │ └── resource/ │ │ │ ├── OAuth2ResourceServer.java │ │ │ └── api/ │ │ │ └── ExampleController.java │ │ └── resources/ │ │ └── application.properties │ ├── client-credentials-server/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── lab01/ │ │ ├── Application.java │ │ ├── authorization/ │ │ │ └── OAuth2AuthorizationServer.java │ │ └── resource/ │ │ ├── OAuth2ResourceServer.java │ │ └── api/ │ │ └── ExampleController.java │ ├── implicit-server/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── lab01/ │ │ │ ├── Application.java │ │ │ ├── authorization/ │ │ │ │ └── OAuth2AuthorizationServer.java │ │ │ └── resource/ │ │ │ ├── OAuth2ResourceServer.java │ │ │ └── api/ │ │ │ └── ExampleController.java │ │ └── resources/ │ │ └── application.properties │ ├── pom.xml │ ├── resource-owner-password-credentials-server/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── lab01/ │ │ │ ├── Application.java │ │ │ ├── authorization/ │ │ │ │ └── OAuth2AuthorizationServer.java │ │ │ └── resource/ │ │ │ ├── OAuth2ResourceServer.java │ │ │ └── api/ │ │ │ └── ExampleController.java │ │ └── resources/ │ │ └── application.properties │ ├── resource-owner-password-credentials-server-by-jdbc-token-store/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── lab02/ │ │ │ ├── Application.java │ │ │ ├── authorization/ │ │ │ │ └── OAuth2AuthorizationServer.java │ │ │ └── resource/ │ │ │ ├── OAuth2ResourceServer.java │ │ │ └── api/ │ │ │ └── ExampleController.java │ │ └── resources/ │ │ ├── application.properties │ │ ├── data.sql │ │ └── schema.sql │ ├── resource-owner-password-credentials-server-with-refresh-token/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── lab01/ │ │ │ ├── Application.java │ │ │ ├── authorization/ │ │ │ │ └── OAuth2AuthorizationServer.java │ │ │ └── resource/ │ │ │ ├── OAuth2ResourceServer.java │ │ │ └── api/ │ │ │ └── ExampleController.java │ │ └── resources/ │ │ └── application.properties │ ├── resource-owner-password-credentials-server-with-revoke-token/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── lab2/ │ │ │ ├── Application.java │ │ │ ├── authorization/ │ │ │ │ ├── OAuth2AuthorizationServer.java │ │ │ │ └── token/ │ │ │ │ └── TokenController.java │ │ │ └── resource/ │ │ │ ├── OAuth2ResourceServer.java │ │ │ └── api/ │ │ │ └── ExampleController.java │ │ └── resources/ │ │ └── application.properties │ ├── 《芋道 Spring Security OAuth2 入门(新)》.md │ └── 《芋道 Spring Security OAuth2 入门(老)》.md ├── lab-03-kafka/ │ ├── lab-03-kafka-demo/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab03/ │ │ │ │ └── kafkademo/ │ │ │ │ ├── Application.java │ │ │ │ ├── config/ │ │ │ │ │ └── KafkaConfiguration.java │ │ │ │ ├── consumer/ │ │ │ │ │ ├── Demo01AConsumer.java │ │ │ │ │ ├── Demo01Consumer.java │ │ │ │ │ └── Demo04Consumer.java │ │ │ │ ├── message/ │ │ │ │ │ ├── Demo01Message.java │ │ │ │ │ └── Demo04Message.java │ │ │ │ └── producer/ │ │ │ │ ├── Demo01Producer.java │ │ │ │ └── Demo04Producer.java │ │ │ └── resources/ │ │ │ ├── application.yaml │ │ │ └── application_bak.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab03/ │ │ └── kafkademo/ │ │ ├── package-info.java │ │ └── producer/ │ │ ├── Demo01ProducerTest.java │ │ └── Demo04ProducerTest.java │ ├── lab-03-kafka-demo-ack/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab03/ │ │ │ │ └── kafkademo/ │ │ │ │ ├── Application.java │ │ │ │ ├── consumer/ │ │ │ │ │ └── Demo08Consumer.java │ │ │ │ ├── message/ │ │ │ │ │ └── Demo08Message.java │ │ │ │ └── producer/ │ │ │ │ └── Demo08Producer.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab03/ │ │ └── kafkademo/ │ │ ├── package-info.java │ │ └── producer/ │ │ └── Demo08ProducerTest.java │ ├── lab-03-kafka-demo-batch/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab03/ │ │ │ │ └── kafkademo/ │ │ │ │ ├── Application.java │ │ │ │ ├── consumer/ │ │ │ │ │ └── Demo02Consumer.java │ │ │ │ ├── message/ │ │ │ │ │ └── Demo02Message.java │ │ │ │ └── producer/ │ │ │ │ └── Demo02Producer.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab03/ │ │ └── kafkademo/ │ │ ├── package-info.java │ │ └── producer/ │ │ └── Demo02ProducerTest.java │ ├── lab-03-kafka-demo-batch-consume/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab03/ │ │ │ │ └── kafkademo/ │ │ │ │ ├── Application.java │ │ │ │ ├── consumer/ │ │ │ │ │ └── Demo02Consumer.java │ │ │ │ ├── message/ │ │ │ │ │ └── Demo02Message.java │ │ │ │ └── producer/ │ │ │ │ └── Demo02Producer.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab03/ │ │ └── kafkademo/ │ │ ├── package-info.java │ │ └── producer/ │ │ └── Demo02ProducerTest.java │ ├── lab-03-kafka-demo-broadcast/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab03/ │ │ │ │ └── kafkademo/ │ │ │ │ ├── Application.java │ │ │ │ ├── consumer/ │ │ │ │ │ └── Demo05Consumer.java │ │ │ │ ├── message/ │ │ │ │ │ └── Demo05Message.java │ │ │ │ └── producer/ │ │ │ │ └── Demo05Producer.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab03/ │ │ └── kafkademo/ │ │ ├── package-info.java │ │ └── producer/ │ │ └── Demo05ProducerTest.java │ ├── lab-03-kafka-demo-concurrency/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab03/ │ │ │ │ └── kafkademo/ │ │ │ │ ├── Application.java │ │ │ │ ├── consumer/ │ │ │ │ │ └── Demo06Consumer.java │ │ │ │ ├── message/ │ │ │ │ │ └── Demo06Message.java │ │ │ │ └── producer/ │ │ │ │ └── Demo06Producer.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab03/ │ │ └── kafkademo/ │ │ ├── package-info.java │ │ └── producer/ │ │ └── Demo06ProducerTest.java │ ├── lab-03-kafka-demo-transaction/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab03/ │ │ │ │ └── kafkademo/ │ │ │ │ ├── Application.java │ │ │ │ ├── consumer/ │ │ │ │ │ └── Demo07Consumer.java │ │ │ │ ├── message/ │ │ │ │ │ └── Demo07Message.java │ │ │ │ └── producer/ │ │ │ │ └── Demo07Producer.java │ │ │ └── resources/ │ │ │ ├── application.yaml │ │ │ └── application_bak.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab03/ │ │ └── kafkademo/ │ │ ├── package-info.java │ │ └── producer/ │ │ └── Demo07ProducerTest.java │ ├── lab-03-kafka-native/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab03/ │ │ └── kafkademo/ │ │ ├── ConsumerMain.java │ │ └── ProducerMain.java │ ├── pom.xml │ └── 《芋道 Spring Boot 消息队列 Kafka 入门》.md ├── lab-04-rabbitmq/ │ ├── lab-04-rabbitmq-consume-retry/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab04/ │ │ │ │ └── rabbitmqdemo/ │ │ │ │ ├── Application.java │ │ │ │ ├── config/ │ │ │ │ │ └── RabbitConfig.java │ │ │ │ ├── consumer/ │ │ │ │ │ ├── Demo07Consumer.java │ │ │ │ │ └── Demo07DeadConsumer.java │ │ │ │ ├── message/ │ │ │ │ │ └── Demo07Message.java │ │ │ │ └── producer/ │ │ │ │ └── Demo07Producer.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab04/ │ │ └── rabbitmqdemo/ │ │ ├── package-info.java │ │ └── producer/ │ │ └── Demo07ProducerTest.java │ ├── lab-04-rabbitmq-demo/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab04/ │ │ │ │ └── rabbitmqdemo/ │ │ │ │ ├── Application.java │ │ │ │ ├── config/ │ │ │ │ │ └── RabbitConfig.java │ │ │ │ ├── consumer/ │ │ │ │ │ ├── Demo01Consumer.java │ │ │ │ │ ├── Demo02Consumer.java │ │ │ │ │ ├── Demo03ConsumerA.java │ │ │ │ │ ├── Demo03ConsumerB.java │ │ │ │ │ └── Demo04Consumer.java │ │ │ │ ├── message/ │ │ │ │ │ ├── Demo01Message.java │ │ │ │ │ ├── Demo02Message.java │ │ │ │ │ ├── Demo03Message.java │ │ │ │ │ └── Demo04Message.java │ │ │ │ └── producer/ │ │ │ │ ├── Demo01Producer.java │ │ │ │ ├── Demo02Producer.java │ │ │ │ ├── Demo03Producer.java │ │ │ │ └── Demo04Producer.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab04/ │ │ └── rabbitmqdemo/ │ │ ├── package-info.java │ │ └── producer/ │ │ ├── Demo01ProducerTest.java │ │ ├── Demo02ProducerTest.java │ │ ├── Demo03ProducerTest.java │ │ └── Demo04ProducerTest.java │ ├── lab-04-rabbitmq-demo-ack/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab04/ │ │ │ │ └── rabbitmqdemo/ │ │ │ │ ├── Application.java │ │ │ │ ├── config/ │ │ │ │ │ └── RabbitConfig.java │ │ │ │ ├── consumer/ │ │ │ │ │ └── Demo12Consumer.java │ │ │ │ ├── message/ │ │ │ │ │ └── Demo12Message.java │ │ │ │ └── producer/ │ │ │ │ └── Demo12Producer.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab04/ │ │ └── rabbitmqdemo/ │ │ ├── package-info.java │ │ └── producer/ │ │ └── Demo12ProducerTest.java │ ├── lab-04-rabbitmq-demo-batch/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab04/ │ │ │ │ └── rabbitmqdemo/ │ │ │ │ ├── Application.java │ │ │ │ ├── config/ │ │ │ │ │ └── RabbitConfig.java │ │ │ │ ├── consumer/ │ │ │ │ │ └── Demo05Consumer.java │ │ │ │ ├── message/ │ │ │ │ │ └── Demo05Message.java │ │ │ │ └── producer/ │ │ │ │ └── Demo05Producer.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab04/ │ │ └── rabbitmqdemo/ │ │ ├── package-info.java │ │ └── producer/ │ │ └── Demo05ProducerTest.java │ ├── lab-04-rabbitmq-demo-batch-consume/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab04/ │ │ │ │ └── rabbitmqdemo/ │ │ │ │ ├── Application.java │ │ │ │ ├── config/ │ │ │ │ │ └── RabbitConfig.java │ │ │ │ ├── consumer/ │ │ │ │ │ └── Demo05Consumer.java │ │ │ │ ├── message/ │ │ │ │ │ └── Demo05Message.java │ │ │ │ └── producer/ │ │ │ │ └── Demo06Producer.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab04/ │ │ └── rabbitmqdemo/ │ │ ├── package-info.java │ │ └── producer/ │ │ └── Demo05ProducerTest.java │ ├── lab-04-rabbitmq-demo-batch-consume-02/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab04/ │ │ │ │ └── rabbitmqdemo/ │ │ │ │ ├── Application.java │ │ │ │ ├── config/ │ │ │ │ │ └── RabbitConfig.java │ │ │ │ ├── consumer/ │ │ │ │ │ └── Demo06Consumer.java │ │ │ │ ├── message/ │ │ │ │ │ └── Demo06Message.java │ │ │ │ └── producer/ │ │ │ │ └── Demo06Producer.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab04/ │ │ └── rabbitmqdemo/ │ │ ├── package-info.java │ │ └── producer/ │ │ └── Demo06ProducerTest.java │ ├── lab-04-rabbitmq-demo-concurrency/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab04/ │ │ │ │ └── rabbitmqdemo/ │ │ │ │ ├── Application.java │ │ │ │ ├── config/ │ │ │ │ │ └── RabbitConfig.java │ │ │ │ ├── consumer/ │ │ │ │ │ └── Demo09Consumer.java │ │ │ │ ├── message/ │ │ │ │ │ └── Demo09Message.java │ │ │ │ └── producer/ │ │ │ │ └── Demo09Producer.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab04/ │ │ └── rabbitmqdemo/ │ │ ├── package-info.java │ │ └── producer/ │ │ └── Demo09ProducerTest.java │ ├── lab-04-rabbitmq-demo-confirm/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab04/ │ │ │ │ └── rabbitmqdemo/ │ │ │ │ ├── Application.java │ │ │ │ ├── config/ │ │ │ │ │ └── RabbitConfig.java │ │ │ │ ├── consumer/ │ │ │ │ │ └── Demo13Consumer.java │ │ │ │ ├── message/ │ │ │ │ │ └── Demo13Message.java │ │ │ │ └── producer/ │ │ │ │ └── Demo13Producer.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab04/ │ │ └── rabbitmqdemo/ │ │ ├── package-info.java │ │ └── producer/ │ │ └── Demo13ProducerTest.java │ ├── lab-04-rabbitmq-demo-confirm-async/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab04/ │ │ │ │ └── rabbitmqdemo/ │ │ │ │ ├── Application.java │ │ │ │ ├── config/ │ │ │ │ │ └── RabbitConfig.java │ │ │ │ ├── consumer/ │ │ │ │ │ └── Demo13Consumer.java │ │ │ │ ├── core/ │ │ │ │ │ ├── RabbitProducerConfirmCallback.java │ │ │ │ │ └── RabbitProducerReturnCallback.java │ │ │ │ ├── message/ │ │ │ │ │ └── Demo13Message.java │ │ │ │ └── producer/ │ │ │ │ └── Demo13Producer.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab04/ │ │ └── rabbitmqdemo/ │ │ ├── package-info.java │ │ └── producer/ │ │ └── Demo13ProducerTest.java │ ├── lab-04-rabbitmq-demo-delay/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab04/ │ │ │ │ └── rabbitmqdemo/ │ │ │ │ ├── Application.java │ │ │ │ ├── config/ │ │ │ │ │ └── RabbitConfig.java │ │ │ │ ├── consumer/ │ │ │ │ │ └── Demo08Consumer.java │ │ │ │ ├── message/ │ │ │ │ │ └── Demo08Message.java │ │ │ │ └── producer/ │ │ │ │ └── Demo08Producer.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab04/ │ │ └── rabbitmqdemo/ │ │ ├── package-info.java │ │ └── producer/ │ │ └── Demo08ProducerTest.java │ ├── lab-04-rabbitmq-demo-error-handler/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab04/ │ │ │ │ └── rabbitmqdemo/ │ │ │ │ ├── Application.java │ │ │ │ ├── config/ │ │ │ │ │ └── RabbitConfig.java │ │ │ │ ├── consumer/ │ │ │ │ │ └── Demo16Consumer.java │ │ │ │ ├── core/ │ │ │ │ │ ├── RabbitListenerErrorHandlerImpl.java │ │ │ │ │ └── RabbitLoggingErrorHandler.java │ │ │ │ ├── message/ │ │ │ │ │ └── Demo16Message.java │ │ │ │ └── producer/ │ │ │ │ └── Demo16Producer.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab04/ │ │ └── rabbitmqdemo/ │ │ ├── package-info.java │ │ └── producer/ │ │ └── Demo16ProducerTest.java │ ├── lab-04-rabbitmq-demo-json/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab04/ │ │ │ │ └── rabbitmqdemo/ │ │ │ │ ├── Application.java │ │ │ │ ├── config/ │ │ │ │ │ └── RabbitConfig.java │ │ │ │ ├── consumer/ │ │ │ │ │ └── Demo15Consumer.java │ │ │ │ ├── message/ │ │ │ │ │ └── Demo15Message.java │ │ │ │ └── producer/ │ │ │ │ └── Demo15Producer.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab04/ │ │ └── rabbitmqdemo/ │ │ ├── package-info.java │ │ └── producer/ │ │ └── Demo15ProducerTest.java │ ├── lab-04-rabbitmq-demo-message-model/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab04/ │ │ │ │ └── rabbitmqdemo/ │ │ │ │ ├── Application.java │ │ │ │ ├── config/ │ │ │ │ │ └── RabbitConfig.java │ │ │ │ ├── consumer/ │ │ │ │ │ ├── BroadcastConsumer.java │ │ │ │ │ └── ClusteringConsumer.java │ │ │ │ ├── message/ │ │ │ │ │ ├── BroadcastMessage.java │ │ │ │ │ └── ClusteringMessage.java │ │ │ │ └── producer/ │ │ │ │ ├── BroadcastProducer.java │ │ │ │ └── ClusteringProducer.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab04/ │ │ └── rabbitmqdemo/ │ │ ├── package-info.java │ │ └── producer/ │ │ ├── BroadcastProducerTest.java │ │ └── ClusteringProducerTest.java │ ├── lab-04-rabbitmq-demo-orderly/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab04/ │ │ │ │ └── rabbitmqdemo/ │ │ │ │ ├── Application.java │ │ │ │ ├── config/ │ │ │ │ │ └── RabbitConfig.java │ │ │ │ ├── consumer/ │ │ │ │ │ └── Demo10Consumer.java │ │ │ │ ├── message/ │ │ │ │ │ └── Demo10Message.java │ │ │ │ └── producer/ │ │ │ │ └── Demo10Producer.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab04/ │ │ └── rabbitmqdemo/ │ │ ├── package-info.java │ │ └── producer/ │ │ └── Demo10ProducerTest.java │ ├── lab-04-rabbitmq-demo-rpc/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab04/ │ │ │ │ └── rabbitmqdemo/ │ │ │ │ ├── Application.java │ │ │ │ ├── config/ │ │ │ │ │ └── RabbitConfig.java │ │ │ │ ├── consumer/ │ │ │ │ │ └── Demo14Consumer.java │ │ │ │ ├── message/ │ │ │ │ │ └── Demo14Message.java │ │ │ │ └── producer/ │ │ │ │ └── Demo14Producer.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab04/ │ │ └── rabbitmqdemo/ │ │ ├── package-info.java │ │ └── producer/ │ │ └── Demo14ProducerTest.java │ ├── lab-04-rabbitmq-demo-transaction/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab04/ │ │ │ │ └── rabbitmqdemo/ │ │ │ │ ├── Application.java │ │ │ │ ├── config/ │ │ │ │ │ └── RabbitConfig.java │ │ │ │ ├── consumer/ │ │ │ │ │ └── Demo11Consumer.java │ │ │ │ ├── message/ │ │ │ │ │ └── Demo11Message.java │ │ │ │ └── producer/ │ │ │ │ └── Demo11Producer.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab04/ │ │ └── rabbitmqdemo/ │ │ ├── package-info.java │ │ └── producer/ │ │ └── Demo11ProducerTest.java │ ├── lab-04-rabbitmq-native/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab04/ │ │ └── rabbitmqdemo/ │ │ ├── RabbitMQConsumer.java │ │ └── RabbitMQProducer.java │ ├── pom.xml │ └── 《芋道 Spring Boot 消息队列 RabbitMQ 入门》.md ├── lab-05-benchmark-tomcat-jetty-undertow/ │ ├── lab-05-jetty/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── labs/ │ │ └── lab05/ │ │ └── tomcat/ │ │ ├── Controller.java │ │ └── JettyApplication.java │ ├── lab-05-tomcat/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── labs/ │ │ └── lab05/ │ │ └── tomcat/ │ │ ├── Controller.java │ │ └── TomcatApplication.java │ ├── lab-05-tomcat-apr/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── labs/ │ │ └── lab05/ │ │ └── tomcat/ │ │ ├── Controller.java │ │ └── TomcatAprApplication.java │ ├── lab-05-undertow/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── labs/ │ │ └── lab05/ │ │ └── undertow/ │ │ ├── Controller.java │ │ └── UndertowApplication.java │ ├── pom.xml │ └── 《性能测试 —— Tomcat、Jetty、Undertow 基准测试》.md ├── lab-06/ │ ├── lab-06-springmvc-tomcat/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── labs/ │ │ └── lab06/ │ │ └── springmvc/ │ │ ├── Controller.java │ │ └── SpringMVCApplication.java │ ├── lab-06-webflux-netty/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── labs/ │ │ └── lab06/ │ │ └── webflux/ │ │ ├── Controller.java │ │ └── WebfluxNettyApplication.java │ ├── lab-06-webflux-tomcat/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── labs/ │ │ └── lab06/ │ │ └── webflux/ │ │ ├── Controller.java │ │ └── WebfluxTomcatApplication.java │ ├── pom.xml │ └── 《性能测试 —— SpringMVC、Webflux 基准测试》.md ├── lab-07/ │ ├── lab-07-spring-cloud-gateway/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── labs/ │ │ │ └── lab07/ │ │ │ └── springcloudgateway/ │ │ │ └── SpringCloudGatewayApplication.java │ │ └── resources/ │ │ └── application.yml │ ├── lab-07-zuul/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── labs/ │ │ │ └── lab07/ │ │ │ └── zuul/ │ │ │ └── ZuulApplication.java │ │ └── resources/ │ │ └── application.yml │ ├── pom.xml │ └── 《性能测试 —— Spring Cloud Gateway、Zuul 基准测试》.md ├── lab-11-spring-data-redis/ │ ├── lab-07-spring-data-redis-unit-test/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── labs/ │ │ │ │ └── lab10/ │ │ │ │ └── springdatarediswithjedis/ │ │ │ │ ├── Application.java │ │ │ │ ├── config/ │ │ │ │ │ └── RedisConfiguration.java │ │ │ │ └── util/ │ │ │ │ └── JSONUtil.java │ │ │ └── resources/ │ │ │ ├── application.yml │ │ │ └── redisson.yml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── labs/ │ │ └── lab10/ │ │ └── springdatarediswithjedis/ │ │ ├── Test01.java │ │ └── config/ │ │ └── TestRedisConfiguration.java │ ├── lab-07-spring-data-redis-with-jedis/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── labs/ │ │ │ │ └── lab10/ │ │ │ │ └── springdatarediswithjedis/ │ │ │ │ ├── Application.java │ │ │ │ ├── cacheobject/ │ │ │ │ │ ├── ProductCacheObject.java │ │ │ │ │ └── UserCacheObject.java │ │ │ │ ├── config/ │ │ │ │ │ └── RedisConfiguration.java │ │ │ │ ├── dao/ │ │ │ │ │ ├── package-info.java │ │ │ │ │ └── redis/ │ │ │ │ │ └── UserCacheDao.java │ │ │ │ ├── listener/ │ │ │ │ │ ├── TestChannelTopicMessageListener.java │ │ │ │ │ └── TestPatternTopicMessageListener.java │ │ │ │ ├── service/ │ │ │ │ │ ├── UserService01.java │ │ │ │ │ └── UserService02.java │ │ │ │ └── util/ │ │ │ │ └── JSONUtil.java │ │ │ └── resources/ │ │ │ ├── application.yml │ │ │ └── lua/ │ │ │ ├── compareAndSet.lua │ │ │ ├── compareAndSet2.lua │ │ │ └── test.lua │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── labs/ │ │ └── lab10/ │ │ └── springdatarediswithjedis/ │ │ ├── PipelineTest.java │ │ ├── PubSubTest.java │ │ ├── ScriptTest.java │ │ ├── SessionTest.java │ │ ├── Test01.java │ │ ├── TransactionTest.java │ │ ├── UserService02Test.java │ │ └── package-info.java │ ├── lab-07-spring-data-redis-with-redisson/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── labs/ │ │ │ │ └── lab10/ │ │ │ │ └── springdatarediswithjedis/ │ │ │ │ ├── Application.java │ │ │ │ ├── cacheobject/ │ │ │ │ │ ├── ProductCacheObject.java │ │ │ │ │ └── UserCacheObject.java │ │ │ │ ├── config/ │ │ │ │ │ └── RedisConfiguration.java │ │ │ │ ├── dao/ │ │ │ │ │ ├── package-info.java │ │ │ │ │ └── redis/ │ │ │ │ │ └── UserCacheDao.java │ │ │ │ ├── listener/ │ │ │ │ │ ├── TestChannelTopicMessageListener.java │ │ │ │ │ └── TestPatternTopicMessageListener.java │ │ │ │ ├── service/ │ │ │ │ │ ├── UserService01.java │ │ │ │ │ └── UserService02.java │ │ │ │ └── util/ │ │ │ │ └── JSONUtil.java │ │ │ └── resources/ │ │ │ ├── application.yml │ │ │ ├── lua/ │ │ │ │ ├── compareAndSet.lua │ │ │ │ ├── compareAndSet2.lua │ │ │ │ └── test.lua │ │ │ └── redisson.yml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── labs/ │ │ └── lab10/ │ │ └── springdatarediswithjedis/ │ │ ├── LockTest.java │ │ ├── PipelineTest.java │ │ ├── PubSubTest.java │ │ ├── RateLimiterTest.java │ │ ├── ScriptTest.java │ │ ├── SessionTest.java │ │ ├── Test01.java │ │ ├── TransactionTest.java │ │ ├── UserService02Test.java │ │ └── package-info.java │ ├── pom.xml │ └── 《芋道 Spring Boot Redis 入门》.md ├── lab-12-mybatis/ │ ├── lab-12-mybatis-annotation/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab12/ │ │ │ │ └── mybatis/ │ │ │ │ ├── Application.java │ │ │ │ ├── dataobject/ │ │ │ │ │ └── UserDO.java │ │ │ │ └── mapper/ │ │ │ │ └── UserMapper.java │ │ │ └── resources/ │ │ │ ├── application.yaml │ │ │ ├── mapper/ │ │ │ │ └── UserMapper.xml │ │ │ ├── mybatis-config.xml │ │ │ └── sql/ │ │ │ └── users.sql │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab12/ │ │ └── mybatis/ │ │ └── mapper/ │ │ └── UserMapperTest.java │ ├── lab-12-mybatis-plus/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab12/ │ │ │ │ └── mybatis/ │ │ │ │ ├── Application.java │ │ │ │ ├── dataobject/ │ │ │ │ │ └── UserDO.java │ │ │ │ └── mapper/ │ │ │ │ └── UserMapper.java │ │ │ └── resources/ │ │ │ ├── application.yaml │ │ │ ├── mapper/ │ │ │ │ └── UserMapper.xml │ │ │ └── sql/ │ │ │ └── users.sql │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab12/ │ │ └── mybatis/ │ │ └── mapper/ │ │ └── UserMapperTest.java │ ├── lab-12-mybatis-plus-tenant/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab12/ │ │ │ │ └── mybatis/ │ │ │ │ ├── Application.java │ │ │ │ ├── config/ │ │ │ │ │ ├── AsyncConfig.java │ │ │ │ │ └── MybatisPlusConfig.java │ │ │ │ ├── context/ │ │ │ │ │ └── TenantHolder.java │ │ │ │ ├── core/ │ │ │ │ │ └── TtlThreadPoolTaskExecutor.java │ │ │ │ ├── dataobject/ │ │ │ │ │ ├── UserDO.java │ │ │ │ │ └── UserProfileDO.java │ │ │ │ ├── mapper/ │ │ │ │ │ ├── UserMapper.java │ │ │ │ │ └── UserProfileMapper.java │ │ │ │ ├── service/ │ │ │ │ │ └── UserService.java │ │ │ │ ├── util/ │ │ │ │ │ └── TtlExecutorsUtil.java │ │ │ │ └── vo/ │ │ │ │ └── UserDetailVO.java │ │ │ └── resources/ │ │ │ ├── application.yaml │ │ │ ├── mapper/ │ │ │ │ └── UserMapper.xml │ │ │ └── sql/ │ │ │ └── users.sql │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab12/ │ │ └── mybatis/ │ │ ├── mapper/ │ │ │ └── UserMapperTest.java │ │ └── service/ │ │ └── UserServiceTest.java │ ├── lab-12-mybatis-tk/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab12/ │ │ │ │ └── mybatis/ │ │ │ │ ├── Application.java │ │ │ │ ├── dataobject/ │ │ │ │ │ └── UserDO.java │ │ │ │ ├── mapper/ │ │ │ │ │ └── UserMapper.java │ │ │ │ └── util/ │ │ │ │ └── BaseMapper.java │ │ │ └── resources/ │ │ │ ├── application.yaml │ │ │ ├── mapper/ │ │ │ │ └── UserMapper.xml │ │ │ ├── mybatis-config.xml │ │ │ └── sql/ │ │ │ └── users.sql │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab12/ │ │ └── mybatis/ │ │ └── mapper/ │ │ └── UserMapperTest.java │ ├── lab-12-mybatis-xml/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab12/ │ │ │ │ └── mybatis/ │ │ │ │ ├── Application.java │ │ │ │ ├── dataobject/ │ │ │ │ │ └── UserDO.java │ │ │ │ └── mapper/ │ │ │ │ └── UserMapper.java │ │ │ └── resources/ │ │ │ ├── application.yaml │ │ │ ├── mapper/ │ │ │ │ └── UserMapper.xml │ │ │ ├── mybatis-config.xml │ │ │ └── sql/ │ │ │ └── users.sql │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab12/ │ │ └── mybatis/ │ │ └── mapper/ │ │ └── UserMapperTest.java │ ├── pom.xml │ └── 《芋道 Spring Boot MyBatis 入门》.md ├── lab-13-spring-data-jpa/ │ ├── lab-13-jpa/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab13/ │ │ │ │ └── jpa/ │ │ │ │ ├── Application.java │ │ │ │ ├── dataobject/ │ │ │ │ │ └── UserDO.java │ │ │ │ └── repository/ │ │ │ │ ├── UserRepository01.java │ │ │ │ ├── UserRepository02.java │ │ │ │ ├── UserRepository03.java │ │ │ │ └── UserRepository04.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab13/ │ │ └── jpa/ │ │ └── repository/ │ │ ├── UserRepository01Test.java │ │ ├── UserRepository02Test.java │ │ ├── UserRepository03Test.java │ │ └── UserRepository04Test.java │ ├── pom.xml │ └── 《芋道 Spring Boot JPA 入门》.md ├── lab-14-spring-jdbc-template/ │ ├── lab-14-jdbctemplate/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab14/ │ │ │ │ └── jdbctemplate/ │ │ │ │ ├── Application.java │ │ │ │ ├── dao/ │ │ │ │ │ └── UserDao.java │ │ │ │ └── dataobject/ │ │ │ │ └── UserDO.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab14/ │ │ └── jdbctemplate/ │ │ └── dao/ │ │ └── UserDaoTest.java │ ├── pom.xml │ └── 《芋道 Spring Boot JdbcTemplate 入门》.md ├── lab-15-spring-data-es/ │ ├── lab-15-spring-data-elasticsearch/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab15/ │ │ │ │ └── springdataelasticsearch/ │ │ │ │ ├── Application.java │ │ │ │ ├── bo/ │ │ │ │ │ └── ProductConditionBO.java │ │ │ │ ├── constant/ │ │ │ │ │ └── FieldAnalyzer.java │ │ │ │ ├── dataobject/ │ │ │ │ │ └── ESProductDO.java │ │ │ │ └── repository/ │ │ │ │ └── ProductRepository.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab15/ │ │ └── springdataelasticsearch/ │ │ └── repository/ │ │ ├── ProductRepository04Test.java │ │ └── ProductRepositoryTest.java │ ├── lab-15-spring-data-jest/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab15/ │ │ │ │ └── springdatajest/ │ │ │ │ ├── Application.java │ │ │ │ ├── constant/ │ │ │ │ │ └── FieldAnalyzer.java │ │ │ │ ├── dataobject/ │ │ │ │ │ └── ESProductDO.java │ │ │ │ └── repository/ │ │ │ │ ├── ProductRepository.java │ │ │ │ ├── ProductRepository02.java │ │ │ │ └── ProductRepository03.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab15/ │ │ └── springdatajest/ │ │ └── repository/ │ │ ├── ProductRepository02Test.java │ │ ├── ProductRepository03Test.java │ │ └── ProductRepositoryTest.java │ ├── pom.xml │ └── 《芋道 Spring Boot Elasticsearch 入门》.md ├── lab-16-spring-data-mongo/ │ ├── lab-16-spring-data-mongodb/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab16/ │ │ │ │ └── springdatamongodb/ │ │ │ │ ├── Application.java │ │ │ │ ├── config/ │ │ │ │ │ └── MongoDBConfig.java │ │ │ │ ├── dao/ │ │ │ │ │ └── UserDao.java │ │ │ │ ├── dataobject/ │ │ │ │ │ ├── ProductDO.java │ │ │ │ │ └── UserDO.java │ │ │ │ ├── mongo/ │ │ │ │ │ ├── IncIdEntity.java │ │ │ │ │ └── MongoInsertEventListener.java │ │ │ │ └── repository/ │ │ │ │ ├── ProductRepository.java │ │ │ │ ├── UserRepository.java │ │ │ │ ├── UserRepository02.java │ │ │ │ └── UserRepository03.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab16/ │ │ └── springdatamongodb/ │ │ ├── dao/ │ │ │ └── UserDaoTest.java │ │ └── repository/ │ │ ├── ProductRepositoryTest.java │ │ ├── UserRepository02Test.java │ │ ├── UserRepository03Test.java │ │ └── UserRepositoryTest.java │ ├── pom.xml │ └── 《芋道 Spring Boot MongoDB 入门》.md ├── lab-17/ │ ├── lab-17-dynamic-datasource-baomidou-01/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab17/ │ │ │ │ └── dynamicdatasource/ │ │ │ │ ├── Application.java │ │ │ │ ├── constant/ │ │ │ │ │ └── DBConstants.java │ │ │ │ ├── dataobject/ │ │ │ │ │ ├── OrderDO.java │ │ │ │ │ └── UserDO.java │ │ │ │ ├── mapper/ │ │ │ │ │ ├── OrderMapper.java │ │ │ │ │ └── UserMapper.java │ │ │ │ └── service/ │ │ │ │ └── OrderService.java │ │ │ └── resources/ │ │ │ ├── application.yaml │ │ │ ├── mapper/ │ │ │ │ ├── OrderMapper.xml │ │ │ │ └── UserMapper.xml │ │ │ ├── mybatis-config.xml │ │ │ └── sql/ │ │ │ └── db.sql │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab17/ │ │ └── dynamicdatasource/ │ │ ├── mapper/ │ │ │ ├── OrderMapperTest.java │ │ │ └── UserMapperTest.java │ │ └── service/ │ │ └── OrderServiceTest.java │ ├── lab-17-dynamic-datasource-baomidou-02/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab17/ │ │ │ │ └── dynamicdatasource/ │ │ │ │ ├── Application.java │ │ │ │ ├── constant/ │ │ │ │ │ └── DBConstants.java │ │ │ │ ├── dataobject/ │ │ │ │ │ └── OrderDO.java │ │ │ │ ├── mapper/ │ │ │ │ │ └── OrderMapper.java │ │ │ │ └── service/ │ │ │ │ └── OrderService.java │ │ │ └── resources/ │ │ │ ├── application.yaml │ │ │ ├── mapper/ │ │ │ │ └── OrderMapper.xml │ │ │ ├── mybatis-config.xml │ │ │ └── sql/ │ │ │ └── db.sql │ │ └── test/ │ │ └── java/ │ │ └── dynamicdatasource/ │ │ ├── mapper/ │ │ │ └── OrderMapperTest.java │ │ └── service/ │ │ └── OrderServiceTest.java │ ├── lab-17-dynamic-datasource-jdbctemplate/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab17/ │ │ │ │ └── dynamicdatasource/ │ │ │ │ ├── Application.java │ │ │ │ ├── config/ │ │ │ │ │ ├── JdbcTemplateOrdersConfig.java │ │ │ │ │ └── JdbcTemplateUsersConfig.java │ │ │ │ ├── constant/ │ │ │ │ │ └── DBConstants.java │ │ │ │ ├── dao/ │ │ │ │ │ ├── OrderDao.java │ │ │ │ │ └── UserDao.java │ │ │ │ ├── dataobject/ │ │ │ │ │ ├── OrderDO.java │ │ │ │ │ └── UserDO.java │ │ │ │ └── service/ │ │ │ │ └── OrderService.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab17/ │ │ └── dynamicdatasource/ │ │ ├── dao/ │ │ │ ├── OrderDaoTest.java │ │ │ └── UserDaoTest.java │ │ ├── package-info.java │ │ └── service/ │ │ └── OrderServiceTest.java │ ├── lab-17-dynamic-datasource-mybatis/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab17/ │ │ │ │ └── dynamicdatasource/ │ │ │ │ ├── Application.java │ │ │ │ ├── config/ │ │ │ │ │ ├── MyBatisOrdersConfig.java │ │ │ │ │ └── MyBatisUsersConfig.java │ │ │ │ ├── constant/ │ │ │ │ │ └── DBConstants.java │ │ │ │ ├── dataobject/ │ │ │ │ │ ├── OrderDO.java │ │ │ │ │ └── UserDO.java │ │ │ │ ├── mapper/ │ │ │ │ │ ├── orders/ │ │ │ │ │ │ └── OrderMapper.java │ │ │ │ │ └── users/ │ │ │ │ │ └── UserMapper.java │ │ │ │ └── service/ │ │ │ │ └── OrderService.java │ │ │ └── resources/ │ │ │ ├── application.yaml │ │ │ ├── mapper/ │ │ │ │ ├── orders/ │ │ │ │ │ └── OrderMapper.xml │ │ │ │ └── users/ │ │ │ │ └── UserMapper.xml │ │ │ ├── mybatis-config.xml │ │ │ └── sql/ │ │ │ └── db.sql │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab17/ │ │ └── dynamicdatasource/ │ │ ├── mapper/ │ │ │ ├── orders/ │ │ │ │ └── OrderMapperTest.java │ │ │ └── users/ │ │ │ └── UserMapperTest.java │ │ ├── package-info.java │ │ └── service/ │ │ └── OrderServiceTest.java │ ├── lab-17-dynamic-datasource-sharding-jdbc-01/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab17/ │ │ │ │ └── dynamicdatasource/ │ │ │ │ ├── Application.java │ │ │ │ ├── dataobject/ │ │ │ │ │ ├── OrderDO.java │ │ │ │ │ └── UserDO.java │ │ │ │ ├── mapper/ │ │ │ │ │ ├── OrderMapper.java │ │ │ │ │ └── UserMapper.java │ │ │ │ └── service/ │ │ │ │ └── OrderService.java │ │ │ └── resources/ │ │ │ ├── application.yaml │ │ │ ├── mapper/ │ │ │ │ ├── OrderMapper.xml │ │ │ │ └── UserMapper.xml │ │ │ ├── mybatis-config.xml │ │ │ └── sql/ │ │ │ └── db.sql │ │ └── test/ │ │ └── java/ │ │ └── dynamicdatasource/ │ │ ├── mapper/ │ │ │ ├── OrderMapperTest.java │ │ │ └── UserMapperTest.java │ │ └── service/ │ │ └── OrderServiceTest.java │ ├── lab-17-dynamic-datasource-sharding-jdbc-02/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab17/ │ │ │ │ └── dynamicdatasource/ │ │ │ │ ├── Application.java │ │ │ │ ├── dataobject/ │ │ │ │ │ └── OrderDO.java │ │ │ │ ├── mapper/ │ │ │ │ │ └── OrderMapper.java │ │ │ │ └── service/ │ │ │ │ └── OrderService.java │ │ │ └── resources/ │ │ │ ├── application.yaml │ │ │ ├── mapper/ │ │ │ │ └── OrderMapper.xml │ │ │ ├── mybatis-config.xml │ │ │ └── sql/ │ │ │ └── db.sql │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab17/ │ │ └── dynamicdatasource/ │ │ ├── mapper/ │ │ │ └── OrderMapperTest.java │ │ └── service/ │ │ └── OrderServiceTest.java │ ├── lab-17-dynamic-datasource-springdatajpa/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab17/ │ │ │ │ └── dynamicdatasource/ │ │ │ │ ├── Application.java │ │ │ │ ├── config/ │ │ │ │ │ ├── HibernateConfig.java │ │ │ │ │ ├── JpaOrdersConfig.java │ │ │ │ │ └── JpaUsersConfig.java │ │ │ │ ├── constant/ │ │ │ │ │ └── DBConstants.java │ │ │ │ ├── dataobject/ │ │ │ │ │ ├── OrderDO.java │ │ │ │ │ └── UserDO.java │ │ │ │ ├── repository/ │ │ │ │ │ ├── orders/ │ │ │ │ │ │ └── OrderRepository.java │ │ │ │ │ └── users/ │ │ │ │ │ └── UserRepository.java │ │ │ │ └── service/ │ │ │ │ └── OrderService.java │ │ │ └── resources/ │ │ │ ├── application.yaml │ │ │ └── sql/ │ │ │ └── db.sql │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab17/ │ │ └── dynamicdatasource/ │ │ ├── package-info.java │ │ ├── repository/ │ │ │ ├── orders/ │ │ │ │ └── OrderRepositoryTest.java │ │ │ └── users/ │ │ │ └── UserRepositoryTest.java │ │ └── service/ │ │ └── OrderServiceTest.java │ ├── pom.xml │ └── 《芋道 Spring Boot 多数据源(读写分离)入门》.md ├── lab-18/ │ ├── lab-18-sharding-datasource-01/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab18/ │ │ │ │ └── shardingdatasource/ │ │ │ │ ├── Application.java │ │ │ │ ├── dataobject/ │ │ │ │ │ ├── OrderConfigDO.java │ │ │ │ │ └── OrderDO.java │ │ │ │ └── mapper/ │ │ │ │ ├── OrderConfigMapper.java │ │ │ │ └── OrderMapper.java │ │ │ └── resources/ │ │ │ ├── application.yaml │ │ │ ├── mapper/ │ │ │ │ ├── OrderConfigMapper.xml │ │ │ │ └── OrderMapper.xml │ │ │ ├── mybatis-config.xml │ │ │ └── sql/ │ │ │ ├── lab18_orders_0.sql │ │ │ └── lab18_orders_1.sql │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab18/ │ │ └── shardingdatasource/ │ │ └── mapper/ │ │ ├── OrderConfigMapperTest.java │ │ └── OrderMapperTest.java │ ├── lab-18-sharding-datasource-02/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab18/ │ │ │ │ └── shardingdatasource/ │ │ │ │ ├── Application.java │ │ │ │ ├── dataobject/ │ │ │ │ │ └── OrderDO.java │ │ │ │ ├── mapper/ │ │ │ │ │ └── OrderMapper.java │ │ │ │ └── service/ │ │ │ │ └── OrderService.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab18/ │ │ └── shardingdatasource/ │ │ ├── mapper/ │ │ │ └── OrderMapperTest.java │ │ └── service/ │ │ └── OrderServiceTest.java │ ├── pom.xml │ └── 《芋道 Spring Boot 分库分表入门》.md ├── lab-19/ │ ├── lab-19-datasource-pool-druid-multiple/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab19/ │ │ │ └── datasourcepool/ │ │ │ ├── Application.java │ │ │ └── config/ │ │ │ └── DataSourceConfig.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-19-datasource-pool-druid-single/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab19/ │ │ │ └── datasourcepool/ │ │ │ ├── Application.java │ │ │ └── controller/ │ │ │ └── DruidStatController.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-19-datasource-pool-hikaricp-multiple/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab19/ │ │ │ └── datasourcepool/ │ │ │ ├── Application.java │ │ │ └── config/ │ │ │ └── DataSourceConfig.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-19-datasource-pool-hikaricp-single/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab19/ │ │ │ └── datasourcepool/ │ │ │ └── Application.java │ │ └── resources/ │ │ └── application.yaml │ ├── pom.xml │ └── 《芋道 Spring Boot 数据库连接池入门》.md ├── lab-20/ │ ├── lab-20-database-version-control-flyway/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab20/ │ │ │ └── databaseversioncontrol/ │ │ │ ├── Application.java │ │ │ └── migration/ │ │ │ ├── ExampleFlywayCallback.java │ │ │ └── V1_1__FixUsername.java │ │ └── resources/ │ │ ├── application.yaml │ │ └── db/ │ │ └── migration/ │ │ └── V1.0__INIT_DB.sql │ ├── lab-20-database-version-control-liquibase/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab20/ │ │ │ └── databaseversioncontrol/ │ │ │ ├── Application.java │ │ │ └── migration/ │ │ │ └── CHANGE_SET_3_FixUsername.java │ │ └── resources/ │ │ ├── application.yaml │ │ └── db/ │ │ └── changelog/ │ │ ├── db.changelog-master-bak.yaml │ │ ├── db.changelog-master.yaml │ │ └── sqlfile/ │ │ └── CHAGE_SET_2_INIT_DB.sql │ ├── pom.xml │ └── 《芋道 Spring Boot 数据库版本管理入门》.md ├── lab-21/ │ ├── lab-21-cache-demo/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab21/ │ │ │ │ └── cache/ │ │ │ │ ├── Application.java │ │ │ │ ├── dataobject/ │ │ │ │ │ └── UserDO.java │ │ │ │ ├── mapper/ │ │ │ │ │ ├── UserCacheDao.java │ │ │ │ │ └── UserMapper.java │ │ │ │ └── service/ │ │ │ │ └── UserService.java │ │ │ └── resources/ │ │ │ ├── application.yaml │ │ │ └── sql/ │ │ │ └── users.sql │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab21/ │ │ └── cache/ │ │ └── UserMapperTest.java │ ├── lab-21-cache-ehcache/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab21/ │ │ │ │ └── cache/ │ │ │ │ ├── Application.java │ │ │ │ ├── dataobject/ │ │ │ │ │ └── UserDO.java │ │ │ │ └── mapper/ │ │ │ │ └── UserMapper.java │ │ │ └── resources/ │ │ │ ├── application.yaml │ │ │ ├── ehcache.xml │ │ │ └── sql/ │ │ │ └── users.sql │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab21/ │ │ └── cache/ │ │ └── UserMapperTest.java │ ├── lab-21-cache-redis/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab21/ │ │ │ │ └── cache/ │ │ │ │ ├── Application.java │ │ │ │ ├── dataobject/ │ │ │ │ │ └── UserDO.java │ │ │ │ └── mapper/ │ │ │ │ └── UserMapper.java │ │ │ └── resources/ │ │ │ ├── application.yaml │ │ │ └── sql/ │ │ │ └── users.sql │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab21/ │ │ └── cache/ │ │ └── UserMapperTest.java │ ├── pom.xml │ └── 《芋道 Spring Boot 缓存 Cache 入门》.md ├── lab-22/ │ ├── lab-22-validation-01/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab22/ │ │ │ │ └── validation/ │ │ │ │ ├── Application.java │ │ │ │ ├── config/ │ │ │ │ │ └── ValidationConfiguration.java │ │ │ │ ├── constants/ │ │ │ │ │ ├── GenderEnum.java │ │ │ │ │ └── ServiceExceptionEnum.java │ │ │ │ ├── controller/ │ │ │ │ │ └── UserController.java │ │ │ │ ├── core/ │ │ │ │ │ ├── exception/ │ │ │ │ │ │ └── ServiceException.java │ │ │ │ │ ├── package-info.java │ │ │ │ │ ├── validator/ │ │ │ │ │ │ ├── InEnum.java │ │ │ │ │ │ ├── InEnumValidator.java │ │ │ │ │ │ └── IntArrayValuable.java │ │ │ │ │ ├── vo/ │ │ │ │ │ │ └── CommonResult.java │ │ │ │ │ └── web/ │ │ │ │ │ ├── GlobalExceptionHandler.java │ │ │ │ │ └── GlobalResponseBodyHandler.java │ │ │ │ ├── dto/ │ │ │ │ │ ├── UserAddDTO.java │ │ │ │ │ ├── UserUpdateDTO.java │ │ │ │ │ ├── UserUpdateGenderDTO.java │ │ │ │ │ └── UserUpdateStatusDTO.java │ │ │ │ └── service/ │ │ │ │ └── UserService.java │ │ │ └── resources/ │ │ │ ├── application.yaml │ │ │ └── i18n/ │ │ │ ├── messages.properties │ │ │ ├── messages_en.properties │ │ │ └── messages_ja.properties │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab22/ │ │ └── validation/ │ │ └── service/ │ │ └── UserServiceTest.java │ ├── pom.xml │ └── 《芋道 Spring Boot 参数校验 Validation 入门》.md ├── lab-23/ │ ├── lab-springmvc-23-01/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab23/ │ │ │ └── springmvc/ │ │ │ ├── Application.java │ │ │ ├── controller/ │ │ │ │ ├── UserController.java │ │ │ │ └── UserController2.java │ │ │ ├── dto/ │ │ │ │ ├── UserAddDTO.java │ │ │ │ └── UserUpdateDTO.java │ │ │ ├── service/ │ │ │ │ └── UserService.java │ │ │ └── vo/ │ │ │ └── UserVO.java │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab23/ │ │ └── springmvc/ │ │ ├── controller/ │ │ │ ├── UserControllerTest.java │ │ │ └── UserControllerTest2.java │ │ └── package-info.java │ ├── lab-springmvc-23-02/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab23/ │ │ │ └── springmvc/ │ │ │ ├── Application.java │ │ │ ├── config/ │ │ │ │ └── SpringMVCConfiguration.java │ │ │ ├── constants/ │ │ │ │ └── ServiceExceptionEnum.java │ │ │ ├── controller/ │ │ │ │ ├── ProductController.java │ │ │ │ └── UserController.java │ │ │ ├── controller2/ │ │ │ │ └── TestController.java │ │ │ ├── core/ │ │ │ │ ├── exception/ │ │ │ │ │ └── ServiceException.java │ │ │ │ ├── interceptor/ │ │ │ │ │ ├── FirstInterceptor.java │ │ │ │ │ ├── SecondInterceptor.java │ │ │ │ │ └── ThirdInterceptor.java │ │ │ │ ├── package-info.java │ │ │ │ ├── servlet/ │ │ │ │ │ ├── TestFilter02.java │ │ │ │ │ ├── TestServlet02.java │ │ │ │ │ └── TestServletContextListener02.java │ │ │ │ ├── vo/ │ │ │ │ │ └── CommonResult.java │ │ │ │ └── web/ │ │ │ │ ├── GlobalExceptionHandler.java │ │ │ │ └── GlobalResponseBodyHandler.java │ │ │ └── vo/ │ │ │ ├── ProductVO.java │ │ │ └── UserVO.java │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab23/ │ │ └── springmvc/ │ │ └── package-info.java │ ├── pom.xml │ └── 《芋道 Spring Boot SpringMVC 入门》.md ├── lab-24/ │ ├── lab-24-apidoc-japidocs/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab24/ │ │ ├── Application.java │ │ ├── TestJApiDocs.java │ │ ├── controller/ │ │ │ └── UserController.java │ │ └── vo/ │ │ ├── UserCreateReqVO.java │ │ ├── UserListReqVO.java │ │ └── UserRespVO.java │ ├── lab-24-apidoc-showdoc/ │ │ ├── pom.xml │ │ ├── showdoc_api.sh │ │ ├── showdoc_db.sh │ │ ├── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab24/ │ │ │ └── apidoc/ │ │ │ ├── ShowDocApplication.java │ │ │ └── controller/ │ │ │ ├── UserController.java │ │ │ └── vo/ │ │ │ ├── UserLoginReqVO.java │ │ │ └── UserLoginRespVO.java │ │ └── swagger.json │ ├── lab-24-apidoc-swagger/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab24/ │ │ └── apidoc/ │ │ ├── Application.java │ │ ├── config/ │ │ │ └── SwaggerConfiguration.java │ │ ├── controller/ │ │ │ ├── TestController.java │ │ │ └── UserController.java │ │ ├── dto/ │ │ │ ├── UserAddDTO.java │ │ │ └── UserUpdateDTO.java │ │ └── vo/ │ │ └── UserVO.java │ ├── lab-24-apidoc-swagger-knife4j/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab24/ │ │ └── apidoc/ │ │ ├── Application.java │ │ ├── config/ │ │ │ └── SwaggerConfiguration.java │ │ ├── controller/ │ │ │ ├── TestController.java │ │ │ └── UserController.java │ │ ├── dto/ │ │ │ ├── UserAddDTO.java │ │ │ └── UserUpdateDTO.java │ │ └── vo/ │ │ └── UserVO.java │ ├── lab-24-apidoc-swagger-starter/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab24/ │ │ │ ├── Application.java │ │ │ ├── config/ │ │ │ │ └── SwaggerConfiguration.java │ │ │ ├── controller/ │ │ │ │ └── UserController.java │ │ │ ├── dto/ │ │ │ │ ├── UserAddDTO.java │ │ │ │ └── UserUpdateDTO.java │ │ │ └── vo/ │ │ │ └── UserVO.java │ │ └── resources/ │ │ └── application.yaml │ ├── pom.xml │ ├── 《芋道 Spring Boot API 接口文档 JApiDocs 入门》.md │ ├── 《芋道 Spring Boot API 接口文档 ShowDoc 入门》.md │ ├── 《芋道 Spring Boot API 接口文档 Swagger Starter 入门》.md │ └── 《芋道 Spring Boot API 接口文档 Swagger 入门》.md ├── lab-25/ │ ├── lab-websocket-25-01/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab25/ │ │ └── springwebsocket/ │ │ ├── Application.java │ │ ├── config/ │ │ │ └── WebSocketConfiguration.java │ │ ├── handler/ │ │ │ ├── AuthMessageHandler.java │ │ │ ├── MessageHandler.java │ │ │ ├── SendToAllHandler.java │ │ │ └── SendToOneHandler.java │ │ ├── message/ │ │ │ ├── AuthRequest.java │ │ │ ├── AuthResponse.java │ │ │ ├── Message.java │ │ │ ├── SendResponse.java │ │ │ ├── SendToAllRequest.java │ │ │ ├── SendToOneRequest.java │ │ │ ├── SendToUserRequest.java │ │ │ └── UserJoinNoticeRequest.java │ │ ├── util/ │ │ │ └── WebSocketUtil.java │ │ └── websocket/ │ │ └── WebsocketServerEndpoint.java │ ├── lab-websocket-25-02/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab25/ │ │ └── springwebsocket/ │ │ ├── Application.java │ │ ├── config/ │ │ │ └── WebSocketConfiguration.java │ │ ├── handler/ │ │ │ ├── AuthMessageHandler.java │ │ │ ├── MessageHandler.java │ │ │ ├── SendToAllHandler.java │ │ │ └── SendToOneHandler.java │ │ ├── message/ │ │ │ ├── AuthRequest.java │ │ │ ├── AuthResponse.java │ │ │ ├── Message.java │ │ │ ├── SendResponse.java │ │ │ ├── SendToAllRequest.java │ │ │ ├── SendToOneRequest.java │ │ │ ├── SendToUserRequest.java │ │ │ └── UserJoinNoticeRequest.java │ │ ├── util/ │ │ │ └── WebSocketUtil.java │ │ └── websocket/ │ │ ├── DemoWebSocketHandler.java │ │ └── DemoWebSocketShakeInterceptor.java │ ├── lab-websocket-25-03/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab25/ │ │ │ └── springwebsocket/ │ │ │ ├── Application.java │ │ │ ├── config/ │ │ │ │ └── WebSocketConfiguration.java │ │ │ ├── controller/ │ │ │ │ └── SendController.java │ │ │ └── message/ │ │ │ ├── AuthRequest.java │ │ │ ├── AuthResponse.java │ │ │ ├── Message.java │ │ │ ├── SendResponse.java │ │ │ ├── SendToAllRequest.java │ │ │ ├── SendToOneRequest.java │ │ │ ├── SendToUserRequest.java │ │ │ └── UserJoinNoticeRequest.java │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab25/ │ │ └── springwebsocket/ │ │ ├── Main.java │ │ └── client/ │ │ ├── DemoWebSocketStompClient.java │ │ └── handler/ │ │ ├── ConnectHandler.java │ │ └── SendToUserRequestHandler.java │ ├── pom.xml │ └── 《芋道 Spring Boot WebSocket 入门》.md ├── lab-26/ │ ├── lab-26-distributed-session-01/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab26/ │ │ │ └── distributedsession/ │ │ │ ├── Application.java │ │ │ ├── config/ │ │ │ │ └── SessionConfiguration.java │ │ │ └── controller/ │ │ │ └── SessionController.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-26-distributed-session-02/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab26/ │ │ │ └── distributedsession/ │ │ │ ├── Application.java │ │ │ ├── config/ │ │ │ │ └── SessionConfiguration.java │ │ │ └── controller/ │ │ │ └── SessionController.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-26-distributed-session-springsecurity/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab26/ │ │ │ └── distributedsession/ │ │ │ ├── Application.java │ │ │ ├── config/ │ │ │ │ └── SessionConfiguration.java │ │ │ └── controller/ │ │ │ └── SessionController.java │ │ └── resources/ │ │ └── application.yaml │ ├── pom.xml │ └── 《芋道 Spring Boot 分布式 Session 入门》.md ├── lab-27/ │ ├── lab-27-webflux-01/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab27/ │ │ │ └── springwebflux/ │ │ │ ├── Application.java │ │ │ ├── controller/ │ │ │ │ ├── UserController.java │ │ │ │ └── UserRouter.java │ │ │ ├── dto/ │ │ │ │ ├── UserAddDTO.java │ │ │ │ └── UserUpdateDTO.java │ │ │ ├── service/ │ │ │ │ └── UserService.java │ │ │ └── vo/ │ │ │ └── UserVO.java │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab27/ │ │ └── springwebflux/ │ │ ├── controller/ │ │ │ ├── UserControllerTest.java │ │ │ └── UserControllerTest2.java │ │ └── package-info.java │ ├── lab-27-webflux-02/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab27/ │ │ └── springwebflux/ │ │ ├── Application.java │ │ ├── config/ │ │ │ └── WebFluxConfiguration.java │ │ ├── constants/ │ │ │ └── ServiceExceptionEnum.java │ │ ├── controller/ │ │ │ ├── UserController.java │ │ │ └── UserRouter.java │ │ ├── controller2/ │ │ │ └── TestController.java │ │ ├── core/ │ │ │ ├── exception/ │ │ │ │ └── ServiceException.java │ │ │ ├── filter/ │ │ │ │ └── DemoWebFilter.java │ │ │ ├── package-info.java │ │ │ ├── vo/ │ │ │ │ └── CommonResult.java │ │ │ └── web/ │ │ │ ├── GlobalExceptionHandler.java │ │ │ └── GlobalResponseBodyHandler.java │ │ └── vo/ │ │ └── UserVO.java │ ├── lab-27-webflux-03/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab27/ │ │ └── springwebflux/ │ │ ├── Application.java │ │ ├── controller/ │ │ │ └── UserController.java │ │ ├── core/ │ │ │ ├── package-info.java │ │ │ └── servlet/ │ │ │ ├── TestFilter02.java │ │ │ ├── TestServlet02.java │ │ │ └── TestServletContextListener02.java │ │ └── vo/ │ │ └── UserVO.java │ ├── lab-27-webflux-elasticsearch/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab27/ │ │ │ └── springwebflux/ │ │ │ ├── Application.java │ │ │ ├── config/ │ │ │ │ └── ElasticsearchConfiguration.java │ │ │ ├── controller/ │ │ │ │ └── UserController.java │ │ │ ├── dao/ │ │ │ │ └── UserRepository.java │ │ │ ├── dataobject/ │ │ │ │ └── UserDO.java │ │ │ ├── dto/ │ │ │ │ ├── UserAddDTO.java │ │ │ │ └── UserUpdateDTO.java │ │ │ └── vo/ │ │ │ └── UserVO.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-27-webflux-mongodb/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab27/ │ │ │ └── springwebflux/ │ │ │ ├── Application.java │ │ │ ├── controller/ │ │ │ │ └── UserController.java │ │ │ ├── dao/ │ │ │ │ └── UserRepository.java │ │ │ ├── dataobject/ │ │ │ │ └── UserDO.java │ │ │ ├── dto/ │ │ │ │ ├── UserAddDTO.java │ │ │ │ └── UserUpdateDTO.java │ │ │ └── vo/ │ │ │ └── UserVO.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-27-webflux-r2dbc/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab27/ │ │ │ └── springwebflux/ │ │ │ ├── Application.java │ │ │ ├── config/ │ │ │ │ └── DatabaseConfiguration.java │ │ │ ├── controller/ │ │ │ │ └── UserController.java │ │ │ ├── dao/ │ │ │ │ └── UserRepository.java │ │ │ ├── dataobject/ │ │ │ │ └── UserDO.java │ │ │ ├── dto/ │ │ │ │ ├── UserAddDTO.java │ │ │ │ └── UserUpdateDTO.java │ │ │ └── vo/ │ │ │ └── UserVO.java │ │ └── resources/ │ │ ├── application.yaml │ │ └── sql/ │ │ └── users.sql │ ├── lab-27-webflux-redis/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab27/ │ │ │ └── springwebflux/ │ │ │ ├── Application.java │ │ │ ├── cacheobject/ │ │ │ │ └── UserCacheObject.java │ │ │ ├── config/ │ │ │ │ └── RedisConfiguration.java │ │ │ └── controller/ │ │ │ └── UserController.java │ │ └── resources/ │ │ └── application.yaml │ ├── pom.xml │ └── 《芋道 Spring Boot 响应式 WebFlux 入门》.md ├── lab-28/ │ ├── lab-28-task-demo/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab28/ │ │ │ └── task/ │ │ │ ├── Application.java │ │ │ ├── config/ │ │ │ │ └── ScheduleConfiguration.java │ │ │ └── job/ │ │ │ └── DemoJob.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-28-task-quartz-jdbc/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab28/ │ │ │ │ └── task/ │ │ │ │ ├── Application.java │ │ │ │ ├── Application02.java │ │ │ │ ├── config/ │ │ │ │ │ ├── DataSourceConfiguration.java │ │ │ │ │ └── ScheduleConfiguration.java │ │ │ │ ├── job/ │ │ │ │ │ ├── DemoJob01.java │ │ │ │ │ └── DemoJob02.java │ │ │ │ └── service/ │ │ │ │ └── DemoService.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab28/ │ │ └── task/ │ │ └── QuartzSchedulerTest.java │ ├── lab-28-task-quartz-memory/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab28/ │ │ │ └── task/ │ │ │ ├── Application.java │ │ │ ├── config/ │ │ │ │ └── ScheduleConfiguration.java │ │ │ ├── job/ │ │ │ │ ├── DemoJob01.java │ │ │ │ └── DemoJob02.java │ │ │ └── service/ │ │ │ └── DemoService.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-28-task-xxl-job/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab28/ │ │ │ └── task/ │ │ │ ├── Application.java │ │ │ ├── config/ │ │ │ │ └── XxlJobConfiguration.java │ │ │ └── job/ │ │ │ └── DemoJob.java │ │ └── resources/ │ │ └── application.yaml │ ├── pom.xml │ └── 《芋道 Spring Boot 定时任务入门》.md ├── lab-29/ │ ├── lab-29-async-demo/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab29/ │ │ │ │ └── asynctask/ │ │ │ │ ├── Application.java │ │ │ │ ├── Demo.java │ │ │ │ ├── config/ │ │ │ │ │ └── AsyncConfig.java │ │ │ │ ├── core/ │ │ │ │ │ ├── async/ │ │ │ │ │ │ └── GlobalAsyncExceptionHandler.java │ │ │ │ │ └── package-info.java │ │ │ │ └── service/ │ │ │ │ └── DemoService.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab29/ │ │ └── asynctask/ │ │ ├── package-info.java │ │ └── service/ │ │ └── DemoServiceTest.java │ ├── lab-29-async-two/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab29/ │ │ │ │ └── asynctask/ │ │ │ │ ├── Application.java │ │ │ │ ├── config/ │ │ │ │ │ └── AsyncConfig.java │ │ │ │ └── service/ │ │ │ │ └── DemoService.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab29/ │ │ └── asynctask/ │ │ ├── package-info.java │ │ └── service/ │ │ └── DemoServiceTest.java │ ├── pom.xml │ └── 《芋道 Spring Boot 异步任务入门》.md ├── lab-30/ │ ├── lab-30-dubbo-annotations-demo/ │ │ ├── pom.xml │ │ ├── user-rpc-service-api-02/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab30/ │ │ │ └── rpc/ │ │ │ ├── api/ │ │ │ │ └── UserRpcService.java │ │ │ └── dto/ │ │ │ └── UserDTO.java │ │ ├── user-rpc-service-consumer-02/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab30/ │ │ │ │ └── rpc/ │ │ │ │ └── ConsumerApplication.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── user-rpc-service-provider-02/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab30/ │ │ │ └── rpc/ │ │ │ ├── ProviderApplication.java │ │ │ └── service/ │ │ │ └── UserRpcServiceImpl.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-30-dubbo-annotations-nacos/ │ │ ├── pom.xml │ │ ├── user-rpc-service-api-03/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab30/ │ │ │ └── rpc/ │ │ │ ├── api/ │ │ │ │ └── UserRpcService.java │ │ │ └── dto/ │ │ │ └── UserDTO.java │ │ ├── user-rpc-service-consumer-03/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab30/ │ │ │ │ └── rpc/ │ │ │ │ └── ConsumerApplication.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── user-rpc-service-provider-03/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab30/ │ │ │ └── rpc/ │ │ │ ├── ProviderApplication.java │ │ │ └── service/ │ │ │ └── UserRpcServiceImpl.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-30-dubbo-annotations-sentinel/ │ │ ├── pom.xml │ │ ├── user-rpc-service-api-04/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab30/ │ │ │ └── rpc/ │ │ │ ├── api/ │ │ │ │ └── UserRpcService.java │ │ │ └── dto/ │ │ │ └── UserDTO.java │ │ ├── user-rpc-service-consumer-04/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab30/ │ │ │ │ └── rpc/ │ │ │ │ ├── ConsumerApplication.java │ │ │ │ └── controller/ │ │ │ │ └── UserController.java │ │ │ └── resources/ │ │ │ ├── application.yaml │ │ │ └── sentinel.properties │ │ └── user-rpc-service-provider-04/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab30/ │ │ │ └── rpc/ │ │ │ ├── ProviderApplication.java │ │ │ └── service/ │ │ │ └── UserRpcServiceImpl.java │ │ └── resources/ │ │ ├── application.yaml │ │ └── sentinel.properties │ ├── lab-30-dubbo-xml-demo/ │ │ ├── pom.xml │ │ ├── user-rpc-service-api/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab30/ │ │ │ └── rpc/ │ │ │ ├── api/ │ │ │ │ └── UserRpcService.java │ │ │ ├── core/ │ │ │ │ ├── ServiceException.java │ │ │ │ └── ServiceExceptionEnum.java │ │ │ └── dto/ │ │ │ ├── UserAddDTO.java │ │ │ └── UserDTO.java │ │ ├── user-rpc-service-consumer/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab30/ │ │ │ │ └── rpc/ │ │ │ │ └── ConsumerApplication.java │ │ │ └── resources/ │ │ │ ├── application.yaml │ │ │ └── dubbo.xml │ │ └── user-rpc-service-provider/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab30/ │ │ │ └── rpc/ │ │ │ ├── ProviderApplication.java │ │ │ ├── filter/ │ │ │ │ └── DubboExceptionFilter.java │ │ │ └── service/ │ │ │ └── UserRpcServiceImpl.java │ │ └── resources/ │ │ ├── META-INF/ │ │ │ └── dubbo/ │ │ │ └── org.apache.dubbo.rpc.Filter │ │ ├── application.yaml │ │ └── dubbo.xml │ ├── pom.xml │ ├── 《芋道 Dubbo Admin 快速入门》.md │ ├── 《芋道 Dubbo Swagger 快速入门》.md │ └── 《芋道 Spring Boot Dubbo 入门》.md ├── lab-31/ │ ├── lab-31-rocketmq-demo/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab31/ │ │ │ │ └── rocketmqdemo/ │ │ │ │ ├── Application.java │ │ │ │ ├── consumer/ │ │ │ │ │ ├── Demo01AConsumer.java │ │ │ │ │ ├── Demo01Consumer.java │ │ │ │ │ ├── Demo02Consumer.java │ │ │ │ │ ├── Demo03Consumer.java │ │ │ │ │ ├── Demo04Consumer.java │ │ │ │ │ ├── Demo05Consumer.java │ │ │ │ │ ├── Demo06Consumer.java │ │ │ │ │ └── Demo07Consumer.java │ │ │ │ ├── core/ │ │ │ │ │ └── ExtRocketMQTemplate.java │ │ │ │ ├── message/ │ │ │ │ │ ├── Demo01Message.java │ │ │ │ │ ├── Demo02Message.java │ │ │ │ │ ├── Demo03Message.java │ │ │ │ │ ├── Demo04Message.java │ │ │ │ │ ├── Demo05Message.java │ │ │ │ │ ├── Demo06Message.java │ │ │ │ │ └── Demo07Message.java │ │ │ │ └── producer/ │ │ │ │ ├── Demo01Producer.java │ │ │ │ ├── Demo02Producer.java │ │ │ │ ├── Demo03Producer.java │ │ │ │ ├── Demo04Producer.java │ │ │ │ ├── Demo05Producer.java │ │ │ │ ├── Demo06Producer.java │ │ │ │ └── Demo07Producer.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab31/ │ │ └── rocketmqdemo/ │ │ ├── package-info.java │ │ └── producer/ │ │ ├── Demo01ProducerTest.java │ │ ├── Demo02ProducerTest.java │ │ ├── Demo03ProducerTest.java │ │ ├── Demo04ProducerTest.java │ │ ├── Demo05ProducerTest.java │ │ ├── Demo06ProducerTest.java │ │ └── Demo07ProducerTest.java │ ├── lab-31-rocketmq-ons/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab31/ │ │ │ │ └── rocketmqdemo/ │ │ │ │ ├── Application.java │ │ │ │ ├── consumer/ │ │ │ │ │ └── Demo01Consumer.java │ │ │ │ ├── message/ │ │ │ │ │ └── Demo01Message.java │ │ │ │ └── producer/ │ │ │ │ └── Demo01Producer.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab31/ │ │ └── rocketmqdemo/ │ │ ├── package-info.java │ │ └── producer/ │ │ └── Demo01ProducerTest.java │ ├── pom.xml │ └── 《芋道 Spring Boot 消息队列 RocketMQ 入门》.md ├── lab-32/ │ ├── lab-32-activemq-demo/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab32/ │ │ │ │ └── activemqdemo/ │ │ │ │ ├── Application.java │ │ │ │ ├── consumer/ │ │ │ │ │ └── Demo01Consumer.java │ │ │ │ ├── message/ │ │ │ │ │ └── Demo01Message.java │ │ │ │ └── producer/ │ │ │ │ └── Demo01Producer.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab32/ │ │ └── activemqdemo/ │ │ ├── package-info.java │ │ └── producer/ │ │ └── Demo01ProducerTest.java │ ├── lab-32-activemq-demo-concurrency/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab32/ │ │ │ │ └── activemqdemo/ │ │ │ │ ├── Application.java │ │ │ │ ├── consumer/ │ │ │ │ │ └── Demo03Consumer.java │ │ │ │ ├── message/ │ │ │ │ │ └── Demo03Message.java │ │ │ │ └── producer/ │ │ │ │ └── Demo03Producer.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab32/ │ │ └── activemqdemo/ │ │ ├── package-info.java │ │ └── producer/ │ │ └── Demo03ProducerTest.java │ ├── lab-32-activemq-demo-consume-retry/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab32/ │ │ │ │ └── activemqdemo/ │ │ │ │ ├── Application.java │ │ │ │ ├── config/ │ │ │ │ │ └── ActiveMQConfig.java │ │ │ │ ├── consumer/ │ │ │ │ │ └── Demo05Consumer.java │ │ │ │ ├── message/ │ │ │ │ │ └── Demo05Message.java │ │ │ │ └── producer/ │ │ │ │ └── Demo05Producer.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab32/ │ │ └── activemqdemo/ │ │ ├── package-info.java │ │ └── producer/ │ │ └── Demo05ProducerTest.java │ ├── lab-32-activemq-demo-delay/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab32/ │ │ │ │ └── activemqdemo/ │ │ │ │ ├── Application.java │ │ │ │ ├── consumer/ │ │ │ │ │ └── Demo02Consumer.java │ │ │ │ ├── message/ │ │ │ │ │ └── Demo02Message.java │ │ │ │ └── producer/ │ │ │ │ └── Demo02Producer.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab32/ │ │ └── activemqdemo/ │ │ ├── package-info.java │ │ └── producer/ │ │ └── Demo02ProducerTest.java │ ├── lab-32-activemq-demo-message-model/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab32/ │ │ │ │ └── activemqdemo/ │ │ │ │ ├── Application.java │ │ │ │ ├── config/ │ │ │ │ │ └── ActiveMQConfig.java │ │ │ │ ├── consumer/ │ │ │ │ │ ├── BroadcastConsumer.java │ │ │ │ │ └── ClusteringConsumer.java │ │ │ │ ├── message/ │ │ │ │ │ ├── BroadcastMessage.java │ │ │ │ │ └── ClusteringMessage.java │ │ │ │ └── producer/ │ │ │ │ ├── BroadcastProducer.java │ │ │ │ └── ClusteringProducer.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab32/ │ │ └── activemqdemo/ │ │ ├── package-info.java │ │ └── producer/ │ │ ├── BroadcastProducerTest.java │ │ └── ClusteringProducerTest.java │ ├── lab-32-activemq-demo-orderly/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab32/ │ │ │ │ └── activemqdemo/ │ │ │ │ ├── Application.java │ │ │ │ ├── consumer/ │ │ │ │ │ └── Demo04Consumer.java │ │ │ │ ├── message/ │ │ │ │ │ └── Demo04Message.java │ │ │ │ └── producer/ │ │ │ │ └── Demo04Producer.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab32/ │ │ └── activemqdemo/ │ │ ├── package-info.java │ │ └── producer/ │ │ └── Demo04ProducerTest.java │ ├── lab-32-activemq-native/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab32/ │ │ └── activemqdemo/ │ │ ├── ActiveMQConsumer.java │ │ └── ActiveMQProducer.java │ ├── pom.xml │ └── 《芋道 Spring Boot 消息队列 ActiveMQ 入门》.md ├── lab-33/ │ ├── lab-33-shiro-demo/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab01/ │ │ │ └── shirodemo/ │ │ │ ├── Application.java │ │ │ ├── config/ │ │ │ │ └── ShiroConfig.java │ │ │ └── controller/ │ │ │ ├── DemoController.java │ │ │ ├── SecurityController.java │ │ │ └── TestController.java │ │ └── resources/ │ │ └── static/ │ │ └── login.html │ ├── pom.xml │ └── 《芋道 Spring Boot 安全框架 Shiro 入门》.md ├── lab-34/ │ ├── lab-34-actuator-demo/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab34/ │ │ │ └── actuatordemo/ │ │ │ └── Application.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-34-actuator-demo-auditevents/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab34/ │ │ │ └── actuatordemo/ │ │ │ ├── Application.java │ │ │ └── config/ │ │ │ └── ActuateConfig.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-34-actuator-demo-custom-endpoint/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab34/ │ │ │ └── actuatordemo/ │ │ │ ├── Application.java │ │ │ └── endpoint/ │ │ │ └── DemoEndPoint.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-34-actuator-demo-health/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab34/ │ │ │ └── actuatordemo/ │ │ │ ├── Application.java │ │ │ └── actuate/ │ │ │ └── DemoHealthIndicator.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-34-actuator-demo-httptrace/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab34/ │ │ │ └── actuatordemo/ │ │ │ ├── Application.java │ │ │ └── config/ │ │ │ └── ActuateConfig.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-34-actuator-demo-info/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab34/ │ │ │ └── actuatordemo/ │ │ │ ├── Application.java │ │ │ ├── config/ │ │ │ │ └── ActuateConfig.java │ │ │ └── demo/ │ │ │ └── DemoInfoContributor.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-34-actuator-demo-metrics/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab34/ │ │ │ └── actuatordemo/ │ │ │ ├── Application.java │ │ │ └── controller/ │ │ │ ├── DemoController.java │ │ │ └── ExampleController.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-34-actuator-demo-security/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab34/ │ │ │ └── actuatordemo/ │ │ │ ├── Application.java │ │ │ └── config/ │ │ │ └── SecurityConfig.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-34-actuator-test/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab34/ │ │ │ └── actuatordemo/ │ │ │ ├── Application.java │ │ │ └── controller/ │ │ │ └── TestController.java │ │ └── resources/ │ │ └── application.yaml │ ├── pom.xml │ └── 《芋道 Spring Boot 监控端点 Actuator 入门》.md ├── lab-35/ │ ├── lab-35-admin-01-adminserver/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab35/ │ │ └── adminserver/ │ │ └── AdminServerApplication.java │ ├── lab-35-admin-01-demo-application/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab35/ │ │ │ └── demo/ │ │ │ └── DemoApplication.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-35-admin-02-adminserver/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab35/ │ │ │ └── adminserver/ │ │ │ └── AdminServerApplication.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-35-admin-02-demo-application/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab35/ │ │ │ └── demo/ │ │ │ ├── Demo01Application.java │ │ │ └── Demo02Application.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-35-admin-02-eurekaserver/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab35/ │ │ │ └── eurekaserver/ │ │ │ └── EurekaServerApplication.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-35-admin-03-adminserver/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab35/ │ │ └── adminserver/ │ │ ├── AdminServerApplication.java │ │ └── config/ │ │ └── SecurityConfig.java │ ├── lab-35-admin-03-demo-application/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab35/ │ │ │ └── demo/ │ │ │ └── DemoApplication.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-35-admin-04-adminserver/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab35/ │ │ │ └── adminserver/ │ │ │ └── AdminServerApplication.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-35-admin-05-adminserver/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab35/ │ │ └── adminserver/ │ │ ├── AdminServerApplication.java │ │ └── notify/ │ │ └── LoggerNotifier.java │ ├── pom.xml │ └── 《芋道 Spring Boot 监控工具 Admin 入门》.md ├── lab-36/ │ ├── lab-36-prometheus-demo/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab36/ │ │ │ └── prometheusdemo/ │ │ │ └── Application.java │ │ └── resources/ │ │ └── application.yaml │ ├── pom.xml │ └── 《芋道 Spring Boot 监控平台 Prometheus + Grafana 入门》.md ├── lab-37/ │ ├── lab-37-logging-actuator/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab37/ │ │ │ └── loggingdemo/ │ │ │ ├── Application.java │ │ │ └── controller/ │ │ │ └── DemoController.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-37-logging-aop/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab37/ │ │ │ └── loggingdemo/ │ │ │ ├── Application.java │ │ │ ├── aspect/ │ │ │ │ └── HttpAccessAspect.java │ │ │ └── controller/ │ │ │ └── DemoController.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-37-logging-debug/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab37/ │ │ │ └── loggingdemo/ │ │ │ ├── Application.java │ │ │ └── controller/ │ │ │ └── DemoController.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-37-logging-demo/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab37/ │ │ │ └── loggingdemo/ │ │ │ ├── Application.java │ │ │ └── controller/ │ │ │ └── DemoController.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-37-logging-log4j2/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab37/ │ │ │ └── loggingdemo/ │ │ │ └── Application.java │ │ └── resources/ │ │ ├── application.yaml │ │ └── log4j2-spring.xml │ ├── lab-37-logging-logback/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab37/ │ │ │ └── loggingdemo/ │ │ │ └── Application.java │ │ └── resources/ │ │ ├── application.yaml │ │ └── logback-spring.xml │ ├── lab-37-logging-multi-env/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab37/ │ │ │ └── loggingdemo/ │ │ │ └── Application.java │ │ └── resources/ │ │ ├── application-dev.yaml │ │ └── application-prod.yaml │ ├── pom.xml │ └── 《芋道 Spring Boot 日志集成 Logging 入门》.md ├── lab-38/ │ ├── lab-38-elk-demo/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab38/ │ │ │ └── elkdemo/ │ │ │ └── Application.java │ │ └── resources/ │ │ └── application.yaml │ ├── pom.xml │ └── 《芋道 Spring Boot 日志平台 ELK + Filebeat 入门》.md ├── lab-39/ │ ├── lab-39-activemq/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab39/ │ │ │ └── skywalkingdemo/ │ │ │ ├── ActiveMQApplication.java │ │ │ ├── consumer/ │ │ │ │ └── DemoConsumer.java │ │ │ ├── controller/ │ │ │ │ └── DemoController.java │ │ │ ├── message/ │ │ │ │ └── DemoMessage.java │ │ │ └── producer/ │ │ │ └── DemoProducer.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-39-async/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab39/ │ │ │ └── skywalkingdemo/ │ │ │ ├── AsyncApplication.java │ │ │ ├── controller/ │ │ │ │ └── DemoController.java │ │ │ └── service/ │ │ │ └── DemoService.java │ │ └── resources/ │ │ └── application.yml │ ├── lab-39-demo/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab39/ │ │ │ └── skywalkingdemo/ │ │ │ ├── Application.java │ │ │ └── controller/ │ │ │ └── DemoController.java │ │ └── resources/ │ │ └── application.yml │ ├── lab-39-elasticsearch/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab39/ │ │ │ └── skywalkingdemo/ │ │ │ ├── ElasticsearchApplication.java │ │ │ ├── controller/ │ │ │ │ └── DemoController.java │ │ │ ├── dataobject/ │ │ │ │ └── ESUserDO.java │ │ │ └── repository/ │ │ │ └── ESUserRepository.java │ │ └── resources/ │ │ └── application.yml │ ├── lab-39-elasticsearch-jest/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab39/ │ │ │ └── skywalkingdemo/ │ │ │ ├── ElasticsearchJestApplication.java │ │ │ ├── controller/ │ │ │ │ └── DemoController.java │ │ │ ├── dataobject/ │ │ │ │ └── ESUserDO.java │ │ │ └── repository/ │ │ │ └── ESUserRepository.java │ │ └── resources/ │ │ └── application.yml │ ├── lab-39-kafka/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab39/ │ │ │ └── skywalkingdemo/ │ │ │ ├── KafkaApplication.java │ │ │ ├── consumer/ │ │ │ │ └── DemoConsumer.java │ │ │ ├── controller/ │ │ │ │ └── DemoController.java │ │ │ ├── message/ │ │ │ │ └── DemoMessage.java │ │ │ └── producer/ │ │ │ └── DemoProducer.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-39-logback/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab39/ │ │ │ └── skywalkingdemo/ │ │ │ ├── LogbackApplication.java │ │ │ └── controller/ │ │ │ └── DemoController.java │ │ └── resources/ │ │ ├── application.yml │ │ └── logback-spring.xml │ ├── lab-39-mongodb/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab39/ │ │ │ └── skywalkingdemo/ │ │ │ ├── MongoDBApplication.java │ │ │ ├── controller/ │ │ │ │ └── DemoController.java │ │ │ └── dataobject/ │ │ │ └── UserDO.java │ │ └── resources/ │ │ └── application.yml │ ├── lab-39-mysql/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab39/ │ │ │ └── skywalkingdemo/ │ │ │ ├── MySQLApplication.java │ │ │ └── controller/ │ │ │ └── DemoController.java │ │ └── resources/ │ │ └── application.yml │ ├── lab-39-opentracing/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab39/ │ │ │ └── skywalkingdemo/ │ │ │ ├── OpentracingApplication.java │ │ │ └── controller/ │ │ │ └── DemoController.java │ │ └── resources/ │ │ └── application.yml │ ├── lab-39-rabbitmq-demo/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab39/ │ │ │ └── skywalkingdemo/ │ │ │ ├── RabbitMQApplication.java │ │ │ ├── config/ │ │ │ │ └── RabbitConfig.java │ │ │ ├── consumer/ │ │ │ │ └── DemoConsumer.java │ │ │ ├── controller/ │ │ │ │ └── DemoController.java │ │ │ ├── message/ │ │ │ │ └── DemoMessage.java │ │ │ └── producer/ │ │ │ └── DemoProducer.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-39-redis/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab39/ │ │ │ └── skywalkingdemo/ │ │ │ ├── RedisApplication.java │ │ │ └── controller/ │ │ │ └── DemoController.java │ │ └── resources/ │ │ └── application.yml │ ├── lab-39-rocketmq/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab39/ │ │ │ └── skywalkingdemo/ │ │ │ └── rocketmqdemo/ │ │ │ ├── RocketMQApplication.java │ │ │ ├── consumer/ │ │ │ │ └── DemoConsumer.java │ │ │ ├── controller/ │ │ │ │ └── DemoController.java │ │ │ ├── message/ │ │ │ │ └── DemoMessage.java │ │ │ └── producer/ │ │ │ └── DemoProducer.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-39-skywalking-dubbo/ │ │ ├── lab-39-skywalking-dubbo-api/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab39/ │ │ │ └── skywalkingdemo/ │ │ │ ├── api/ │ │ │ │ └── UserService.java │ │ │ └── package-info.java │ │ ├── lab-39-skywalking-dubbo-consumer/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab39/ │ │ │ │ └── skywalkingdemo/ │ │ │ │ └── consumerdemo/ │ │ │ │ ├── ConsumerApplication.java │ │ │ │ └── controller/ │ │ │ │ └── UserController.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ ├── lab-39-skywalking-dubbo-provider/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab39/ │ │ │ │ └── skywalkingdemo/ │ │ │ │ └── providerdemo/ │ │ │ │ ├── ProviderApplication.java │ │ │ │ └── service/ │ │ │ │ └── UserServiceImpl.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── pom.xml │ ├── lab-39-springmvc/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab39/ │ │ │ └── skywalkingdemo/ │ │ │ ├── Application.java │ │ │ └── controller/ │ │ │ └── DemoController.java │ │ └── resources/ │ │ └── application.yml │ ├── lab-39-trace-annotations/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab39/ │ │ │ └── skywalkingdemo/ │ │ │ ├── TraceAnnotationsApplication.java │ │ │ └── controller/ │ │ │ └── DemoController.java │ │ └── resources/ │ │ └── application.yml │ ├── pom.xml │ └── 《芋道 Spring Boot 链路追踪 SkyWalking 入门》.md ├── lab-40/ │ ├── lab-40-activemq/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab40/ │ │ │ └── zipkindemo/ │ │ │ ├── ActiveMQApplication.java │ │ │ ├── config/ │ │ │ │ ├── SpringMvcConfiguration.java │ │ │ │ └── ZipkinConfiguration.java │ │ │ ├── consumer/ │ │ │ │ └── DemoConsumer.java │ │ │ ├── controller/ │ │ │ │ └── DemoController.java │ │ │ ├── message/ │ │ │ │ └── DemoMessage.java │ │ │ └── producer/ │ │ │ └── DemoProducer.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-40-demo/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab40/ │ │ │ └── zipkindemo/ │ │ │ ├── Application.java │ │ │ ├── Application2.java │ │ │ ├── config/ │ │ │ │ ├── SpringMvcConfiguration.java │ │ │ │ └── ZipkinConfiguration.java │ │ │ └── controller/ │ │ │ └── DemoController.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-40-elasticsearch/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab40/ │ │ │ └── zipkindemo/ │ │ │ ├── ElasticsearchApplication.java │ │ │ ├── config/ │ │ │ │ ├── SpringMvcConfiguration.java │ │ │ │ └── ZipkinConfiguration.java │ │ │ ├── controller/ │ │ │ │ └── DemoController.java │ │ │ ├── dataobject/ │ │ │ │ └── ESUserDO.java │ │ │ ├── repository/ │ │ │ │ └── ESUserRepository.java │ │ │ └── spring/ │ │ │ ├── ClusterNodes.java │ │ │ └── TracingTransportClientFactoryBean.java │ │ └── resources/ │ │ └── application.yml │ ├── lab-40-kafka/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab40/ │ │ │ └── zipkindemo/ │ │ │ ├── KafkaApplication.java │ │ │ ├── config/ │ │ │ │ ├── SpringMvcConfiguration.java │ │ │ │ └── ZipkinConfiguration.java │ │ │ ├── consumer/ │ │ │ │ └── DemoConsumer.java │ │ │ ├── controller/ │ │ │ │ └── DemoController.java │ │ │ ├── message/ │ │ │ │ └── DemoMessage.java │ │ │ └── producer/ │ │ │ └── DemoProducer.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-40-logback/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab40/ │ │ │ └── zipkindemo/ │ │ │ ├── LogbackApplication.java │ │ │ ├── config/ │ │ │ │ ├── SpringMvcConfiguration.java │ │ │ │ └── ZipkinConfiguration.java │ │ │ └── controller/ │ │ │ └── DemoController.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-40-mongodb/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab40/ │ │ │ └── zipkin/ │ │ │ ├── MongoDBApplication.java │ │ │ ├── config/ │ │ │ │ ├── SpringMvcConfiguration.java │ │ │ │ └── ZipkinConfiguration.java │ │ │ ├── controller/ │ │ │ │ └── DemoController.java │ │ │ └── dataobject/ │ │ │ └── UserDO.java │ │ └── resources/ │ │ └── application.yml │ ├── lab-40-mysql/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab40/ │ │ │ └── zipkindemo/ │ │ │ ├── MySQLApplication.java │ │ │ ├── config/ │ │ │ │ ├── SpringMvcConfiguration.java │ │ │ │ └── ZipkinConfiguration.java │ │ │ └── controller/ │ │ │ └── DemoController.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-40-opentracing/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab40/ │ │ │ └── zipkindemo/ │ │ │ ├── OpentracingApplication.java │ │ │ ├── config/ │ │ │ │ ├── SpringMvcConfiguration.java │ │ │ │ └── ZipkinConfiguration.java │ │ │ └── controller/ │ │ │ └── DemoController.java │ │ └── resources/ │ │ └── application.yml │ ├── lab-40-rabbitmq/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab40/ │ │ │ └── zipkindemo/ │ │ │ ├── RabbitMQApplication.java │ │ │ ├── config/ │ │ │ │ ├── RabbitConfig.java │ │ │ │ ├── SpringMvcConfiguration.java │ │ │ │ └── ZipkinConfiguration.java │ │ │ ├── consumer/ │ │ │ │ └── DemoConsumer.java │ │ │ ├── controller/ │ │ │ │ └── DemoController.java │ │ │ ├── message/ │ │ │ │ └── DemoMessage.java │ │ │ └── producer/ │ │ │ └── DemoProducer.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-40-redis/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab40/ │ │ │ └── zipkindemo/ │ │ │ ├── RedisApplication.java │ │ │ ├── config/ │ │ │ │ ├── SpringMvcConfiguration.java │ │ │ │ └── ZipkinConfiguration.java │ │ │ └── controller/ │ │ │ └── DemoController.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-40-springmvc/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab40/ │ │ │ └── zipkindemo/ │ │ │ ├── SpringMVCApplication.java │ │ │ ├── config/ │ │ │ │ ├── SpringMvcConfiguration.java │ │ │ │ └── ZipkinConfiguration.java │ │ │ └── controller/ │ │ │ └── DemoController.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-40-zipkin-dubbo/ │ │ ├── lab-40-zipkin-dubbo-api/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab40/ │ │ │ └── zipkindemo/ │ │ │ ├── api/ │ │ │ │ └── UserService.java │ │ │ └── package-info.java │ │ ├── lab-40-zipkin-dubbo-consumer/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab40/ │ │ │ │ └── zpkindemo/ │ │ │ │ └── consumerdemo/ │ │ │ │ ├── ConsumerApplication.java │ │ │ │ ├── config/ │ │ │ │ │ ├── SpringMvcConfiguration.java │ │ │ │ │ └── ZipkinConfiguration.java │ │ │ │ └── controller/ │ │ │ │ └── UserController.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ ├── lab-40-zipkin-dubbo-provider/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab40/ │ │ │ │ └── zipkindemo/ │ │ │ │ └── providerdemo/ │ │ │ │ ├── ProviderApplication.java │ │ │ │ ├── config/ │ │ │ │ │ └── ZipkinConfiguration.java │ │ │ │ └── service/ │ │ │ │ └── UserServiceImpl.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── pom.xml │ ├── pom.xml │ └── 《芋道 Spring Boot 链路追踪 Zipkin 入门》.md ├── lab-41/ │ ├── deploy.sh │ ├── lab-41-demo01/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab40/ │ │ │ └── jenkinsdemo/ │ │ │ ├── Application.java │ │ │ └── controller/ │ │ │ └── DemoController.java │ │ └── resources/ │ │ ├── application-dev.yaml │ │ ├── application-local.yaml │ │ ├── application-pre.yaml │ │ ├── application-prod.yaml │ │ └── application-uat.yaml │ ├── lab-41-demo02/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab40/ │ │ │ └── jenkinsdemo/ │ │ │ ├── Demo02Application.java │ │ │ ├── actuate/ │ │ │ │ └── ServerHealthIndicator.java │ │ │ └── controller/ │ │ │ └── DemoController.java │ │ └── resources/ │ │ ├── application-dev.yaml │ │ ├── application-local.yaml │ │ ├── application-pre.yaml │ │ ├── application-prod.yaml │ │ └── application-uat.yaml │ ├── pom.xml │ └── 《芋道 Spring Boot 持续交付 Jenkins 入门》.md ├── lab-42/ │ ├── lab-42-demo01/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab23/ │ │ │ │ └── testdemo/ │ │ │ │ ├── Application.java │ │ │ │ ├── controller/ │ │ │ │ │ └── UserController.java │ │ │ │ ├── dao/ │ │ │ │ │ └── UserDao.java │ │ │ │ ├── dataobject/ │ │ │ │ │ └── UserDO.java │ │ │ │ └── service/ │ │ │ │ └── UserService.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab23/ │ │ │ └── testdemo/ │ │ │ ├── controller/ │ │ │ │ └── UserControllerTest.java │ │ │ ├── dao/ │ │ │ │ └── UserDaoTest.java │ │ │ ├── package-info.java │ │ │ └── service/ │ │ │ └── UserServiceTest.java │ │ └── resources/ │ │ ├── application.yaml │ │ └── sql/ │ │ ├── clean.sql │ │ └── create_tables.sql │ ├── lab-42-demo02/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab23/ │ │ │ │ └── testdemo/ │ │ │ │ ├── Application.java │ │ │ │ ├── controller/ │ │ │ │ │ └── UserController.java │ │ │ │ ├── dao/ │ │ │ │ │ └── UserDao.java │ │ │ │ ├── dataobject/ │ │ │ │ │ └── UserDO.java │ │ │ │ └── service/ │ │ │ │ └── UserService.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab23/ │ │ │ └── testdemo/ │ │ │ ├── controller/ │ │ │ │ └── UserControllerTest.java │ │ │ ├── dao/ │ │ │ │ └── UserDaoTest.java │ │ │ ├── package-info.java │ │ │ └── service/ │ │ │ ├── UserServiceTest.java │ │ │ └── UserServiceTest2.java │ │ └── resources/ │ │ ├── application.yaml │ │ └── sql/ │ │ ├── clean.sql │ │ └── create_tables.sql │ ├── pom.xml │ └── 《芋道 Spring Boot 单元测试 Test 入门》.md ├── lab-43/ │ ├── lab-43-demo/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab43/ │ │ │ └── propertydemo/ │ │ │ ├── Application.java │ │ │ └── OrderProperties.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-43-demo-configname/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab43/ │ │ │ └── propertydemo/ │ │ │ └── Application.java │ │ └── resources/ │ │ ├── application.yaml │ │ └── rpc.yaml │ ├── lab-43-demo-jasypt/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab43/ │ │ │ │ └── propertydemo/ │ │ │ │ └── Application.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab43/ │ │ └── propertydemo/ │ │ └── JasyptTest.java │ ├── lab-43-demo-profiles/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab43/ │ │ │ └── propertydemo/ │ │ │ └── ProfilesApplication.java │ │ └── resources/ │ │ ├── application-dev.yaml │ │ ├── application-local.yaml │ │ ├── application-pre.yaml │ │ ├── application-prod.yaml │ │ ├── application-uat.yaml │ │ └── application.yaml │ ├── pom.xml │ └── 《芋道 Spring Boot 配置文件入门》.md ├── lab-44/ │ ├── lab-44-nacos-config-demo/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab44/ │ │ │ └── nacosdemo/ │ │ │ ├── Application.java │ │ │ └── OrderProperties.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-44-nacos-config-demo-actuator/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab44/ │ │ │ └── nacosdemo/ │ │ │ ├── Application.java │ │ │ ├── controller/ │ │ │ │ └── DemoController.java │ │ │ ├── listener/ │ │ │ │ └── LoggingSystemConfigListener.java │ │ │ └── properties/ │ │ │ └── TestProperties.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-44-nacos-config-demo-auto-refresh/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab44/ │ │ │ └── nacosdemo/ │ │ │ ├── Application.java │ │ │ ├── controller/ │ │ │ │ └── DemoController.java │ │ │ ├── listener/ │ │ │ │ └── LoggingSystemConfigListener.java │ │ │ └── properties/ │ │ │ └── TestProperties.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-44-nacos-config-demo-jasypt/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab44/ │ │ │ │ └── nacosdemo/ │ │ │ │ ├── Application.java │ │ │ │ └── controller/ │ │ │ │ └── DemoController.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab44/ │ │ └── nacosdemo/ │ │ └── JasyptTest.java │ ├── lab-44-nacos-config-demo-multi/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab44/ │ │ │ └── nacosdemo/ │ │ │ ├── Application.java │ │ │ └── controller/ │ │ │ └── DemoController.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-44-nacos-config-demo-profiles/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab44/ │ │ │ └── nacosdemo/ │ │ │ └── ProfilesApplication.java │ │ └── resources/ │ │ ├── application-dev.yaml │ │ ├── application-prod.yaml │ │ └── application.yaml │ ├── lab-44-nacos-discovery-demo/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab44/ │ │ │ └── nacosdemo/ │ │ │ ├── Application.java │ │ │ └── controller/ │ │ │ ├── ConsumerController.java │ │ │ └── ProviderController.java │ │ └── resources/ │ │ └── application.yaml │ ├── pom.xml │ ├── 《芋道 Spring Boot 注册中心 Nacos 入门》.md │ └── 《芋道 Spring Boot 配置中心 Nacos 入门》.md ├── lab-45/ │ ├── lab-45-apollo-demo/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab45/ │ │ │ └── apollodemo/ │ │ │ ├── Application.java │ │ │ └── OrderProperties.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-45-apollo-demo-auto-refresh/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab45/ │ │ │ └── apollodemo/ │ │ │ ├── Application.java │ │ │ ├── controller/ │ │ │ │ └── DemoController.java │ │ │ ├── listener/ │ │ │ │ └── LoggingSystemConfigListener.java │ │ │ └── properties/ │ │ │ └── TestProperties.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-45-apollo-demo-jasypt/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab45/ │ │ │ │ └── apollodemo/ │ │ │ │ ├── Application.java │ │ │ │ └── controller/ │ │ │ │ └── DemoController.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab45/ │ │ └── apollodemo/ │ │ └── JasyptTest.java │ ├── lab-45-apollo-demo-multi/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab45/ │ │ │ └── apollodemo/ │ │ │ ├── Application.java │ │ │ └── controller/ │ │ │ └── DemoController.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-45-apollo-demo-profiles/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab45/ │ │ │ └── apollodemo/ │ │ │ └── ProfilesApplication.java │ │ └── resources/ │ │ ├── application-dev.yaml │ │ ├── application-prod.yaml │ │ └── application.yaml │ ├── pom.xml │ └── 《芋道 Spring Boot 配置中心 Apollo 入门》.md ├── lab-46/ │ ├── lab-46-sentinel-demo/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab46/ │ │ │ └── sentineldemo/ │ │ │ ├── Application.java │ │ │ ├── config/ │ │ │ │ ├── SentinelConfiguration.java │ │ │ │ └── SpringMvcConfiguration.java │ │ │ ├── controller/ │ │ │ │ └── DemoController.java │ │ │ └── web/ │ │ │ └── GlobalExceptionHandler.java │ │ └── resources/ │ │ └── sentinel.properties │ ├── lab-46-sentinel-demo-apollo/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab46/ │ │ │ └── sentineldemo/ │ │ │ ├── Application.java │ │ │ ├── config/ │ │ │ │ ├── SentinelConfiguration.java │ │ │ │ └── SpringMvcConfiguration.java │ │ │ ├── controller/ │ │ │ │ └── DemoController.java │ │ │ └── web/ │ │ │ └── GlobalExceptionHandler.java │ │ └── resources/ │ │ ├── application.yaml │ │ └── sentinel.properties │ ├── lab-46-sentinel-demo-file/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab46/ │ │ │ └── sentineldemo/ │ │ │ ├── Application.java │ │ │ ├── config/ │ │ │ │ ├── SentinelConfiguration.java │ │ │ │ └── SpringMvcConfiguration.java │ │ │ ├── controller/ │ │ │ │ └── DemoController.java │ │ │ └── web/ │ │ │ └── GlobalExceptionHandler.java │ │ └── resources/ │ │ ├── flow-rule.json │ │ └── sentinel.properties │ ├── lab-46-sentinel-demo-nacos/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab46/ │ │ │ └── sentineldemo/ │ │ │ ├── Application.java │ │ │ ├── config/ │ │ │ │ ├── SentinelConfiguration.java │ │ │ │ └── SpringMvcConfiguration.java │ │ │ ├── controller/ │ │ │ │ └── DemoController.java │ │ │ └── web/ │ │ │ └── GlobalExceptionHandler.java │ │ └── resources/ │ │ └── sentinel.properties │ ├── pom.xml │ └── 《芋道 Spring Boot 服务容错 Sentinel 入门》.md ├── lab-47/ │ ├── lab-47-demo/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab47/ │ │ │ └── demo/ │ │ │ └── DemoApplication.java │ │ └── resources/ │ │ └── application.yaml │ ├── pom.xml │ ├── yunai-server-spring-boot-starter/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab47/ │ │ │ └── yunaiserver/ │ │ │ └── autoconfigure/ │ │ │ ├── YunaiServerAutoConfiguration.java │ │ │ └── YunaiServerProperties.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── spring.factories │ └── 《芋道 Spring Boot 自动配置原理》.md ├── lab-48-hot-swap/ │ ├── lab-48-demo/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab48/ │ │ └── demo/ │ │ ├── DemoApplication.java │ │ └── controller/ │ │ └── DemoController.java │ ├── pom.xml │ └── 《芋道 Spring Boot 热部署入门》.md ├── lab-49/ │ ├── lab-49-lombok-demo/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab49/ │ │ │ └── lombokdemo/ │ │ │ ├── LombokApplication.java │ │ │ ├── dataobject/ │ │ │ │ ├── UserDO.java │ │ │ │ └── UserDO01.java │ │ │ └── service/ │ │ │ ├── UserService.java │ │ │ └── UserService01.java │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab49/ │ │ └── lombokdemo/ │ │ ├── dataobject/ │ │ │ └── UserDOTest.java │ │ ├── package-info.java │ │ └── service/ │ │ └── UserService01Test.java │ ├── pom.xml │ └── 《芋道 Spring Boot 消除冗余代码 Lombok 入门》.md ├── lab-50/ │ ├── lab-50-demo/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab50/ │ │ │ │ └── maildemo/ │ │ │ │ └── Application.java │ │ │ └── resources/ │ │ │ └── application.yml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab50/ │ │ └── maildemo/ │ │ └── ApplicationTests.java │ ├── pom.xml │ └── 计划中 ├── lab-51/ │ ├── lab-51-sentry-logback/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab51/ │ │ │ └── sentrydemo/ │ │ │ ├── DemoApplication.java │ │ │ ├── controller/ │ │ │ │ └── DemoController.java │ │ │ └── core/ │ │ │ ├── package-info.java │ │ │ └── web/ │ │ │ └── GlobalExceptionHandler.java │ │ └── resources/ │ │ ├── logback-spring.xml │ │ └── sentry.properties │ ├── lab-51-sentry-spring/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab51/ │ │ │ └── sentrydemo/ │ │ │ ├── DemoApplication.java │ │ │ └── controller/ │ │ │ └── DemoController.java │ │ └── resources/ │ │ └── application.yml │ ├── pom.xml │ └── 《芋道 Spring Boot 异常管理平台 Sentry 入门》.md ├── lab-52/ │ ├── lab-52-multiple-datasource/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab52/ │ │ │ └── seatademo/ │ │ │ ├── MultipleDatasourceApplication.java │ │ │ ├── controller/ │ │ │ │ └── OrderController.java │ │ │ ├── dao/ │ │ │ │ ├── AccountDao.java │ │ │ │ ├── OrderDao.java │ │ │ │ └── ProductDao.java │ │ │ ├── entity/ │ │ │ │ └── OrderDO.java │ │ │ └── service/ │ │ │ ├── AccountService.java │ │ │ ├── OrderService.java │ │ │ ├── ProductService.java │ │ │ └── impl/ │ │ │ ├── AccountServiceImpl.java │ │ │ ├── OrderServiceImpl.java │ │ │ └── ProductServiceImpl.java │ │ └── resources/ │ │ ├── application.yaml │ │ └── data.sql │ ├── lab-52-seata-at-httpclient-demo/ │ │ ├── lab-52-seata-at-httpclient-demo-account-service/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab52/ │ │ │ │ └── accountservice/ │ │ │ │ ├── AccountServiceApplication.java │ │ │ │ ├── controller/ │ │ │ │ │ └── AccountController.java │ │ │ │ ├── dao/ │ │ │ │ │ └── AccountDao.java │ │ │ │ ├── dto/ │ │ │ │ │ └── AccountReduceBalanceDTO.java │ │ │ │ └── service/ │ │ │ │ ├── AccountService.java │ │ │ │ └── AccountServiceImpl.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ ├── lab-52-seata-at-httpclient-demo-order-service/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab52/ │ │ │ │ └── orderservice/ │ │ │ │ ├── OrderServiceApplication.java │ │ │ │ ├── controller/ │ │ │ │ │ └── OrderController.java │ │ │ │ ├── dao/ │ │ │ │ │ └── OrderDao.java │ │ │ │ ├── entity/ │ │ │ │ │ └── OrderDO.java │ │ │ │ └── service/ │ │ │ │ ├── OrderService.java │ │ │ │ └── OrderServiceImpl.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ ├── lab-52-seata-at-httpclient-demo-product-service/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab52/ │ │ │ │ └── productservice/ │ │ │ │ ├── ProductServiceApplication.java │ │ │ │ ├── controller/ │ │ │ │ │ └── ProductController.java │ │ │ │ ├── dao/ │ │ │ │ │ └── ProductDao.java │ │ │ │ ├── dto/ │ │ │ │ │ └── ProductReduceStockDTO.java │ │ │ │ └── service/ │ │ │ │ ├── ProductService.java │ │ │ │ └── ProductServiceImpl.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── pom.xml │ ├── pom.xml │ └── 《芋道 Spring Boot 分布式事务 Seata 入门》.md ├── lab-53/ │ ├── lab-53-seata-at-dubbo-demo/ │ │ ├── data.sql │ │ ├── lab-53-seata-at-dubbo-demo-account-service/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab53/ │ │ │ │ └── accountservice/ │ │ │ │ ├── AccountServiceApplication.java │ │ │ │ ├── dao/ │ │ │ │ │ └── AccountDao.java │ │ │ │ └── service/ │ │ │ │ └── AccountServiceImpl.java │ │ │ └── resources/ │ │ │ ├── application-file.yaml │ │ │ └── application.yaml │ │ ├── lab-53-seata-at-dubbo-demo-account-service-api/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab53/ │ │ │ └── accountservice/ │ │ │ └── api/ │ │ │ └── AccountService.java │ │ ├── lab-53-seata-at-dubbo-demo-order-service/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab53/ │ │ │ │ └── orderservice/ │ │ │ │ ├── OrderServiceApplication.java │ │ │ │ ├── controller/ │ │ │ │ │ └── OrderController.java │ │ │ │ ├── dao/ │ │ │ │ │ └── OrderDao.java │ │ │ │ ├── entity/ │ │ │ │ │ └── OrderDO.java │ │ │ │ └── service/ │ │ │ │ └── OrderServiceImpl.java │ │ │ └── resources/ │ │ │ ├── application-file.yaml │ │ │ └── application.yaml │ │ ├── lab-53-seata-at-dubbo-demo-order-service-api/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab53/ │ │ │ └── orderservice/ │ │ │ └── api/ │ │ │ └── OrderService.java │ │ ├── lab-53-seata-at-dubbo-demo-product-service/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab53/ │ │ │ │ └── productservice/ │ │ │ │ ├── ProductServiceApplication.java │ │ │ │ ├── dao/ │ │ │ │ │ └── ProductDao.java │ │ │ │ └── service/ │ │ │ │ └── ProductServiceImpl.java │ │ │ └── resources/ │ │ │ ├── application-file.yaml │ │ │ └── application.yaml │ │ ├── lab-53-seata-at-dubbo-demo-product-service-api/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab53/ │ │ │ └── productservice/ │ │ │ └── api/ │ │ │ └── ProductService.java │ │ └── pom.xml │ ├── pom.xml │ └── 《Dubbo 分布式事务 Seata 入门》.md ├── lab-54/ │ ├── lab-54-demo/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab54/ │ │ └── eventdemo/ │ │ ├── DemoApplication.java │ │ ├── controller/ │ │ │ └── DemoController.java │ │ ├── event/ │ │ │ └── UserRegisterEvent.java │ │ └── service/ │ │ ├── CouponService.java │ │ ├── EmailService.java │ │ └── UserService.java │ ├── pom.xml │ └── 《芋道 Spring Boot 事件机制 Event 入门》.md ├── lab-55/ │ ├── lab-55-mapstruct-demo/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab55/ │ │ └── mapstructdemo/ │ │ ├── UserBOTest.java │ │ ├── UserDetailBOTest.java │ │ ├── bo/ │ │ │ ├── UserBO.java │ │ │ └── UserDetailBO.java │ │ ├── convert/ │ │ │ └── UserConvert.java │ │ └── dataobject/ │ │ └── UserDO.java │ ├── lab-55-mapstruct-demo-lombok/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab55/ │ │ └── mapstructdemo/ │ │ ├── UserBOTest.java │ │ ├── bo/ │ │ │ └── UserBO.java │ │ ├── convert/ │ │ │ └── UserConvert.java │ │ └── dataobject/ │ │ └── UserDO.java │ ├── pom.xml │ └── 《芋道 Spring Boot 对象转换 MapStruct 入门》.md ├── lab-56/ │ ├── lab-56-demo01/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab56/ │ │ ├── Demo01Application.java │ │ ├── Demo02Application.java │ │ └── controller/ │ │ └── DemoController.java │ ├── pom.xml │ ├── 《芋道 APISIX 极简入门(国产微服务网关)》.md │ └── 《芋道 Kong 极简入门(微服务网关)》.md ├── lab-57/ │ ├── lab-57-hystrix-demo01/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab57/ │ │ └── hystrixdemo/ │ │ ├── DemoApplication.java │ │ ├── config/ │ │ │ └── HystrixConfig.java │ │ └── controller/ │ │ └── DemoController.java │ ├── lab-57-hystrix-dubbo-demo/ │ │ ├── lab-57-hystrix-dubbo-demo-application/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springcloud/ │ │ │ │ └── labx23/ │ │ │ │ └── demo/ │ │ │ │ ├── DemoApplication.java │ │ │ │ ├── config/ │ │ │ │ │ └── HystrixConfig.java │ │ │ │ └── controller/ │ │ │ │ └── DemoController.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ ├── lab-57-hystrix-dubbo-demo-user-service/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab57/ │ │ │ │ └── userservice/ │ │ │ │ ├── UserServiceApplication.java │ │ │ │ └── service/ │ │ │ │ └── UserServiceImpl.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ ├── lab-57-hystrix-dubbo-demo-user-service-api/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab57/ │ │ │ └── userservice/ │ │ │ └── api/ │ │ │ └── UserService.java │ │ └── pom.xml │ ├── lab-57-user-service/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab57/ │ │ └── userservice/ │ │ └── UserServiceApplication.java │ ├── pom.xml │ └── 《芋道 Spring Boot 服务容错 Hystrix 入门》.md ├── lab-58/ │ ├── lab-58-feign-demo/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab58/ │ │ └── feigndemo/ │ │ ├── FeignDemoApplication.java │ │ ├── config/ │ │ │ └── FeignConfig.java │ │ ├── controller/ │ │ │ ├── DemoController.java │ │ │ └── DemoController02.java │ │ └── feign/ │ │ ├── UserServiceFeignClient.java │ │ ├── UserServiceFeignClient02.java │ │ ├── request/ │ │ │ └── UserAddRequest.java │ │ └── response/ │ │ └── UserResponse.java │ ├── lab-58-user-service/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab58/ │ │ └── userservice/ │ │ ├── UserServiceApplication.java │ │ ├── controller/ │ │ │ └── UserController.java │ │ ├── request/ │ │ │ └── UserAddRequest.java │ │ └── response/ │ │ └── UserResponse.java │ ├── pom.xml │ └── 《芋道 Spring Boot 声明式调用 Feign 入门》.md ├── lab-59/ │ ├── lab-59-resilience4j-actuator/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab59/ │ │ │ └── resillience4jdemo/ │ │ │ ├── DemoApplication.java │ │ │ └── controller/ │ │ │ ├── BulkheadDemoController.java │ │ │ ├── DemoController.java │ │ │ ├── RateLimiterDemoController.java │ │ │ ├── RetryDemoController.java │ │ │ ├── ThreadPoolBulkheadDemoController.java │ │ │ └── TimeLimiterDemoController.java │ │ └── resources/ │ │ └── application.yml │ ├── lab-59-resilience4j-demo01/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab59/ │ │ │ └── resillience4jdemo/ │ │ │ ├── DemoApplication.java │ │ │ └── controller/ │ │ │ ├── BulkheadDemoController.java │ │ │ ├── DemoController.java │ │ │ ├── RateLimiterDemoController.java │ │ │ ├── RetryDemoController.java │ │ │ ├── ThreadPoolBulkheadDemoController.java │ │ │ └── TimeLimiterDemoController.java │ │ └── resources/ │ │ └── application.yml │ ├── lab-59-user-service/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab59/ │ │ └── userservice/ │ │ └── UserServiceApplication.java │ ├── pom.xml │ └── 《芋道 Spring Boot 服务容错 Resilience4j 入门》.md ├── lab-60/ │ ├── lab-60-soul-dubbo-demo/ │ │ ├── lab-60-soul-dubbo-demo-user-service/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab60/ │ │ │ │ └── userservice/ │ │ │ │ ├── UserServiceApplication.java │ │ │ │ └── service/ │ │ │ │ └── UserServiceImpl.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ ├── lab-60-soul-dubbo-demo-user-service-api/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab60/ │ │ │ └── userservice/ │ │ │ └── api/ │ │ │ ├── UserService.java │ │ │ └── dto/ │ │ │ └── UserCreateDTO.java │ │ └── pom.xml │ ├── lab-60-soul-spring-boot-demo/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab60/ │ │ │ ├── DemoApplication.java │ │ │ ├── controller/ │ │ │ │ └── UserController.java │ │ │ └── dto/ │ │ │ └── UserCreateDTO.java │ │ └── resources/ │ │ └── application.yaml │ ├── lab-60-soul-spring-cloud-demo/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── lab60/ │ │ │ ├── DemoApplication.java │ │ │ ├── controller/ │ │ │ │ └── UserController.java │ │ │ └── dto/ │ │ │ └── UserCreateDTO.java │ │ └── resources/ │ │ └── application.yaml │ ├── pom.xml │ └── 《芋道 Soul 极简入门(国产微服务网关)》.md ├── lab-61/ │ ├── lab-61-cat-opentracing/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab61/ │ │ └── cat/ │ │ └── opentracing/ │ │ ├── CatSpan.java │ │ ├── CatSpanBuilder.java │ │ ├── CatTracer.java │ │ └── Tag.java │ ├── lab-61-cat-opentracing-demo/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── catdemo/ │ │ │ ├── Application.java │ │ │ ├── config/ │ │ │ │ └── ZipkinConfig.java │ │ │ └── controller/ │ │ │ └── DemoController.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── app.properties │ ├── lab-61-demo/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab61/ │ │ │ └── catdemo/ │ │ │ ├── Application.java │ │ │ └── controller/ │ │ │ └── DemoController.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── app.properties │ ├── lab-61-logback/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab61/ │ │ │ └── catdemo/ │ │ │ ├── Application.java │ │ │ └── controller/ │ │ │ └── LoggerController.java │ │ └── resources/ │ │ ├── META-INF/ │ │ │ └── app.properties │ │ └── logback-spring.xml │ ├── lab-61-springmvc/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab61/ │ │ │ └── catdemo/ │ │ │ ├── Application.java │ │ │ ├── config/ │ │ │ │ └── CatFilterConfigure.java │ │ │ └── controller/ │ │ │ └── DemoController.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── app.properties │ ├── pom.xml │ └── 《芋道 Spring Boot 监控平台 CAT 入门》.md ├── lab-62/ │ ├── lab-62-sofarpc-annotations-demo/ │ │ ├── lab-62-sofarpc-annotations-demo-user-rpc-service-api/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab62/ │ │ │ └── rpc/ │ │ │ ├── api/ │ │ │ │ └── UserRpcService.java │ │ │ └── dto/ │ │ │ ├── UserAddDTO.java │ │ │ └── UserDTO.java │ │ ├── lab-62-sofarpc-annotations-demo-user-rpc-service-consumer/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab62/ │ │ │ │ └── rpc/ │ │ │ │ ├── ConsumerApplication.java │ │ │ │ └── controller/ │ │ │ │ └── UserController.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ ├── lab-62-sofarpc-annotations-demo-user-rpc-service-provider/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab62/ │ │ │ │ └── rpc/ │ │ │ │ ├── ProviderApplication.java │ │ │ │ └── service/ │ │ │ │ └── UserRpcServiceImpl.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── pom.xml │ ├── lab-62-sofarpc-xml-demo/ │ │ ├── lab-62-sofarpc-xml-demo-user-rpc-service-api/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab62/ │ │ │ └── rpc/ │ │ │ ├── api/ │ │ │ │ └── UserRpcService.java │ │ │ └── dto/ │ │ │ ├── UserAddDTO.java │ │ │ └── UserDTO.java │ │ ├── lab-62-sofarpc-xml-demo-user-rpc-service-consumer/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab62/ │ │ │ │ └── rpc/ │ │ │ │ ├── ConsumerApplication.java │ │ │ │ └── controller/ │ │ │ │ └── UserController.java │ │ │ └── resources/ │ │ │ ├── application.yaml │ │ │ └── sofarpc.xml │ │ ├── lab-62-sofarpc-xml-demo-user-rpc-service-provider/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab62/ │ │ │ │ └── rpc/ │ │ │ │ ├── ProviderApplication.java │ │ │ │ └── service/ │ │ │ │ └── UserRpcServiceImpl.java │ │ │ └── resources/ │ │ │ ├── application.yaml │ │ │ └── sofarpc.xml │ │ └── pom.xml │ ├── pom.xml │ └── 《芋道 Spring Boot SOFARPC 入门》.md ├── lab-63/ │ ├── lab-63-motan-annotations-demo/ │ │ ├── lab-63-motan-annotations-demo-user-rpc-service-api/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab63/ │ │ │ └── rpc/ │ │ │ ├── api/ │ │ │ │ └── UserRpcService.java │ │ │ └── dto/ │ │ │ ├── UserAddDTO.java │ │ │ └── UserDTO.java │ │ ├── lab-63-motan-annotations-demo-user-rpc-service-consumer/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab63/ │ │ │ │ └── rpc/ │ │ │ │ ├── ConsumerApplication.java │ │ │ │ └── controller/ │ │ │ │ └── UserController.java │ │ │ └── resources/ │ │ │ └── motan.xml │ │ ├── lab-63-motan-annotations-demo-user-rpc-service-provider/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab63/ │ │ │ │ └── rpc/ │ │ │ │ ├── ProviderApplication.java │ │ │ │ └── service/ │ │ │ │ └── UserRpcServiceImpl.java │ │ │ └── resources/ │ │ │ └── motan.xml │ │ └── pom.xml │ ├── lab-63-motan-xml-demo/ │ │ ├── lab-63-motan-xml-demo-user-rpc-service-api/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab63/ │ │ │ └── rpc/ │ │ │ ├── api/ │ │ │ │ └── UserRpcService.java │ │ │ └── dto/ │ │ │ ├── UserAddDTO.java │ │ │ └── UserDTO.java │ │ ├── lab-63-motan-xml-demo-user-rpc-service-consumer/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab63/ │ │ │ │ └── rpc/ │ │ │ │ ├── ConsumerApplication.java │ │ │ │ └── controller/ │ │ │ │ └── UserController.java │ │ │ └── resources/ │ │ │ └── motan.xml │ │ ├── lab-63-motan-xml-demo-user-rpc-service-provider/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab63/ │ │ │ │ └── rpc/ │ │ │ │ ├── ProviderApplication.java │ │ │ │ └── service/ │ │ │ │ └── UserRpcServiceImpl.java │ │ │ └── resources/ │ │ │ └── motan.xml │ │ └── pom.xml │ ├── pom.xml │ └── 《芋道 Spring Boot Motan 入门》.md ├── lab-64/ │ ├── lab-64-grpc-demo/ │ │ ├── lab-64-grpc-demo-application/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab64/ │ │ │ └── demo/ │ │ │ ├── DemoApplication.java │ │ │ ├── config/ │ │ │ │ └── GrpcConfig.java │ │ │ └── controller/ │ │ │ └── DemoController.java │ │ ├── lab-64-grpc-demo-user-service/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab64/ │ │ │ └── userservice/ │ │ │ ├── UserServiceApplication.java │ │ │ ├── config/ │ │ │ │ └── GrpcConfig.java │ │ │ └── rpc/ │ │ │ └── UserServiceGrpcImpl.java │ │ ├── lab-64-grpc-demo-user-service-api/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── proto/ │ │ │ └── UserService.proto │ │ └── pom.xml │ ├── lab-64-grpc-starter/ │ │ ├── lab-64-grpc-starter-application/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab64/ │ │ │ │ └── demo/ │ │ │ │ ├── DemoApplication.java │ │ │ │ └── controller/ │ │ │ │ └── DemoController.java │ │ │ └── resources/ │ │ │ └── application.yml │ │ ├── lab-64-grpc-starter-user-service/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab64/ │ │ │ │ └── userservice/ │ │ │ │ ├── UserServiceApplication.java │ │ │ │ └── rpc/ │ │ │ │ └── UserServiceGrpcImpl.java │ │ │ └── resources/ │ │ │ └── application.yml │ │ ├── lab-64-grpc-starter-user-service-api/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── proto/ │ │ │ └── UserService.proto │ │ └── pom.xml │ ├── pom.xml │ └── 《芋道 Spring Boot gRPC 入门》.md ├── lab-65/ │ ├── lab-65-cxf-ws-demo/ │ │ ├── lab-65-cxf-ws-demo-application/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab65/ │ │ │ │ └── demo/ │ │ │ │ ├── DemoApplication.java │ │ │ │ ├── config/ │ │ │ │ │ └── CXFConfig.java │ │ │ │ └── controller/ │ │ │ │ └── DemoController.java │ │ │ └── resources/ │ │ │ └── wsdl/ │ │ │ └── user.wsdl │ │ ├── lab-65-cxf-ws-demo-user-service/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab65/ │ │ │ │ └── userservice/ │ │ │ │ ├── UserServiceApplication.java │ │ │ │ ├── config/ │ │ │ │ │ └── CXFConfig.java │ │ │ │ ├── request/ │ │ │ │ │ ├── UserCreateRequest.java │ │ │ │ │ └── UserGetRequest.java │ │ │ │ ├── response/ │ │ │ │ │ ├── UserCreateResponse.java │ │ │ │ │ └── UserGetResponse.java │ │ │ │ └── service/ │ │ │ │ ├── UserService.java │ │ │ │ └── UserServiceImpl.java │ │ │ └── resources/ │ │ │ └── application.yml │ │ └── pom.xml │ ├── lab-65-spring-ws-demo/ │ │ ├── lab-65-spring-ws-demo-application/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab65/ │ │ │ │ └── demo/ │ │ │ │ ├── DemoApplication.java │ │ │ │ ├── client/ │ │ │ │ │ └── UserClient.java │ │ │ │ ├── config/ │ │ │ │ │ └── WebServicesConfig.java │ │ │ │ └── controller/ │ │ │ │ └── DemoController.java │ │ │ └── resources/ │ │ │ └── application.yml │ │ ├── lab-65-spring-ws-demo-user-service/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab65/ │ │ │ │ └── userservice/ │ │ │ │ ├── UserServiceApplication.java │ │ │ │ ├── config/ │ │ │ │ │ └── WebServicesConfig.java │ │ │ │ └── endpoint/ │ │ │ │ └── UserEndpoint.java │ │ │ └── resources/ │ │ │ └── users.xsd │ │ └── pom.xml │ ├── lab-65-ws-feign-client/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab65/ │ │ │ └── demo/ │ │ │ ├── FeignDemoApplication.java │ │ │ ├── config/ │ │ │ │ └── FeignConfig.java │ │ │ ├── controller/ │ │ │ │ └── DemoController.java │ │ │ └── feign/ │ │ │ └── UserServiceFeignClient.java │ │ └── resources/ │ │ └── application.yml │ ├── pom.xml │ └── 《芋道 Spring Boot Web Services 入门》.md ├── lab-66/ │ ├── lab-66-spring-data-solr/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab15/ │ │ │ │ └── springdatasolr/ │ │ │ │ ├── Application.java │ │ │ │ ├── dataobject/ │ │ │ │ │ └── SolrProductDO.java │ │ │ │ └── repository/ │ │ │ │ ├── ProductRepository.java │ │ │ │ ├── ProductRepository02.java │ │ │ │ └── ProductRepository03.java │ │ │ └── resources/ │ │ │ └── application.yml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab15/ │ │ └── springdatasolr/ │ │ └── repository/ │ │ ├── ProductRepository02Test.java │ │ ├── ProductRepository03Test.java │ │ └── ProductRepositoryTest.java │ ├── pom.xml │ └── 《芋道 Spring Boot Web Solr 入门》.md ├── lab-67/ │ ├── lab-67-netty-demo/ │ │ ├── lab-67-netty-demo-client/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab67/ │ │ │ │ └── nettyclientdemo/ │ │ │ │ ├── NettyClientApplication.java │ │ │ │ ├── client/ │ │ │ │ │ ├── NettyClient.java │ │ │ │ │ └── handler/ │ │ │ │ │ ├── NettyClientHandler.java │ │ │ │ │ └── NettyClientHandlerInitializer.java │ │ │ │ ├── config/ │ │ │ │ │ └── NettyClientConfig.java │ │ │ │ ├── controller/ │ │ │ │ │ └── TestController.java │ │ │ │ ├── message/ │ │ │ │ │ ├── auth/ │ │ │ │ │ │ ├── AuthRequest.java │ │ │ │ │ │ └── AuthResponse.java │ │ │ │ │ ├── chat/ │ │ │ │ │ │ ├── ChatRedirectToUserRequest.java │ │ │ │ │ │ ├── ChatSendResponse.java │ │ │ │ │ │ ├── ChatSendToAllRequest.java │ │ │ │ │ │ └── ChatSendToOneRequest.java │ │ │ │ │ └── heartbeat/ │ │ │ │ │ ├── HeartbeatRequest.java │ │ │ │ │ └── HeartbeatResponse.java │ │ │ │ └── messagehandler/ │ │ │ │ ├── auth/ │ │ │ │ │ └── AuthResponseHandler.java │ │ │ │ ├── chat/ │ │ │ │ │ ├── ChatRedirectToUserRequestHandler.java │ │ │ │ │ └── ChatSendResponseHandler.java │ │ │ │ └── heartbeat/ │ │ │ │ └── HeartbeatResponseHandler.java │ │ │ └── resources/ │ │ │ └── application.yml │ │ ├── lab-67-netty-demo-common/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab67/ │ │ │ └── nettycommondemo/ │ │ │ ├── codec/ │ │ │ │ ├── Invocation.java │ │ │ │ ├── InvocationDecoder.java │ │ │ │ └── InvocationEncoder.java │ │ │ └── dispatcher/ │ │ │ ├── Message.java │ │ │ ├── MessageDispatcher.java │ │ │ ├── MessageHandler.java │ │ │ └── MessageHandlerContainer.java │ │ ├── lab-67-netty-demo-server/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springboot/ │ │ │ │ └── lab67/ │ │ │ │ └── nettyserverdemo/ │ │ │ │ ├── NettyServerApplication.java │ │ │ │ ├── config/ │ │ │ │ │ └── NettyServerConfig.java │ │ │ │ ├── message/ │ │ │ │ │ ├── auth/ │ │ │ │ │ │ ├── AuthRequest.java │ │ │ │ │ │ └── AuthResponse.java │ │ │ │ │ ├── chat/ │ │ │ │ │ │ ├── ChatRedirectToUserRequest.java │ │ │ │ │ │ ├── ChatSendResponse.java │ │ │ │ │ │ ├── ChatSendToAllRequest.java │ │ │ │ │ │ └── ChatSendToOneRequest.java │ │ │ │ │ └── heartbeat/ │ │ │ │ │ ├── HeartbeatRequest.java │ │ │ │ │ └── HeartbeatResponse.java │ │ │ │ ├── messagehandler/ │ │ │ │ │ ├── auth/ │ │ │ │ │ │ └── AuthRequestHandler.java │ │ │ │ │ ├── chat/ │ │ │ │ │ │ ├── ChatSendToAllHandler.java │ │ │ │ │ │ └── ChatSendToOneHandler.java │ │ │ │ │ └── heartbeat/ │ │ │ │ │ └── HeartbeatRequestHandler.java │ │ │ │ └── server/ │ │ │ │ ├── NettyChannelManager.java │ │ │ │ ├── NettyServer.java │ │ │ │ └── handler/ │ │ │ │ ├── NettyServerHandler.java │ │ │ │ └── NettyServerHandlerInitializer.java │ │ │ └── resources/ │ │ │ └── application.yml │ │ └── pom.xml │ ├── pom.xml │ └── 《芋道 Spring Boot Netty 入门》.md ├── lab-68-spring-security-oauth/ │ ├── lab-68-demo01-authorization-code-server/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab68/ │ │ └── resourceserverdemo/ │ │ ├── ResourceServerApplication.java │ │ ├── config/ │ │ │ ├── OAuth2AuthorizationServerConfig.java │ │ │ ├── OAuth2ResourceServerConfig.java │ │ │ └── SecurityConfig.java │ │ └── controller/ │ │ └── ExampleController.java │ ├── lab-68-demo01-client-credentials-server/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab68/ │ │ └── resourceserverdemo/ │ │ ├── ResourceServerApplication.java │ │ ├── config/ │ │ │ ├── OAuth2AuthorizationServerConfig.java │ │ │ └── OAuth2ResourceServerConfig.java │ │ └── controller/ │ │ └── ExampleController.java │ ├── lab-68-demo01-implicit-server/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab68/ │ │ └── resourceserverdemo/ │ │ ├── ResourceServerApplication.java │ │ ├── config/ │ │ │ ├── OAuth2AuthorizationServerConfig.java │ │ │ ├── OAuth2ResourceServerConfig.java │ │ │ └── SecurityConfig.java │ │ └── controller/ │ │ └── ExampleController.java │ ├── lab-68-demo01-resource-owner-password-credentials-server/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab68/ │ │ └── resourceserverdemo/ │ │ ├── ResourceServerApplication.java │ │ ├── config/ │ │ │ ├── OAuth2AuthorizationServerConfig.java │ │ │ ├── OAuth2ResourceServerConfig.java │ │ │ └── SecurityConfig.java │ │ └── controller/ │ │ └── ExampleController.java │ ├── lab-68-demo02-authorization-server-with-authorization-code/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab68/ │ │ └── authorizationserverdemo/ │ │ ├── AuthorizationServerApplication.java │ │ └── config/ │ │ ├── OAuth2AuthorizationServerConfig.java │ │ └── SecurityConfig.java │ ├── lab-68-demo02-authorization-server-with-client-credentials/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab68/ │ │ └── authorizationserverdemo/ │ │ ├── AuthorizationServerApplication.java │ │ └── config/ │ │ └── OAuth2AuthorizationServerConfig.java │ ├── lab-68-demo02-authorization-server-with-implicit/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab68/ │ │ └── authorizationserverdemo/ │ │ ├── AuthorizationServerApplication.java │ │ └── config/ │ │ ├── OAuth2AuthorizationServerConfig.java │ │ └── SecurityConfig.java │ ├── lab-68-demo02-authorization-server-with-resource-owner-password-credentials/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab68/ │ │ └── authorizationserverdemo/ │ │ ├── AuthorizationServerApplication.java │ │ └── config/ │ │ ├── OAuth2AuthorizationServerConfig.java │ │ └── SecurityConfig.java │ ├── lab-68-demo02-resource-server/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab68/ │ │ │ └── resourceserverdemo/ │ │ │ ├── ResourceServerApplication.java │ │ │ ├── config/ │ │ │ │ └── OAuth2ResourceServerConfig.java │ │ │ └── controller/ │ │ │ ├── Callback02Controller.java │ │ │ ├── CallbackController.java │ │ │ ├── ClientLoginController.java │ │ │ ├── ExampleController.java │ │ │ └── LoginController.java │ │ └── resources/ │ │ └── application.yml │ ├── lab-68-demo03-authorization-server-with-resource-owner-password-credentials/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab68/ │ │ └── authorizationserverdemo/ │ │ ├── AuthorizationServerApplication.java │ │ ├── config/ │ │ │ ├── OAuth2AuthorizationServerConfig.java │ │ │ └── SecurityConfig.java │ │ └── controller/ │ │ └── TokenDemoController.java │ ├── lab-68-demo11-authorization-server-by-jdbc-store/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab68/ │ │ │ └── authorizationserverdemo/ │ │ │ ├── AuthorizationServerApplication.java │ │ │ └── config/ │ │ │ ├── OAuth2AuthorizationServerConfig.java │ │ │ └── SecurityConfig.java │ │ └── resources/ │ │ ├── application.yaml │ │ └── db/ │ │ ├── data.sql │ │ └── schema.sql │ ├── lab-68-demo11-authorization-server-by-jwt-store/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab68/ │ │ └── authorizationserverdemo/ │ │ ├── AuthorizationServerApplication.java │ │ └── config/ │ │ ├── OAuth2AuthorizationServerConfig.java │ │ └── SecurityConfig.java │ ├── lab-68-demo11-authorization-server-by-redis-store/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab68/ │ │ │ └── authorizationserverdemo/ │ │ │ ├── AuthorizationServerApplication.java │ │ │ └── config/ │ │ │ ├── OAuth2AuthorizationServerConfig.java │ │ │ └── SecurityConfig.java │ │ └── resources/ │ │ └── application.yml │ ├── lab-68-demo21-authorization-server-on-sso/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab68/ │ │ │ └── authorizationserverdemo/ │ │ │ ├── AuthorizationServerApplication.java │ │ │ └── config/ │ │ │ ├── OAuth2AuthorizationServerConfig.java │ │ │ └── SecurityConfig.java │ │ └── resources/ │ │ ├── application.yaml │ │ └── db/ │ │ ├── oauth_data.sql │ │ ├── oauth_schema.sql │ │ ├── user_data.sql │ │ └── user_schema.sql │ ├── lab-68-demo21-resource-server-on-sso/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── lab68/ │ │ │ └── resourceserverdemo/ │ │ │ ├── ResourceServerApplication.java │ │ │ ├── config/ │ │ │ │ ├── OAuthSsoConfig.java │ │ │ │ └── SecurityConfig.java │ │ │ └── controller/ │ │ │ ├── DemoController.java │ │ │ └── UserController.java │ │ └── resources/ │ │ └── application.yml │ ├── pom.xml │ ├── 《芋道 Spring Security OAuth2 入门》.md │ ├── 《芋道 Spring Security OAuth2 单点登陆》.md │ └── 《芋道 Spring Security OAuth2 存储器》.md ├── lab-69-proxy/ │ ├── lab-69-proxy-cglib/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── labs/ │ │ └── lab69/ │ │ ├── TestProxyMain.java │ │ ├── intercept/ │ │ │ └── UserServiceMethodInterceptor.java │ │ └── service/ │ │ └── UserServiceImpl.java │ ├── lab-69-proxy-jdk/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── labs/ │ │ └── lab69/ │ │ ├── GenerateProxyMain.java │ │ ├── TestProxyMain.java │ │ ├── handler/ │ │ │ └── UserServiceHandler.java │ │ └── service/ │ │ ├── UserService.java │ │ └── UserServiceImpl.java │ └── pom.xml ├── lab-70-db-doc/ │ ├── lab-70-db-doc-screw-01/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── ScrewMain.java │ ├── lab-70-db-doc-screw-02/ │ │ ├── doc/ │ │ │ └── 测试文档名称.html │ │ └── pom.xml │ ├── lab-70-db-doc-screw-03/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── ScrewMain.java │ ├── pom.xml │ └── 《芋道 Spring Boot 数据表结构文档》.md ├── lab-71-http-debug/ │ ├── lab-71-idea-http-client/ │ │ ├── http-client.env.json │ │ ├── http-client.private.env.json │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab71/ │ │ ├── Application.java │ │ ├── controller/ │ │ │ ├── UserController.http │ │ │ ├── UserController.java │ │ │ ├── UserController2.http │ │ │ ├── UserController3.http │ │ │ └── UserController4.http │ │ └── vo/ │ │ └── UserUpdateVO.java │ ├── pom.xml │ └── 《芋道 Spring Boot API 接口调试 IDEA HTTP Client》.md ├── lab-72-minio/ │ ├── pom.xml │ ├── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springboot/ │ │ └── lab72/ │ │ ├── MinIOApplication.java │ │ ├── config/ │ │ │ └── MinIOConfiguration.java │ │ └── controller/ │ │ └── FileController.java │ └── 《芋道 Spring Boot 对象存储 MinIO 入门》.md ├── labx-01-spring-cloud-alibaba-nacos-discovery/ │ ├── labx-01-sca-nacos-discovery-demo01-consumer/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx01/ │ │ │ └── nacosdemo/ │ │ │ └── consumer/ │ │ │ └── DemoConsumerApplication.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-01-sca-nacos-discovery-demo01-provider/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx01/ │ │ │ └── nacosdemo/ │ │ │ └── provider/ │ │ │ └── DemoProviderApplication.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-01-sca-nacos-discovery-demo02-consumer/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx01/ │ │ │ └── nacosdemo/ │ │ │ └── consumer/ │ │ │ └── DemoConsumerApplication.java │ │ └── resources/ │ │ ├── application-dev.yaml │ │ ├── application-uat.yaml │ │ └── application.yaml │ ├── labx-01-sca-nacos-discovery-demo02-provider/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx01/ │ │ │ └── nacosdemo/ │ │ │ └── provider/ │ │ │ └── DemoProviderApplication.java │ │ └── resources/ │ │ ├── application-dev.yaml │ │ ├── application-uat.yaml │ │ └── application.yaml │ ├── labx-01-sca-nacos-discovery-demo03-consumer/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx01/ │ │ │ └── nacosdemo/ │ │ │ └── consumer/ │ │ │ └── DemoConsumerApplication.java │ │ └── resources/ │ │ └── application.yaml │ ├── pom.xml │ └── 《芋道 Spring Cloud Alibaba 注册中心 Nacos 入门》.md ├── labx-02-spring-cloud-netflix-ribbon/ │ ├── labx-02-scn-ribbon-demo01-consumer/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudnetflix/ │ │ │ └── labx02/ │ │ │ └── ribbondemo/ │ │ │ └── consumer/ │ │ │ └── DemoConsumerApplication.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-02-scn-ribbon-demo01-provider/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudnetflix/ │ │ │ └── labx02/ │ │ │ └── ribbondemo/ │ │ │ └── provider/ │ │ │ └── DemoProviderApplication.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-02-scn-ribbon-demo02A-consumer/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudnetflix/ │ │ │ └── labx02/ │ │ │ └── ribbondemo/ │ │ │ └── consumer/ │ │ │ └── DemoConsumerApplication.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-02-scn-ribbon-demo02B-consumer/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ ├── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springcloudnetflix/ │ │ │ │ └── labx02/ │ │ │ │ └── ribbondemo/ │ │ │ │ └── consumer/ │ │ │ │ ├── DemoConsumerApplication.java │ │ │ │ └── config/ │ │ │ │ └── RibbonConfiguration.java │ │ │ └── ribbon/ │ │ │ ├── DefaultRibbonClientConfiguration.java │ │ │ └── UserProviderRibbonClientConfiguration.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-02-scn-ribbon-demo03-consumer/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudnetflix/ │ │ │ └── labx02/ │ │ │ └── ribbondemo/ │ │ │ └── consumer/ │ │ │ └── DemoConsumerApplication.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-02-scn-ribbon-demo04-consumer/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudnetflix/ │ │ │ └── labx02/ │ │ │ └── ribbondemo/ │ │ │ └── consumer/ │ │ │ └── DemoConsumerApplication.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-02-scn-ribbon-demo05-consumer/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudnetflix/ │ │ │ └── labx02/ │ │ │ └── ribbondemo/ │ │ │ └── consumer/ │ │ │ └── DemoConsumerApplication.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-02-scn-ribbon-demo06-consumer/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudnetflix/ │ │ │ └── labx02/ │ │ │ └── ribbondemo/ │ │ │ └── consumer/ │ │ │ └── DemoConsumerApplication.java │ │ └── resources/ │ │ └── application.yaml │ ├── pom.xml │ └── 《芋道 Spring Cloud Netflix 负载均衡 Ribbon 入门》.md ├── labx-03-spring-cloud-feign/ │ ├── labx-03-sc-feign-demo01-consumer/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx03/ │ │ │ └── feigndemo/ │ │ │ └── consumer/ │ │ │ ├── DemoConsumerApplication.java │ │ │ ├── FeignDemo.java │ │ │ ├── controller/ │ │ │ │ └── ConsumerController.java │ │ │ └── feign/ │ │ │ └── DemoProviderFeignClient.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-03-sc-feign-demo01-provider/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx03/ │ │ │ └── feigndemo/ │ │ │ └── provider/ │ │ │ ├── DemoProviderApplication.java │ │ │ └── controller/ │ │ │ └── ProviderController.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-03-sc-feign-demo02A-consumer/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx03/ │ │ │ └── feigndemo/ │ │ │ └── consumer/ │ │ │ ├── DemoConsumerApplication.java │ │ │ ├── controller/ │ │ │ │ └── ConsumerController.java │ │ │ └── feign/ │ │ │ └── DemoProviderFeignClient.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-03-sc-feign-demo02B-consumer/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx03/ │ │ │ └── feigndemo/ │ │ │ └── consumer/ │ │ │ ├── DemoConsumerApplication.java │ │ │ ├── config/ │ │ │ │ ├── DefaultFeignClientConfiguration.java │ │ │ │ └── DemoProviderFeignClientConfiguration.java │ │ │ ├── controller/ │ │ │ │ └── ConsumerController.java │ │ │ └── feign/ │ │ │ └── DemoProviderFeignClient.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-03-sc-feign-demo03-consumer/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx03/ │ │ │ └── feigndemo/ │ │ │ └── consumer/ │ │ │ ├── DemoConsumerApplication.java │ │ │ ├── controller/ │ │ │ │ └── ConsumerController.java │ │ │ └── feign/ │ │ │ └── DemoProviderFeignClient.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-03-sc-feign-demo03-provider/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx03/ │ │ │ └── feigndemo/ │ │ │ └── provider/ │ │ │ ├── DemoProviderApplication.java │ │ │ └── controller/ │ │ │ └── ProviderController.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-03-sc-feign-demo03-provider-api/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springcloud/ │ │ └── labx03/ │ │ └── feigndemo/ │ │ └── provider/ │ │ └── api/ │ │ └── ProviderService.java │ ├── labx-03-sc-feign-demo04-consumer/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx03/ │ │ │ └── feigndemo/ │ │ │ └── consumer/ │ │ │ ├── DemoConsumerApplication.java │ │ │ ├── controller/ │ │ │ │ └── ConsumerController.java │ │ │ ├── dto/ │ │ │ │ └── DemoDTO.java │ │ │ └── feign/ │ │ │ └── DemoProviderFeignClient.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-03-sc-feign-demo04-provider/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx03/ │ │ │ └── feigndemo/ │ │ │ └── provider/ │ │ │ ├── DemoProviderApplication.java │ │ │ ├── controller/ │ │ │ │ └── ProviderController.java │ │ │ └── dto/ │ │ │ └── DemoDTO.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-03-sc-feign-demo05-consumer/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx03/ │ │ │ └── feigndemo/ │ │ │ └── consumer/ │ │ │ ├── DemoConsumerApplication.java │ │ │ ├── controller/ │ │ │ │ └── ConsumerController.java │ │ │ └── feign/ │ │ │ └── DemoProviderFeignClient.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-03-sc-feign-demo06A-consumer/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx03/ │ │ │ └── feigndemo/ │ │ │ └── consumer/ │ │ │ ├── DemoConsumerApplication.java │ │ │ ├── controller/ │ │ │ │ └── ConsumerController.java │ │ │ └── feign/ │ │ │ └── DemoProviderFeignClient.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-03-sc-feign-demo06B-consumer/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx03/ │ │ │ └── feigndemo/ │ │ │ └── consumer/ │ │ │ ├── DemoConsumerApplication.java │ │ │ ├── controller/ │ │ │ │ └── ConsumerController.java │ │ │ └── feign/ │ │ │ └── DemoProviderFeignClient.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-03-sc-feign-demo07-consumer/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx03/ │ │ │ └── feigndemo/ │ │ │ └── consumer/ │ │ │ ├── DemoConsumerApplication.java │ │ │ ├── controller/ │ │ │ │ └── ConsumerController.java │ │ │ └── feign/ │ │ │ └── DemoProviderFeignClient.java │ │ └── resources/ │ │ └── application.yaml │ ├── pom.xml │ └── 《芋道 Spring Cloud 声明式调用 Feign 入门》.md ├── labx-04-spring-cloud-alibaba-sentinel/ │ ├── labx-04-sca-sentinel-actuator-provider/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx04/ │ │ │ └── sentineldemo/ │ │ │ └── provider/ │ │ │ ├── DemoProviderApplication.java │ │ │ ├── controller/ │ │ │ │ └── DemoController.java │ │ │ └── web/ │ │ │ ├── CustomBlockExceptionHandler.java │ │ │ ├── CustomRequestOriginParser.java │ │ │ └── GlobalExceptionHandler.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-04-sca-sentinel-apollo-provider/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx04/ │ │ │ └── sentineldemo/ │ │ │ └── provider/ │ │ │ ├── DemoProviderApplication.java │ │ │ ├── controller/ │ │ │ │ └── DemoController.java │ │ │ └── web/ │ │ │ ├── CustomBlockExceptionHandler.java │ │ │ ├── CustomRequestOriginParser.java │ │ │ └── GlobalExceptionHandler.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-04-sca-sentinel-demo01-provider/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx04/ │ │ │ └── sentineldemo/ │ │ │ └── provider/ │ │ │ ├── DemoProviderApplication.java │ │ │ ├── controller/ │ │ │ │ └── DemoController.java │ │ │ └── web/ │ │ │ ├── CustomBlockExceptionHandler.java │ │ │ ├── CustomRequestOriginParser.java │ │ │ └── GlobalExceptionHandler.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-04-sca-sentinel-feign-consumer/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx04/ │ │ │ └── sentineldemo/ │ │ │ └── provider/ │ │ │ ├── DemoConsumerApplication.java │ │ │ ├── controller/ │ │ │ │ └── ConsumerController.java │ │ │ ├── fallback/ │ │ │ │ ├── DemoProviderFeignClientFallback.java │ │ │ │ └── DemoProviderFeignClientFallbackFactory.java │ │ │ └── feign/ │ │ │ └── DemoProviderFeignClient.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-04-sca-sentinel-file-provider/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx04/ │ │ │ └── sentineldemo/ │ │ │ └── provider/ │ │ │ ├── DemoProviderApplication.java │ │ │ ├── controller/ │ │ │ │ └── DemoController.java │ │ │ └── web/ │ │ │ ├── CustomBlockExceptionHandler.java │ │ │ ├── CustomRequestOriginParser.java │ │ │ └── GlobalExceptionHandler.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-04-sca-sentinel-nacos-provider/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx04/ │ │ │ └── sentineldemo/ │ │ │ └── provider/ │ │ │ ├── DemoProviderApplication.java │ │ │ ├── controller/ │ │ │ │ └── DemoController.java │ │ │ └── web/ │ │ │ ├── CustomBlockExceptionHandler.java │ │ │ ├── CustomRequestOriginParser.java │ │ │ └── GlobalExceptionHandler.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-04-sca-sentinel-resttemplate-consumer/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx04/ │ │ │ └── sentineldemo/ │ │ │ └── provider/ │ │ │ ├── DemoConsumerApplication.java │ │ │ ├── config/ │ │ │ │ └── RestTemplateConfiguration.java │ │ │ └── controller/ │ │ │ └── ConsumerController.java │ │ └── resources/ │ │ └── application.yaml │ ├── pom.xml │ └── 《芋道 Spring Cloud Alibaba 服务容错 Sentinel 入门》.md ├── labx-05-spring-cloud-alibaba-nacos-config/ │ ├── labx-05-sca-nacos-config-auto-refresh/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx5/ │ │ │ └── nacosdemo/ │ │ │ ├── DemoApplication.java │ │ │ ├── config/ │ │ │ │ └── OrderProperties.java │ │ │ ├── controller/ │ │ │ │ └── DemoController.java │ │ │ └── listener/ │ │ │ └── DemoEnvironmentChangeListener.java │ │ └── resources/ │ │ └── bootstrap.yaml │ ├── labx-05-sca-nacos-config-demo/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx5/ │ │ │ └── nacosdemo/ │ │ │ ├── DemoApplication.java │ │ │ ├── config/ │ │ │ │ └── OrderProperties.java │ │ │ └── controller/ │ │ │ └── DemoController.java │ │ └── resources/ │ │ └── bootstrap.yaml │ ├── labx-05-sca-nacos-config-demo-actuator/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx5/ │ │ │ └── nacosdemo/ │ │ │ ├── DemoApplication.java │ │ │ ├── config/ │ │ │ │ └── OrderProperties.java │ │ │ └── controller/ │ │ │ └── DemoController.java │ │ └── resources/ │ │ ├── application.yaml │ │ └── bootstrap.yaml │ ├── labx-05-sca-nacos-config-demo-jasypt/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springcloudalibaba/ │ │ │ │ └── labx5/ │ │ │ │ └── nacosdemo/ │ │ │ │ ├── DemoApplication.java │ │ │ │ ├── controller/ │ │ │ │ │ └── DemoController.java │ │ │ │ └── listener/ │ │ │ │ └── JasyptEnvironmentChangeListener.java │ │ │ └── resources/ │ │ │ └── bootstrap.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springcloudalibaba/ │ │ └── labx5/ │ │ └── nacosdemo/ │ │ └── JasyptTest.java │ ├── labx-05-sca-nacos-config-demo-multi/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx5/ │ │ │ └── nacosdemo/ │ │ │ └── DemoApplication.java │ │ └── resources/ │ │ └── bootstrap.yaml │ ├── labx-05-sca-nacos-config-demo-profiles/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx5/ │ │ │ └── nacosdemo/ │ │ │ └── ProfilesApplication.java │ │ └── resources/ │ │ ├── bootstrap-dev.yaml │ │ ├── bootstrap-prod.yaml │ │ └── bootstrap.yaml │ ├── pom.xml │ └── 《芋道 Spring Cloud Alibaba 配置中心 Nacos 入门》.md ├── labx-06-spring-cloud-stream-rocketmq/ │ ├── labx-06-sca-stream-rocketmq-consumer-actuator/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx6/ │ │ │ └── rocketmqdemo/ │ │ │ └── consumerdemo/ │ │ │ ├── ConsumerApplication.java │ │ │ ├── listener/ │ │ │ │ ├── Demo01Consumer.java │ │ │ │ └── MySink.java │ │ │ └── message/ │ │ │ └── Demo01Message.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-06-sca-stream-rocketmq-consumer-aliyun/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx6/ │ │ │ └── rocketmqdemo/ │ │ │ └── consumerdemo/ │ │ │ ├── ConsumerApplication.java │ │ │ ├── listener/ │ │ │ │ ├── Demo01Consumer.java │ │ │ │ └── MySink.java │ │ │ └── message/ │ │ │ └── Demo01Message.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-06-sca-stream-rocketmq-consumer-broadcasting/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx6/ │ │ │ └── rocketmqdemo/ │ │ │ └── consumerdemo/ │ │ │ ├── ConsumerApplication.java │ │ │ ├── listener/ │ │ │ │ ├── Demo01Consumer.java │ │ │ │ └── MySink.java │ │ │ └── message/ │ │ │ └── Demo01Message.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-06-sca-stream-rocketmq-consumer-demo/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx6/ │ │ │ └── rocketmqdemo/ │ │ │ └── consumerdemo/ │ │ │ ├── ConsumerApplication.java │ │ │ ├── listener/ │ │ │ │ ├── Demo01Consumer.java │ │ │ │ └── MySink.java │ │ │ └── message/ │ │ │ └── Demo01Message.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-06-sca-stream-rocketmq-consumer-error-handler/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx6/ │ │ │ └── rocketmqdemo/ │ │ │ └── consumerdemo/ │ │ │ ├── ConsumerApplication.java │ │ │ ├── listener/ │ │ │ │ ├── Demo01Consumer.java │ │ │ │ └── MySink.java │ │ │ └── message/ │ │ │ └── Demo01Message.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-06-sca-stream-rocketmq-consumer-filter/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx6/ │ │ │ └── rocketmqdemo/ │ │ │ └── consumerdemo/ │ │ │ ├── ConsumerApplication.java │ │ │ ├── listener/ │ │ │ │ ├── Demo01Consumer.java │ │ │ │ └── MySink.java │ │ │ └── message/ │ │ │ └── Demo01Message.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-06-sca-stream-rocketmq-consumer-orderly/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx6/ │ │ │ └── rocketmqdemo/ │ │ │ └── consumerdemo/ │ │ │ ├── ConsumerApplication.java │ │ │ ├── listener/ │ │ │ │ ├── Demo01Consumer.java │ │ │ │ └── MySink.java │ │ │ └── message/ │ │ │ └── Demo01Message.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-06-sca-stream-rocketmq-consumer-retry/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx6/ │ │ │ └── rocketmqdemo/ │ │ │ └── consumerdemo/ │ │ │ ├── ConsumerApplication.java │ │ │ ├── listener/ │ │ │ │ ├── Demo01Consumer.java │ │ │ │ └── MySink.java │ │ │ └── message/ │ │ │ └── Demo01Message.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-06-sca-stream-rocketmq-producer-actuator/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx6/ │ │ │ └── rocketmqdemo/ │ │ │ └── producerdemo/ │ │ │ ├── ProducerApplication.java │ │ │ ├── controller/ │ │ │ │ └── Demo01Controller.java │ │ │ └── message/ │ │ │ ├── Demo01Message.java │ │ │ └── MySource.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-06-sca-stream-rocketmq-producer-aliyun/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx6/ │ │ │ └── rocketmqdemo/ │ │ │ └── producerdemo/ │ │ │ ├── ProducerApplication.java │ │ │ ├── controller/ │ │ │ │ └── Demo01Controller.java │ │ │ └── message/ │ │ │ ├── Demo01Message.java │ │ │ └── MySource.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-06-sca-stream-rocketmq-producer-demo/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx6/ │ │ │ └── rocketmqdemo/ │ │ │ └── producerdemo/ │ │ │ ├── ProducerApplication.java │ │ │ ├── controller/ │ │ │ │ └── Demo01Controller.java │ │ │ └── message/ │ │ │ ├── Demo01Message.java │ │ │ └── MySource.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-06-sca-stream-rocketmq-producer-orderly/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx6/ │ │ │ └── rocketmqdemo/ │ │ │ └── producerdemo/ │ │ │ ├── ProducerApplication.java │ │ │ ├── controller/ │ │ │ │ └── Demo01Controller.java │ │ │ └── message/ │ │ │ ├── Demo01Message.java │ │ │ └── MySource.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-06-sca-stream-rocketmq-producer-transaction/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx6/ │ │ │ └── rocketmqdemo/ │ │ │ └── producerdemo/ │ │ │ ├── ProducerApplication.java │ │ │ ├── controller/ │ │ │ │ └── Demo01Controller.java │ │ │ ├── listener/ │ │ │ │ └── TransactionListenerImpl.java │ │ │ └── message/ │ │ │ ├── Demo01Message.java │ │ │ └── MySource.java │ │ └── resources/ │ │ └── application.yml │ ├── pom.xml │ └── 《芋道 Spring Cloud Alibaba 消息队列 RocketMQ 入门》.md ├── labx-07-spring-cloud-alibaba-dubbo/ │ ├── labx-07-sca-dubbo-demo01/ │ │ ├── labx-07-sca-dubbo-demo01-api/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx7/ │ │ │ ├── api/ │ │ │ │ └── UserService.java │ │ │ └── dto/ │ │ │ ├── UserAddDTO.java │ │ │ └── UserDTO.java │ │ ├── labx-07-sca-dubbo-demo01-consumer/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springcloudalibaba/ │ │ │ │ └── labx7/ │ │ │ │ └── consumerdemo/ │ │ │ │ ├── ConsumerApplication.java │ │ │ │ └── controller/ │ │ │ │ └── UserController.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ ├── labx-07-sca-dubbo-demo01-provider/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springcloudalibaba/ │ │ │ │ └── labx7/ │ │ │ │ └── providerdemo/ │ │ │ │ ├── ProviderApplication.java │ │ │ │ └── service/ │ │ │ │ └── UserServiceImpl.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── pom.xml │ ├── labx-07-sca-dubbo-demo02/ │ │ ├── labx-07-sca-dubbo-demo02-api/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx7/ │ │ │ ├── api/ │ │ │ │ └── UserService.java │ │ │ └── dto/ │ │ │ ├── UserAddDTO.java │ │ │ └── UserDTO.java │ │ ├── labx-07-sca-dubbo-demo02-consumer/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springcloudalibaba/ │ │ │ │ └── labx7/ │ │ │ │ └── consumerdemo/ │ │ │ │ ├── FeignConsumerApplication.java │ │ │ │ ├── config/ │ │ │ │ │ └── RestTemplateConfig.java │ │ │ │ ├── controller/ │ │ │ │ │ ├── User01Controller.java │ │ │ │ │ ├── User02Controller.java │ │ │ │ │ ├── User03Controller.java │ │ │ │ │ └── User04Controller.java │ │ │ │ ├── dto/ │ │ │ │ │ ├── UserAddDTO.java │ │ │ │ │ └── UserDTO.java │ │ │ │ └── feign/ │ │ │ │ ├── UserFeignClient.java │ │ │ │ └── UserFeignClient02.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ ├── labx-07-sca-dubbo-demo02-provider-rest/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springcloudalibaba/ │ │ │ │ └── labx7/ │ │ │ │ └── providerdemo/ │ │ │ │ ├── RestProviderApplication.java │ │ │ │ └── service/ │ │ │ │ └── UserServiceImpl.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ ├── labx-07-sca-dubbo-demo02-provider-springmvc/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springcloudalibaba/ │ │ │ │ └── labx7/ │ │ │ │ └── providerdemo/ │ │ │ │ ├── SpringMVCProviderApplication.java │ │ │ │ └── service/ │ │ │ │ └── UserServiceImpl.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── pom.xml │ ├── labx-07-sca-dubbo-demo03-validation/ │ │ ├── labx-07-sca-dubbo-demo03-api/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx7/ │ │ │ ├── api/ │ │ │ │ └── UserService.java │ │ │ └── dto/ │ │ │ ├── UserAddDTO.java │ │ │ └── UserDTO.java │ │ ├── labx-07-sca-dubbo-demo03-consumer/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springcloudalibaba/ │ │ │ │ └── labx7/ │ │ │ │ └── consumerdemo/ │ │ │ │ ├── ConsumerApplication.java │ │ │ │ └── controller/ │ │ │ │ └── UserController.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ ├── labx-07-sca-dubbo-demo03-provider/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springcloudalibaba/ │ │ │ │ └── labx7/ │ │ │ │ └── providerdemo/ │ │ │ │ ├── ProviderApplication.java │ │ │ │ └── service/ │ │ │ │ └── UserServiceImpl.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── pom.xml │ ├── labx-07-sca-dubbo-demo04-filter/ │ │ ├── labx-07-sca-dubbo-demo04-api/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx7/ │ │ │ ├── api/ │ │ │ │ └── UserService.java │ │ │ ├── core/ │ │ │ │ ├── ServiceException.java │ │ │ │ └── ServiceExceptionEnum.java │ │ │ └── dto/ │ │ │ ├── UserAddDTO.java │ │ │ └── UserDTO.java │ │ ├── labx-07-sca-dubbo-demo04-consumer/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springcloudalibaba/ │ │ │ │ └── labx7/ │ │ │ │ └── consumerdemo/ │ │ │ │ ├── ConsumerApplication.java │ │ │ │ └── controller/ │ │ │ │ └── UserController.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ ├── labx-07-sca-dubbo-demo04-provider/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springcloudalibaba/ │ │ │ │ └── labx7/ │ │ │ │ └── providerdemo/ │ │ │ │ ├── ProviderApplication.java │ │ │ │ ├── filter/ │ │ │ │ │ └── DubboExceptionFilter.java │ │ │ │ └── service/ │ │ │ │ └── UserServiceImpl.java │ │ │ └── resources/ │ │ │ ├── META-INF/ │ │ │ │ └── dubbo/ │ │ │ │ └── org.apache.dubbo.rpc.Filter │ │ │ └── application.yaml │ │ └── pom.xml │ ├── labx-07-sca-dubbo-demo05-sentinel/ │ │ ├── labx-07-sca-dubbo-demo05-api/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx7/ │ │ │ ├── api/ │ │ │ │ └── UserService.java │ │ │ └── dto/ │ │ │ ├── UserAddDTO.java │ │ │ └── UserDTO.java │ │ ├── labx-07-sca-dubbo-demo05-consumer/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springcloudalibaba/ │ │ │ │ └── labx7/ │ │ │ │ └── consumerdemo/ │ │ │ │ ├── ConsumerApplication.java │ │ │ │ └── controller/ │ │ │ │ └── UserController.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ ├── labx-07-sca-dubbo-demo05-provider/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springcloudalibaba/ │ │ │ │ └── labx7/ │ │ │ │ └── providerdemo/ │ │ │ │ ├── ProviderApplication.java │ │ │ │ └── service/ │ │ │ │ └── UserServiceImpl.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── pom.xml │ ├── pom.xml │ └── 《芋道 Spring Cloud Alibaba 服务调用 Dubbo 入门》.md ├── labx-08-spring-cloud-gateway/ │ ├── labx-08-sc-gateway-demo01/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx08/ │ │ │ └── gatewaydemo/ │ │ │ └── GatewayApplication.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-08-sc-gateway-demo01-test/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx08/ │ │ │ └── gatewaydemo/ │ │ │ ├── GatewayApplication.java │ │ │ └── config/ │ │ │ └── GatewayConfig.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-08-sc-gateway-demo02-registry/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx08/ │ │ │ └── gatewaydemo/ │ │ │ └── GatewayApplication.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-08-sc-gateway-demo03-config-apollo/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx08/ │ │ │ └── gatewaydemo/ │ │ │ ├── GatewayApplication.java │ │ │ └── GatewayPropertiesRefresher.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-08-sc-gateway-demo03-config-nacos/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx08/ │ │ │ └── gatewaydemo/ │ │ │ └── GatewayApplication.java │ │ └── resources/ │ │ ├── application.yaml │ │ └── bootstrap.yaml │ ├── labx-08-sc-gateway-demo04/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx08/ │ │ │ └── gatewaydemo/ │ │ │ └── GatewayApplication.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-08-sc-gateway-demo05-custom-gateway-filter/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx08/ │ │ │ └── gatewaydemo/ │ │ │ ├── GatewayApplication.java │ │ │ └── filter/ │ │ │ └── AuthGatewayFilterFactory.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-08-sc-gateway-demo06-rate-limiter/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx08/ │ │ │ └── gatewaydemo/ │ │ │ ├── GatewayApplication.java │ │ │ └── config/ │ │ │ └── GatewayConfig.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-08-sc-gateway-demo07-hystrix/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx08/ │ │ │ └── gatewaydemo/ │ │ │ ├── GatewayApplication.java │ │ │ └── controller/ │ │ │ └── FallbackController.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-08-sc-gateway-demo07-sentinel/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx08/ │ │ │ └── gatewaydemo/ │ │ │ ├── CustomBlockRequestHandler.java │ │ │ └── GatewayApplication.java │ │ └── resources/ │ │ ├── application.yaml │ │ ├── sentinel-gw-api-group.json │ │ └── sentinel-gw-flow.json │ ├── labx-08-sc-gateway-demo08-custom-global-filter/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx08/ │ │ │ └── gatewaydemo/ │ │ │ ├── GatewayApplication.java │ │ │ ├── User03Controller.java │ │ │ ├── config/ │ │ │ │ └── RestTemplateConfig.java │ │ │ └── filter/ │ │ │ └── DubboFilter.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-08-sc-gateway-demo09-actuator/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx08/ │ │ │ └── gatewaydemo/ │ │ │ └── GatewayApplication.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-08-sc-gateway-demo10-troubleshooting/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx08/ │ │ │ └── gatewaydemo/ │ │ │ └── GatewayApplication.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-08-sc-user-service/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx08/ │ │ │ └── userservice/ │ │ │ ├── UserServiceApplication.java │ │ │ ├── controller/ │ │ │ │ └── UserController.java │ │ │ └── dto/ │ │ │ ├── UserAddDTO.java │ │ │ └── UserDTO.java │ │ └── resources/ │ │ └── application.yaml │ ├── pom.xml │ └── 《芋道 Spring Cloud 网关 Spring Cloud Gateway 入门》.md ├── labx-09-spring-cloud-apollo/ │ ├── labx-09-sc-apollo-demo/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx09/ │ │ │ └── apollodemo/ │ │ │ ├── DemoApplication.java │ │ │ ├── config/ │ │ │ │ └── OrderProperties.java │ │ │ └── controller/ │ │ │ └── DemoController.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-09-sc-apollo-demo-auto-refresh/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx09/ │ │ │ └── apollodemo/ │ │ │ ├── DemoApplication.java │ │ │ ├── config/ │ │ │ │ └── OrderProperties.java │ │ │ ├── controller/ │ │ │ │ └── DemoController.java │ │ │ └── listener/ │ │ │ └── ApolloPropertiesRefresher.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-09-sc-apollo-demo-jasypt/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springcloud/ │ │ │ │ └── labx09/ │ │ │ │ └── apollodemo/ │ │ │ │ ├── DemoApplication.java │ │ │ │ └── controller/ │ │ │ │ └── DemoController.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── test/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springcloud/ │ │ └── labx09/ │ │ └── apollodemo/ │ │ └── JasyptTest.java │ ├── labx-09-sc-apollo-demo-multi/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx09/ │ │ │ └── apollodemo/ │ │ │ └── DemoApplication.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-09-sc-apollo-demo-profiles/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx09/ │ │ │ └── apollodemo/ │ │ │ └── DemoApplication.java │ │ └── resources/ │ │ ├── application-dev.yaml │ │ ├── application-prod.yaml │ │ └── application.yaml │ ├── pom.xml │ └── 《芋道 Spring Cloud 配置中心 Apollo 入门》.md ├── labx-10-spring-cloud-stream-rabbitmq/ │ ├── labx-10-sc-stream-rabbitmq-consumer-ack/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx10/ │ │ │ └── rabbitmqdemo/ │ │ │ └── consumerdemo/ │ │ │ ├── ConsumerApplication.java │ │ │ ├── listener/ │ │ │ │ ├── Demo01Consumer.java │ │ │ │ └── MySink.java │ │ │ └── message/ │ │ │ └── Demo01Message.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-10-sc-stream-rabbitmq-consumer-actuator/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx10/ │ │ │ └── rabbitmqdemo/ │ │ │ └── consumerdemo/ │ │ │ ├── ConsumerApplication.java │ │ │ ├── listener/ │ │ │ │ ├── Demo01Consumer.java │ │ │ │ └── MySink.java │ │ │ └── message/ │ │ │ └── Demo01Message.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-10-sc-stream-rabbitmq-consumer-batch/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx10/ │ │ │ └── rabbitmqdemo/ │ │ │ └── consumerdemo/ │ │ │ ├── ConsumerApplication.java │ │ │ ├── listener/ │ │ │ │ ├── Demo01Consumer.java │ │ │ │ └── MySink.java │ │ │ └── message/ │ │ │ └── Demo01Message.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-10-sc-stream-rabbitmq-consumer-broadcasting/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx10/ │ │ │ └── rabbitmqdemo/ │ │ │ └── consumerdemo/ │ │ │ ├── ConsumerApplication.java │ │ │ ├── listener/ │ │ │ │ ├── Demo01Consumer.java │ │ │ │ └── MySink.java │ │ │ └── message/ │ │ │ └── Demo01Message.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-10-sc-stream-rabbitmq-consumer-concurrency/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx10/ │ │ │ └── rabbitmqdemo/ │ │ │ └── consumerdemo/ │ │ │ ├── ConsumerApplication.java │ │ │ ├── listener/ │ │ │ │ ├── Demo01Consumer.java │ │ │ │ └── MySink.java │ │ │ └── message/ │ │ │ └── Demo01Message.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-10-sc-stream-rabbitmq-consumer-delay/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx10/ │ │ │ └── rabbitmqdemo/ │ │ │ └── consumerdemo/ │ │ │ ├── ConsumerApplication.java │ │ │ ├── listener/ │ │ │ │ ├── Demo01Consumer.java │ │ │ │ └── MySink.java │ │ │ └── message/ │ │ │ └── Demo01Message.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-10-sc-stream-rabbitmq-consumer-demo/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx10/ │ │ │ └── rabbitmqdemo/ │ │ │ └── consumerdemo/ │ │ │ ├── ConsumerApplication.java │ │ │ ├── listener/ │ │ │ │ ├── Demo01Consumer.java │ │ │ │ └── MySink.java │ │ │ └── message/ │ │ │ └── Demo01Message.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-10-sc-stream-rabbitmq-consumer-error-handler/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx10/ │ │ │ └── rabbitmqdemo/ │ │ │ └── consumerdemo/ │ │ │ ├── ConsumerApplication.java │ │ │ ├── listener/ │ │ │ │ ├── Demo01Consumer.java │ │ │ │ └── MySink.java │ │ │ └── message/ │ │ │ └── Demo01Message.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-10-sc-stream-rabbitmq-consumer-filter/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx10/ │ │ │ └── rabbitmqdemo/ │ │ │ └── consumerdemo/ │ │ │ ├── ConsumerApplication.java │ │ │ ├── listener/ │ │ │ │ ├── Demo01Consumer.java │ │ │ │ └── MySink.java │ │ │ └── message/ │ │ │ └── Demo01Message.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-10-sc-stream-rabbitmq-consumer-partitioning/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx10/ │ │ │ └── rabbitmqdemo/ │ │ │ └── consumerdemo/ │ │ │ ├── ConsumerApplication.java │ │ │ ├── listener/ │ │ │ │ ├── Demo01Consumer.java │ │ │ │ └── MySink.java │ │ │ └── message/ │ │ │ └── Demo01Message.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-10-sc-stream-rabbitmq-consumer-retry/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx10/ │ │ │ └── rabbitmqdemo/ │ │ │ └── consumerdemo/ │ │ │ ├── ConsumerApplication.java │ │ │ ├── listener/ │ │ │ │ ├── Demo01Consumer.java │ │ │ │ └── MySink.java │ │ │ └── message/ │ │ │ └── Demo01Message.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-10-sc-stream-rabbitmq-producer-actuator/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx10/ │ │ │ └── rabbitmqdemo/ │ │ │ └── producerdemo/ │ │ │ ├── ProducerApplication.java │ │ │ ├── controller/ │ │ │ │ └── Demo01Controller.java │ │ │ └── message/ │ │ │ ├── Demo01Message.java │ │ │ └── MySource.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-10-sc-stream-rabbitmq-producer-batch/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx10/ │ │ │ └── rabbitmqdemo/ │ │ │ └── producerdemo/ │ │ │ ├── ProducerApplication.java │ │ │ ├── controller/ │ │ │ │ └── Demo01Controller.java │ │ │ └── message/ │ │ │ ├── Demo01Message.java │ │ │ └── MySource.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-10-sc-stream-rabbitmq-producer-confirm/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx10/ │ │ │ └── rabbitmqdemo/ │ │ │ └── producerdemo/ │ │ │ ├── ProducerApplication.java │ │ │ ├── controller/ │ │ │ │ └── Demo01Controller.java │ │ │ └── message/ │ │ │ ├── Demo01Message.java │ │ │ ├── Demo01ProducerConfirmCallback.java │ │ │ ├── Demo01ProducerReturnCallback.java │ │ │ └── MySource.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-10-sc-stream-rabbitmq-producer-delay/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx10/ │ │ │ └── rabbitmqdemo/ │ │ │ └── producerdemo/ │ │ │ ├── ProducerApplication.java │ │ │ ├── controller/ │ │ │ │ └── Demo01Controller.java │ │ │ └── message/ │ │ │ ├── Demo01Message.java │ │ │ └── MySource.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-10-sc-stream-rabbitmq-producer-demo/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx10/ │ │ │ └── rabbitmqdemo/ │ │ │ └── producerdemo/ │ │ │ ├── ProducerApplication.java │ │ │ ├── controller/ │ │ │ │ └── Demo01Controller.java │ │ │ └── message/ │ │ │ ├── Demo01Message.java │ │ │ └── MySource.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-10-sc-stream-rabbitmq-producer-partitioning/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx10/ │ │ │ └── rabbitmqdemo/ │ │ │ └── producerdemo/ │ │ │ ├── ProducerApplication.java │ │ │ ├── controller/ │ │ │ │ └── Demo01Controller.java │ │ │ └── message/ │ │ │ ├── Demo01Message.java │ │ │ └── MySource.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-10-sc-stream-rabbitmq-producer-transaction/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx10/ │ │ │ └── rabbitmqdemo/ │ │ │ └── producerdemo/ │ │ │ ├── ProducerApplication.java │ │ │ ├── config/ │ │ │ │ └── TransactionConfig.java │ │ │ ├── controller/ │ │ │ │ └── Demo01Controller.java │ │ │ └── message/ │ │ │ ├── Demo01Message.java │ │ │ └── MySource.java │ │ └── resources/ │ │ └── application.yml │ ├── pom.xml │ └── 《芋道 Spring Cloud 消息队列 RabbitMQ 入门》.md ├── labx-11-spring-cloud-stream-kafka/ │ ├── labx-11-sc-stream-kafka-consumer-ack/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx11/ │ │ │ └── kafkademo/ │ │ │ └── consumerdemo/ │ │ │ ├── ConsumerApplication.java │ │ │ ├── listener/ │ │ │ │ ├── Demo01Consumer.java │ │ │ │ └── MySink.java │ │ │ └── message/ │ │ │ └── Demo01Message.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-11-sc-stream-kafka-consumer-actuator/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx11/ │ │ │ └── kafkademo/ │ │ │ └── consumerdemo/ │ │ │ ├── ConsumerApplication.java │ │ │ ├── listener/ │ │ │ │ ├── Demo01Consumer.java │ │ │ │ └── MySink.java │ │ │ └── message/ │ │ │ └── Demo01Message.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-11-sc-stream-kafka-consumer-batch/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx11/ │ │ │ └── kafkademo/ │ │ │ └── consumerdemo/ │ │ │ ├── ConsumerApplication.java │ │ │ ├── listener/ │ │ │ │ ├── Demo01Consumer.java │ │ │ │ └── MySink.java │ │ │ └── message/ │ │ │ └── Demo01Message.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-11-sc-stream-kafka-consumer-broadcasting/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx11/ │ │ │ └── kafkademo/ │ │ │ └── consumerdemo/ │ │ │ ├── ConsumerApplication.java │ │ │ ├── listener/ │ │ │ │ ├── Demo01Consumer.java │ │ │ │ └── MySink.java │ │ │ └── message/ │ │ │ └── Demo01Message.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-11-sc-stream-kafka-consumer-concurrency/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx11/ │ │ │ └── kafkademo/ │ │ │ └── consumerdemo/ │ │ │ ├── ConsumerApplication.java │ │ │ ├── listener/ │ │ │ │ ├── Demo01Consumer.java │ │ │ │ └── MySink.java │ │ │ └── message/ │ │ │ └── Demo01Message.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-11-sc-stream-kafka-consumer-demo/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx11/ │ │ │ └── kafkademo/ │ │ │ └── consumerdemo/ │ │ │ ├── ConsumerApplication.java │ │ │ ├── listener/ │ │ │ │ ├── Demo01Consumer.java │ │ │ │ └── MySink.java │ │ │ └── message/ │ │ │ └── Demo01Message.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-11-sc-stream-kafka-consumer-error-handler/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx11/ │ │ │ └── kafkademo/ │ │ │ └── consumerdemo/ │ │ │ ├── ConsumerApplication.java │ │ │ ├── listener/ │ │ │ │ ├── Demo01Consumer.java │ │ │ │ └── MySink.java │ │ │ └── message/ │ │ │ └── Demo01Message.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-11-sc-stream-kafka-consumer-filter/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx11/ │ │ │ └── kafkademo/ │ │ │ └── consumerdemo/ │ │ │ ├── ConsumerApplication.java │ │ │ ├── listener/ │ │ │ │ ├── Demo01Consumer.java │ │ │ │ └── MySink.java │ │ │ └── message/ │ │ │ └── Demo01Message.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-11-sc-stream-kafka-consumer-partitioning/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx11/ │ │ │ └── kafkademo/ │ │ │ └── consumerdemo/ │ │ │ ├── ConsumerApplication.java │ │ │ ├── listener/ │ │ │ │ ├── Demo01Consumer.java │ │ │ │ └── MySink.java │ │ │ └── message/ │ │ │ └── Demo01Message.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-11-sc-stream-kafka-consumer-retry/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx11/ │ │ │ └── kafkademo/ │ │ │ └── consumerdemo/ │ │ │ ├── ConsumerApplication.java │ │ │ ├── listener/ │ │ │ │ ├── Demo01Consumer.java │ │ │ │ └── MySink.java │ │ │ └── message/ │ │ │ └── Demo01Message.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-11-sc-stream-kafka-consumer-transaction/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx11/ │ │ │ └── kafkademo/ │ │ │ └── consumerdemo/ │ │ │ ├── ConsumerApplication.java │ │ │ ├── listener/ │ │ │ │ ├── Demo01Consumer.java │ │ │ │ └── MySink.java │ │ │ └── message/ │ │ │ └── Demo01Message.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-11-sc-stream-kafka-producer-actuator/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx11/ │ │ │ └── kafkademo/ │ │ │ └── kafkademo/ │ │ │ ├── ProducerApplication.java │ │ │ ├── controller/ │ │ │ │ └── Demo01Controller.java │ │ │ └── message/ │ │ │ ├── Demo01Message.java │ │ │ └── MySource.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-11-sc-stream-kafka-producer-batch/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx11/ │ │ │ └── kafkademo/ │ │ │ └── kafkademo/ │ │ │ ├── ProducerApplication.java │ │ │ ├── controller/ │ │ │ │ └── Demo01Controller.java │ │ │ └── message/ │ │ │ ├── Demo01Message.java │ │ │ └── MySource.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-11-sc-stream-kafka-producer-demo/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx11/ │ │ │ └── kafkademo/ │ │ │ └── kafkademo/ │ │ │ ├── ProducerApplication.java │ │ │ ├── controller/ │ │ │ │ └── Demo01Controller.java │ │ │ └── message/ │ │ │ ├── Demo01Message.java │ │ │ └── MySource.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-11-sc-stream-kafka-producer-partitioning/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx11/ │ │ │ └── kafkademo/ │ │ │ └── kafkademo/ │ │ │ ├── ProducerApplication.java │ │ │ ├── controller/ │ │ │ │ └── Demo01Controller.java │ │ │ └── message/ │ │ │ ├── Demo01Message.java │ │ │ └── MySource.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-11-sc-stream-kafka-producer-transaction/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx11/ │ │ │ └── kafkademo/ │ │ │ └── kafkademo/ │ │ │ ├── ProducerApplication.java │ │ │ ├── config/ │ │ │ │ └── TransactionConfig.java │ │ │ ├── controller/ │ │ │ │ └── Demo01Controller.java │ │ │ └── message/ │ │ │ ├── Demo01Message.java │ │ │ └── MySource.java │ │ └── resources/ │ │ └── application.yml │ ├── pom.xml │ └── 《芋道 Spring Cloud 消息队列 Kafka 入门》.md ├── labx-12-spring-cloud-config/ │ ├── labx-12-sc-config-server-demo/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx12/ │ │ │ └── configserverdemo/ │ │ │ └── ConfigServerApplication.java │ │ └── resources/ │ │ ├── application.yml │ │ └── shared/ │ │ └── user-application.yml │ ├── labx-12-sc-config-server-git/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx12/ │ │ │ └── configserverdemo/ │ │ │ └── ConfigServerApplication.java │ │ └── resources/ │ │ ├── application.yml │ │ ├── bootstrap.yml │ │ └── configserver.jks │ ├── labx-12-sc-config-server-git-auto-refresh-by-bus/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx12/ │ │ │ └── configserverdemo/ │ │ │ └── ConfigServerApplication.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-12-sc-config-server-git-nacos/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx12/ │ │ │ └── configserverdemo/ │ │ │ └── ConfigServerApplication.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-12-sc-config-user-application/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx12/ │ │ │ └── userapplication/ │ │ │ ├── UserApplication.java │ │ │ ├── config/ │ │ │ │ └── OrderProperties.java │ │ │ └── controller/ │ │ │ └── DemoController.java │ │ └── resources/ │ │ └── bootstrap.yml │ ├── labx-12-sc-config-user-application-auto-refresh-by-actuator/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx12/ │ │ │ └── userapplication/ │ │ │ ├── UserApplication.java │ │ │ ├── config/ │ │ │ │ └── OrderProperties.java │ │ │ ├── controller/ │ │ │ │ └── DemoController.java │ │ │ └── listener/ │ │ │ └── DemoEnvironmentChangeListener.java │ │ └── resources/ │ │ ├── application.yml │ │ └── bootstrap.yml │ ├── labx-12-sc-config-user-application-auto-refresh-by-bus/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx12/ │ │ │ └── userapplication/ │ │ │ ├── UserApplication.java │ │ │ ├── config/ │ │ │ │ └── OrderProperties.java │ │ │ └── controller/ │ │ │ └── DemoController.java │ │ └── resources/ │ │ ├── application.yml │ │ └── bootstrap.yml │ ├── labx-12-sc-config-user-application-nacos/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx12/ │ │ │ └── userapplication/ │ │ │ ├── UserApplication.java │ │ │ ├── config/ │ │ │ │ └── OrderProperties.java │ │ │ └── controller/ │ │ │ └── DemoController.java │ │ └── resources/ │ │ └── bootstrap.yml │ ├── pom.xml │ └── 《芋道 Spring Cloud 配置中心 Spring Cloud Config 入门》.md ├── labx-13/ │ ├── labx-13-sc-sleuth-db-elasticsearch/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx13/ │ │ │ └── springmvcdemo/ │ │ │ ├── UserServiceApplication.java │ │ │ ├── config/ │ │ │ │ └── SleuthConfiguration.java │ │ │ ├── controller/ │ │ │ │ └── UserController.java │ │ │ ├── dataobject/ │ │ │ │ └── ESUserDO.java │ │ │ ├── repository/ │ │ │ │ └── ESUserRepository.java │ │ │ └── spring/ │ │ │ ├── ClusterNodes.java │ │ │ └── TracingTransportClientFactoryBean.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-13-sc-sleuth-db-mongodb/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx13/ │ │ │ └── springmvcdemo/ │ │ │ ├── UserServiceApplication.java │ │ │ ├── config/ │ │ │ │ └── SleuthConfiguration.java │ │ │ ├── controller/ │ │ │ │ └── UserController.java │ │ │ └── dataobject/ │ │ │ └── UserDO.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-13-sc-sleuth-db-mysql/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx13/ │ │ │ └── springmvcdemo/ │ │ │ ├── UserServiceApplication.java │ │ │ └── controller/ │ │ │ └── UserController.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-13-sc-sleuth-db-redis/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx13/ │ │ │ └── springmvcdemo/ │ │ │ ├── UserServiceApplication.java │ │ │ ├── config/ │ │ │ │ └── SleuthConfiguration.java │ │ │ └── controller/ │ │ │ └── UserController.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-13-sc-sleuth-dubbo/ │ │ ├── labx-13-sc-sleuth-dubbo-api/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx13/ │ │ │ ├── api/ │ │ │ │ └── UserService.java │ │ │ └── package-info.java │ │ ├── labx-13-sc-sleuth-dubbo-consumer/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springcloud/ │ │ │ │ └── labx13/ │ │ │ │ └── consumerdemo/ │ │ │ │ ├── ConsumerApplication.java │ │ │ │ └── controller/ │ │ │ │ └── UserController.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ ├── labx-13-sc-sleuth-dubbo-provider/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springcloud/ │ │ │ │ └── labx13/ │ │ │ │ └── providerdemo/ │ │ │ │ ├── ProviderApplication.java │ │ │ │ └── service/ │ │ │ │ └── UserServiceImpl.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── pom.xml │ ├── labx-13-sc-sleuth-feign/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx13/ │ │ │ └── springmvcdemo/ │ │ │ ├── FeignApplication.java │ │ │ ├── controller/ │ │ │ │ └── FeignController.java │ │ │ └── feign/ │ │ │ └── UserServiceFeignClient.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-13-sc-sleuth-logback/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx13/ │ │ │ └── springmvcdemo/ │ │ │ ├── UserServiceApplication.java │ │ │ └── controller/ │ │ │ └── UserController.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-13-sc-sleuth-mq-activemq/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springboot/ │ │ │ └── labx13/ │ │ │ └── activemqdemo/ │ │ │ ├── ActiveMQApplication.java │ │ │ ├── consumer/ │ │ │ │ └── DemoConsumer.java │ │ │ ├── controller/ │ │ │ │ └── DemoController.java │ │ │ ├── message/ │ │ │ │ └── DemoMessage.java │ │ │ └── producer/ │ │ │ └── DemoProducer.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-13-sc-sleuth-mq-kafka/ │ │ ├── labx-13-sc-sleuth-mq-kafka-producer/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springcloud/ │ │ │ │ └── labx13/ │ │ │ │ └── kafkademo/ │ │ │ │ └── producerdemo/ │ │ │ │ ├── ProducerApplication.java │ │ │ │ ├── controller/ │ │ │ │ │ └── Demo01Controller.java │ │ │ │ └── message/ │ │ │ │ ├── Demo01Message.java │ │ │ │ └── MySource.java │ │ │ └── resources/ │ │ │ └── application.yml │ │ ├── labx-13-sc-stream-mq-kafka-consumer/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springcloud/ │ │ │ │ └── labx13/ │ │ │ │ └── kafkademo/ │ │ │ │ └── consumerdemo/ │ │ │ │ ├── ConsumerApplication.java │ │ │ │ ├── listener/ │ │ │ │ │ ├── Demo01Consumer.java │ │ │ │ │ └── MySink.java │ │ │ │ └── message/ │ │ │ │ └── Demo01Message.java │ │ │ └── resources/ │ │ │ └── application.yml │ │ └── pom.xml │ ├── labx-13-sc-sleuth-mq-rabbitmq/ │ │ ├── labx-13-sc-sleuth-mq-rabbitmq-producer/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springcloud/ │ │ │ │ └── labx13/ │ │ │ │ └── rabbitmqdemo/ │ │ │ │ └── producerdemo/ │ │ │ │ ├── ProducerApplication.java │ │ │ │ ├── controller/ │ │ │ │ │ └── Demo01Controller.java │ │ │ │ └── message/ │ │ │ │ ├── Demo01Message.java │ │ │ │ └── MySource.java │ │ │ └── resources/ │ │ │ └── application.yml │ │ ├── labx-13-sc-stream-mq-rabbitmq-consumer/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springcloud/ │ │ │ │ └── labx13/ │ │ │ │ └── rabbitmqdemo/ │ │ │ │ └── consumerdemo/ │ │ │ │ ├── ConsumerApplication.java │ │ │ │ ├── listener/ │ │ │ │ │ ├── Demo01Consumer.java │ │ │ │ │ └── MySink.java │ │ │ │ └── message/ │ │ │ │ └── Demo01Message.java │ │ │ └── resources/ │ │ │ └── application.yml │ │ └── pom.xml │ ├── labx-13-sc-sleuth-opentracing/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx13/ │ │ │ └── springmvcdemo/ │ │ │ ├── UserServiceApplication.java │ │ │ └── controller/ │ │ │ └── UserController.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-13-sc-sleuth-sampler/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx13/ │ │ │ └── springmvcdemo/ │ │ │ ├── UserServiceApplication.java │ │ │ └── controller/ │ │ │ └── UserController.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-13-sc-sleuth-springcloudgateway/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx13/ │ │ │ └── gatewaydemo/ │ │ │ └── GatewayApplication.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-13-sc-sleuth-springmvc/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx13/ │ │ │ └── springmvcdemo/ │ │ │ ├── UserServiceApplication.java │ │ │ └── controller/ │ │ │ └── UserController.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-13-sc-sleuth-zipkin-server-demo-in-memory/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx13/ │ │ │ └── sleuthzipkinserverdemo/ │ │ │ └── SleuthZipkinServerApplication.java │ │ └── resources/ │ │ └── application.yml │ ├── pom.xml │ └── 《芋道 Spring Cloud 链路追踪 Spring Cloud Sleuth》.md ├── labx-14/ │ ├── labx-14-sc-skywalking-dubbo/ │ │ ├── labx-14-sc-skywalking-dubbo-api/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx14/ │ │ │ ├── api/ │ │ │ │ └── UserService.java │ │ │ └── package-info.java │ │ ├── labx-14-sc-skywalking-dubbo-consumer/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springcloud/ │ │ │ │ └── labx14/ │ │ │ │ └── consumerdemo/ │ │ │ │ ├── ConsumerApplication.java │ │ │ │ └── controller/ │ │ │ │ └── UserController.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ ├── labx-14-sc-skywalking-dubbo-provider/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springcloud/ │ │ │ │ └── labx14/ │ │ │ │ └── providerdemo/ │ │ │ │ ├── ProviderApplication.java │ │ │ │ └── service/ │ │ │ │ └── UserServiceImpl.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ └── pom.xml │ ├── labx-14-sc-skywalking-feign/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx14/ │ │ │ └── springmvcdemo/ │ │ │ ├── FeignApplication.java │ │ │ ├── controller/ │ │ │ │ └── FeignController.java │ │ │ └── feign/ │ │ │ └── UserServiceFeignClient.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-14-sc-skywalking-mq-kafka/ │ │ ├── labx-14-sc-skywalking-mq-kafka-consumer/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springcloud/ │ │ │ │ └── labx14/ │ │ │ │ └── kafkademo/ │ │ │ │ └── consumerdemo/ │ │ │ │ ├── ConsumerApplication.java │ │ │ │ ├── listener/ │ │ │ │ │ ├── Demo01Consumer.java │ │ │ │ │ └── MySink.java │ │ │ │ └── message/ │ │ │ │ └── Demo01Message.java │ │ │ └── resources/ │ │ │ └── application.yml │ │ ├── labx-14-sc-skywalking-mq-kafka-producer/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springcloud/ │ │ │ │ └── labx14/ │ │ │ │ └── kafkademo/ │ │ │ │ └── producerdemo/ │ │ │ │ ├── ProducerApplication.java │ │ │ │ ├── controller/ │ │ │ │ │ └── Demo01Controller.java │ │ │ │ └── message/ │ │ │ │ ├── Demo01Message.java │ │ │ │ └── MySource.java │ │ │ └── resources/ │ │ │ └── application.yml │ │ └── pom.xml │ ├── labx-14-sc-skywalking-mq-rabbitmq/ │ │ ├── labx-14-sc-skywalking-mq-rabbitmq-consumer/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springcloud/ │ │ │ │ └── labx14/ │ │ │ │ └── rabbitmqdemo/ │ │ │ │ └── consumerdemo/ │ │ │ │ ├── ConsumerApplication.java │ │ │ │ ├── listener/ │ │ │ │ │ ├── Demo01Consumer.java │ │ │ │ │ └── MySink.java │ │ │ │ └── message/ │ │ │ │ └── Demo01Message.java │ │ │ └── resources/ │ │ │ └── application.yml │ │ ├── labx-14-sc-skywalking-mq-rabbitmq-producer/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springcloud/ │ │ │ │ └── labx14/ │ │ │ │ └── rabbitmqdemo/ │ │ │ │ └── producerdemo/ │ │ │ │ ├── ProducerApplication.java │ │ │ │ ├── controller/ │ │ │ │ │ └── Demo01Controller.java │ │ │ │ └── message/ │ │ │ │ ├── Demo01Message.java │ │ │ │ └── MySource.java │ │ │ └── resources/ │ │ │ └── application.yml │ │ └── pom.xml │ ├── labx-14-sc-skywalking-mq-rocketmq/ │ │ ├── labx-14-sc-skywalking-mq-rocketmq-consumer/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springcloud/ │ │ │ │ └── labx14/ │ │ │ │ └── rocketmqdemo/ │ │ │ │ └── consumerdemo/ │ │ │ │ ├── ConsumerApplication.java │ │ │ │ ├── listener/ │ │ │ │ │ ├── Demo01Consumer.java │ │ │ │ │ └── MySink.java │ │ │ │ └── message/ │ │ │ │ └── Demo01Message.java │ │ │ └── resources/ │ │ │ └── application.yml │ │ ├── labx-14-sc-skywalking-mq-rocketmq-producer/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springcloud/ │ │ │ │ └── labx14/ │ │ │ │ └── rocketmqdemo/ │ │ │ │ └── producerdemo/ │ │ │ │ ├── ProducerApplication.java │ │ │ │ ├── controller/ │ │ │ │ │ └── Demo01Controller.java │ │ │ │ └── message/ │ │ │ │ ├── Demo01Message.java │ │ │ │ └── MySource.java │ │ │ └── resources/ │ │ │ └── application.yml │ │ └── pom.xml │ ├── labx-14-sc-skywalking-springcloudgateway/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx14/ │ │ │ └── gatewaydemo/ │ │ │ └── GatewayApplication.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-14-sc-skywalking-springmvc/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx14/ │ │ │ └── springmvcdemo/ │ │ │ ├── UserServiceApplication.java │ │ │ └── controller/ │ │ │ └── UserController.java │ │ └── resources/ │ │ └── application.yml │ ├── pom.xml │ └── 《芋道 Spring Cloud 链路追踪 SkyWalking》.md ├── labx-15/ │ ├── labx-15-admin-02-adminserver/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx15/ │ │ │ └── adminserver/ │ │ │ └── AdminServerApplication.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-15-admin-02-demo-application/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx15/ │ │ │ └── demo/ │ │ │ ├── Demo01Application.java │ │ │ ├── Demo02Application.java │ │ │ └── Demo03Application.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-15-admin-03-adminserver/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx15/ │ │ │ └── adminserver/ │ │ │ ├── AdminServerApplication.java │ │ │ └── config/ │ │ │ └── SecurityConfig.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-15-admin-03-demo-application/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx15/ │ │ │ └── demo/ │ │ │ ├── Demo01Application.java │ │ │ └── Demo02Application.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-15-admin-04-adminserver-custom/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx15/ │ │ │ └── adminserver/ │ │ │ ├── AdminServerApplication.java │ │ │ └── notify/ │ │ │ └── LoggerNotifier.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-15-admin-04-adminserver-mail/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx15/ │ │ │ └── adminserver/ │ │ │ └── AdminServerApplication.java │ │ └── resources/ │ │ └── application.yaml │ ├── pom.xml │ └── 《芋道 Spring Cloud 监控工具 Admin 入门》.md ├── labx-16/ │ ├── deploy.sh │ ├── labx-16-demo-01/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── lab16/ │ │ │ └── jenkinsdemo/ │ │ │ ├── Application.java │ │ │ └── controller/ │ │ │ └── DemoController.java │ │ └── resources/ │ │ ├── application-dev.yaml │ │ ├── application-local.yaml │ │ ├── application-pre.yaml │ │ ├── application-prod.yaml │ │ └── application-uat.yaml │ ├── pom.xml │ └── 《芋道 Spring Cloud 持续交付 Jenkins 入门》.md ├── labx-17/ │ ├── labx-17-sc-seata-at-feign-demo/ │ │ ├── data.sql │ │ ├── labx-17-sc-seata-at-feign-demo-account-service/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springcloud/ │ │ │ │ └── labx17/ │ │ │ │ └── accountservice/ │ │ │ │ ├── AccountServiceApplication.java │ │ │ │ ├── controller/ │ │ │ │ │ └── AccountController.java │ │ │ │ ├── dao/ │ │ │ │ │ └── AccountDao.java │ │ │ │ ├── dto/ │ │ │ │ │ └── AccountReduceBalanceDTO.java │ │ │ │ └── service/ │ │ │ │ ├── AccountService.java │ │ │ │ └── impl/ │ │ │ │ └── AccountServiceImpl.java │ │ │ └── resources/ │ │ │ ├── application-file.yaml │ │ │ └── application.yaml │ │ ├── labx-17-sc-seata-at-feign-demo-order-service/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springcloud/ │ │ │ │ └── labx17/ │ │ │ │ └── orderservice/ │ │ │ │ ├── OrderServiceApplication.java │ │ │ │ ├── controller/ │ │ │ │ │ └── OrderController.java │ │ │ │ ├── dao/ │ │ │ │ │ └── OrderDao.java │ │ │ │ ├── entity/ │ │ │ │ │ └── OrderDO.java │ │ │ │ ├── feign/ │ │ │ │ │ ├── AccountServiceFeignClient.java │ │ │ │ │ ├── ProductServiceFeignClient.java │ │ │ │ │ └── dto/ │ │ │ │ │ ├── AccountReduceBalanceDTO.java │ │ │ │ │ └── ProductReduceStockDTO.java │ │ │ │ └── service/ │ │ │ │ ├── OrderService.java │ │ │ │ └── impl/ │ │ │ │ └── OrderServiceImpl.java │ │ │ └── resources/ │ │ │ ├── application-file.yaml │ │ │ └── application.yaml │ │ ├── labx-17-sc-seata-at-feign-demo-product-service/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springcloud/ │ │ │ │ └── labx17/ │ │ │ │ └── productservice/ │ │ │ │ ├── ProductServiceApplication.java │ │ │ │ ├── controller/ │ │ │ │ │ └── ProductController.java │ │ │ │ ├── dao/ │ │ │ │ │ └── ProductDao.java │ │ │ │ ├── dto/ │ │ │ │ │ └── ProductReduceStockDTO.java │ │ │ │ └── service/ │ │ │ │ ├── ProductService.java │ │ │ │ └── impl/ │ │ │ │ └── ProductServiceImpl.java │ │ │ └── resources/ │ │ │ ├── application-file.yaml │ │ │ └── application.yaml │ │ └── pom.xml │ ├── labx-17-sca-seata-at-dubbo-demo/ │ │ ├── data.sql │ │ ├── labx-17-sca-seata-at-dubbo-demo-account-service/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springcloudalibaba/ │ │ │ │ └── labx17/ │ │ │ │ └── accountservice/ │ │ │ │ ├── AccountServiceApplication.java │ │ │ │ ├── dao/ │ │ │ │ │ └── AccountDao.java │ │ │ │ └── service/ │ │ │ │ └── AccountServiceImpl.java │ │ │ └── resources/ │ │ │ ├── application-file.yaml │ │ │ └── application.yaml │ │ ├── labx-17-sca-seata-at-dubbo-demo-account-service-api/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx17/ │ │ │ └── accountservice/ │ │ │ └── api/ │ │ │ └── AccountService.java │ │ ├── labx-17-sca-seata-at-dubbo-demo-order-service/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springcloudalibaba/ │ │ │ │ └── labx17/ │ │ │ │ └── orderservice/ │ │ │ │ ├── OrderServiceApplication.java │ │ │ │ ├── controller/ │ │ │ │ │ └── OrderController.java │ │ │ │ ├── dao/ │ │ │ │ │ └── OrderDao.java │ │ │ │ ├── entity/ │ │ │ │ │ └── OrderDO.java │ │ │ │ └── service/ │ │ │ │ └── OrderServiceImpl.java │ │ │ └── resources/ │ │ │ ├── application-file.yaml │ │ │ └── application.yaml │ │ ├── labx-17-sca-seata-at-dubbo-demo-order-service-api/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx17/ │ │ │ └── orderservice/ │ │ │ └── api/ │ │ │ └── OrderService.java │ │ ├── labx-17-sca-seata-at-dubbo-demo-product-service/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springcloudalibaba/ │ │ │ │ └── labx17/ │ │ │ │ └── productservice/ │ │ │ │ ├── ProductServiceApplication.java │ │ │ │ ├── dao/ │ │ │ │ │ └── ProductDao.java │ │ │ │ └── service/ │ │ │ │ └── ProductServiceImpl.java │ │ │ └── resources/ │ │ │ ├── application-file.yaml │ │ │ └── application.yaml │ │ ├── labx-17-sca-seata-at-dubbo-demo-product-service-api/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx17/ │ │ │ └── producctservice/ │ │ │ └── api/ │ │ │ └── ProductService.java │ │ └── pom.xml │ ├── pom.xml │ └── 《芋道 Spring Cloud Alibaba 分布式事务 Seata 入门》.md ├── labx-18/ │ ├── labx-18-sc-bus-rabbitmq-demo-listener/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx18/ │ │ │ └── listenerdemo/ │ │ │ ├── ListenerDemoApplication.java │ │ │ ├── event/ │ │ │ │ └── UserRegisterEvent.java │ │ │ └── listener/ │ │ │ └── UserRegisterListener.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-18-sc-bus-rabbitmq-demo-listener-actuator/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx18/ │ │ │ └── listenerdemo/ │ │ │ ├── ListenerDemoApplication.java │ │ │ ├── event/ │ │ │ │ └── UserRegisterEvent.java │ │ │ └── listener/ │ │ │ └── UserRegisterListener.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-18-sc-bus-rabbitmq-demo-publisher/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx18/ │ │ │ └── publisherdemo/ │ │ │ ├── PublisherDemoApplication.java │ │ │ ├── controller/ │ │ │ │ └── DemoController.java │ │ │ └── event/ │ │ │ └── UserRegisterEvent.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-18-sc-bus-rabbitmq-demo-publisher-actuator/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx18/ │ │ │ └── publisherdemo/ │ │ │ ├── PublisherDemoApplication.java │ │ │ ├── controller/ │ │ │ │ └── DemoController.java │ │ │ └── event/ │ │ │ └── UserRegisterEvent.java │ │ └── resources/ │ │ └── application.yml │ ├── pom.xml │ └── 《芋道 Spring Cloud 事件总线 RabbitMQ 入门》.md ├── labx-19/ │ ├── labx-19-sc-bus-kafka-demo-listener/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx19/ │ │ │ └── listenerdemo/ │ │ │ ├── ListenerDemoApplication.java │ │ │ ├── event/ │ │ │ │ └── UserRegisterEvent.java │ │ │ └── listener/ │ │ │ └── UserRegisterListener.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-19-sc-bus-kafka-demo-listener-actuator/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx19/ │ │ │ └── listenerdemo/ │ │ │ ├── ListenerDemoApplication.java │ │ │ ├── event/ │ │ │ │ └── UserRegisterEvent.java │ │ │ └── listener/ │ │ │ └── UserRegisterListener.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-19-sc-bus-kafka-demo-publisher/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx19/ │ │ │ └── publisherdemo/ │ │ │ ├── PublisherDemoApplication.java │ │ │ ├── controller/ │ │ │ │ └── DemoController.java │ │ │ └── event/ │ │ │ └── UserRegisterEvent.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-19-sc-bus-kafka-demo-publisher-actuator/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx19/ │ │ │ └── publisherdemo/ │ │ │ ├── PublisherDemoApplication.java │ │ │ ├── controller/ │ │ │ │ └── DemoController.java │ │ │ └── event/ │ │ │ └── UserRegisterEvent.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-19-sc-config-server-git-auto-refresh-by-bus/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx19/ │ │ │ └── configserverdemo/ │ │ │ └── ConfigServerApplication.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-19-sc-config-user-application-auto-refresh-by-bus/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx19/ │ │ │ └── userapplication/ │ │ │ ├── UserApplication.java │ │ │ ├── config/ │ │ │ │ └── OrderProperties.java │ │ │ └── controller/ │ │ │ └── DemoController.java │ │ └── resources/ │ │ ├── application.yml │ │ └── bootstrap.yml │ ├── pom.xml │ └── 《芋道 Spring Cloud 事件总线 Kafka 入门》.md ├── labx-20/ │ ├── labx-20-sc-config-server-git-auto-refresh-by-bus/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx20/ │ │ │ └── configserverdemo/ │ │ │ └── ConfigServerApplication.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-20-sc-config-user-application-auto-refresh-by-bus/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx20/ │ │ │ └── userapplication/ │ │ │ ├── UserApplication.java │ │ │ ├── config/ │ │ │ │ └── OrderProperties.java │ │ │ └── controller/ │ │ │ └── DemoController.java │ │ └── resources/ │ │ ├── application.yml │ │ └── bootstrap.yml │ ├── labx-20-sca-bus-rocketmq-demo-listener/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx20/ │ │ │ └── listenerdemo/ │ │ │ ├── ListenerDemoApplication.java │ │ │ ├── event/ │ │ │ │ └── UserRegisterEvent.java │ │ │ └── listener/ │ │ │ └── UserRegisterListener.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-20-sca-bus-rocketmq-demo-listener-actuator/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx20/ │ │ │ └── listenerdemo/ │ │ │ ├── ListenerDemoApplication.java │ │ │ ├── event/ │ │ │ │ └── UserRegisterEvent.java │ │ │ └── listener/ │ │ │ └── UserRegisterListener.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-20-sca-bus-rocketmq-demo-publisher/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx20/ │ │ │ └── publisherdemo/ │ │ │ ├── PublisherDemoApplication.java │ │ │ ├── controller/ │ │ │ │ └── DemoController.java │ │ │ └── event/ │ │ │ └── UserRegisterEvent.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-20-sca-bus-rocketmq-demo-publisher-actuator/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx20/ │ │ │ └── publisherdemo/ │ │ │ ├── PublisherDemoApplication.java │ │ │ ├── controller/ │ │ │ │ └── DemoController.java │ │ │ └── event/ │ │ │ └── UserRegisterEvent.java │ │ └── resources/ │ │ └── application.yml │ ├── pom.xml │ └── 《芋道 Spring Cloud Alibaba 事件总线 Bus RocketMQ 入门》.md ├── labx-21/ │ ├── labx-21-sc-user-service/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx21/ │ │ │ └── userservice/ │ │ │ ├── UserServiceApplication.java │ │ │ ├── controller/ │ │ │ │ └── UserController.java │ │ │ └── dto/ │ │ │ ├── UserAddDTO.java │ │ │ └── UserDTO.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-21-sc-zuul-demo01/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx21/ │ │ │ └── zuuldemo/ │ │ │ └── ZuulApplication.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-21-sc-zuul-demo02-registry/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx21/ │ │ │ └── zuuldemo/ │ │ │ └── ZuulApplication.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-21-sc-zuul-demo03-config-apollo/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx21/ │ │ │ └── zuuldemo/ │ │ │ ├── ZuulApplication.java │ │ │ └── ZuulPropertiesRefresher.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-21-sc-zuul-demo03-config-nacos/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx21/ │ │ │ └── zuuldemo/ │ │ │ ├── ZuulApplication.java │ │ │ └── ZuulRouteRefreshListener.java │ │ └── resources/ │ │ ├── application.yaml │ │ └── bootstrap.yaml │ ├── labx-21-sc-zuul-demo05-custom-zuul-filter/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx21/ │ │ │ └── zuuldemo/ │ │ │ ├── ZuulApplication.java │ │ │ └── filter/ │ │ │ └── AuthZuulFilter.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-21-sc-zuul-demo07-hystrix/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx21/ │ │ │ └── zuuldemo/ │ │ │ ├── ZuulApplication.java │ │ │ └── fallback/ │ │ │ └── ApiFallbackProvider.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-21-sc-zuul-demo07-sentinel/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx21/ │ │ │ └── zuuldemo/ │ │ │ ├── CustomBlockFallbackProvider.java │ │ │ └── ZuulApplication.java │ │ └── resources/ │ │ ├── application.yaml │ │ ├── sentinel-gw-api-group.json │ │ └── sentinel-gw-flow.json │ ├── labx-21-sc-zuul-demo09-actuator/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx21/ │ │ │ └── zuuldemo/ │ │ │ └── ZuulApplication.java │ │ └── resources/ │ │ └── application.yaml │ ├── pom.xml │ └── 《芋道 Spring Cloud 网关 Zuul 入门》.md ├── labx-22/ │ ├── labx-22-scn-eureka-demo01-consumer/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx22/ │ │ │ └── consumerdemo/ │ │ │ └── DemoConsumerApplication.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-22-scn-eureka-demo01-provider/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx22/ │ │ │ └── providerdemo/ │ │ │ └── DemoProviderApplication.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-22-scn-eureka-demo02-consumer/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx22/ │ │ │ └── consumerdemo/ │ │ │ └── DemoConsumerApplication.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-22-scn-eureka-demo02-provider/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx22/ │ │ │ └── providerdemo/ │ │ │ └── DemoProviderApplication.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-22-scn-eureka-demo03-consumer/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx22/ │ │ │ └── consumerdemo/ │ │ │ └── DemoConsumerApplication.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-22-scn-eureka-demo03-provider/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx22/ │ │ │ └── providerdemo/ │ │ │ └── DemoProviderApplication.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-22-scn-eureka-server-cluster/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx22/ │ │ │ └── eurekaserverdemo/ │ │ │ └── EurekaServerApplication.java │ │ └── resources/ │ │ ├── application-node01.yaml │ │ └── application-node02.yaml │ ├── labx-22-scn-eureka-server-security/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx22/ │ │ │ └── eurekaserverdemo/ │ │ │ ├── EurekaServerApplication.java │ │ │ └── config/ │ │ │ └── WebSecurityConfig.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-22-scn-eureka-server-standalone/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloudalibaba/ │ │ │ └── labx22/ │ │ │ └── eurekaserverdemo/ │ │ │ └── EurekaServerApplication.java │ │ └── resources/ │ │ └── application.yaml │ ├── pom.xml │ └── 《芋道 Spring Cloud Netflix 注册中心 Eureka 入门》.md ├── labx-23/ │ ├── labx-23-scn-hystrix-actuator/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx23/ │ │ │ └── hystrixdemo/ │ │ │ ├── DemoApplication.java │ │ │ ├── controller/ │ │ │ │ ├── CacheDemoController.java │ │ │ │ ├── CollapserDemoController.java │ │ │ │ └── DemoController.java │ │ │ ├── filter/ │ │ │ │ └── HystrixRequestContextFilter.java │ │ │ └── service/ │ │ │ ├── CacheDemoService.java │ │ │ └── CollapserDemoService.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-23-scn-hystrix-dashboard/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx23/ │ │ │ └── hystrixdemo/ │ │ │ └── HystrixDashboardApplication.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-23-scn-hystrix-dashboard-turbine/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx23/ │ │ │ └── hystrixdemo/ │ │ │ └── HystrixDashboardApplication.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-23-scn-hystrix-demo01/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springcloud/ │ │ └── labx23/ │ │ └── hystrixdemo/ │ │ ├── DemoApplication.java │ │ ├── controller/ │ │ │ ├── CacheDemoController.java │ │ │ ├── CollapserDemoController.java │ │ │ └── DemoController.java │ │ ├── filter/ │ │ │ └── HystrixRequestContextFilter.java │ │ └── service/ │ │ ├── CacheDemoService.java │ │ └── CollapserDemoService.java │ ├── labx-23-scn-hystrix-demo01-cluster/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx23/ │ │ │ └── hystrixdemo/ │ │ │ ├── DemoApplication.java │ │ │ ├── controller/ │ │ │ │ ├── CacheDemoController.java │ │ │ │ ├── CollapserDemoController.java │ │ │ │ └── DemoController.java │ │ │ ├── filter/ │ │ │ │ └── HystrixRequestContextFilter.java │ │ │ └── service/ │ │ │ ├── CacheDemoService.java │ │ │ └── CollapserDemoService.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-23-scn-hystrix-dubbo-demo/ │ │ ├── labx-23-scn-hystrix-dubbo-demo-application/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springcloud/ │ │ │ │ └── labx23/ │ │ │ │ └── demo/ │ │ │ │ ├── DemoApplication.java │ │ │ │ └── controller/ │ │ │ │ └── DemoController.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ ├── labx-23-scn-hystrix-dubbo-demo-user-service/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springcloud/ │ │ │ │ └── labx23/ │ │ │ │ └── userservice/ │ │ │ │ ├── UserServiceApplication.java │ │ │ │ └── service/ │ │ │ │ └── UserServiceImpl.java │ │ │ └── resources/ │ │ │ └── application.yaml │ │ ├── labx-23-scn-hystrix-dubbo-demo-user-service-api/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx23/ │ │ │ └── userservice/ │ │ │ └── api/ │ │ │ └── UserService.java │ │ └── pom.xml │ ├── labx-23-scn-hystrix-feign/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx23/ │ │ │ └── hystrixdemo/ │ │ │ ├── DemoApplication.java │ │ │ ├── controller/ │ │ │ │ └── FeignDemoController.java │ │ │ ├── fallback/ │ │ │ │ └── UserServiceFeignClientFallbackFactory.java │ │ │ └── feign/ │ │ │ └── UserServiceFeignClient.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-23-user-service/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springcloud/ │ │ └── labx23/ │ │ └── userservice/ │ │ └── UserServiceApplication.java │ ├── pom.xml │ └── 《芋道 Spring Cloud Netflix 服务容错 Hystrix 入门》.md ├── labx-24/ │ ├── labx-24-resilience4j-demo01/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx24/ │ │ │ └── resilience4jdemo/ │ │ │ ├── DemoApplication.java │ │ │ └── controller/ │ │ │ ├── BulkheadDemoController.java │ │ │ ├── DemoController.java │ │ │ ├── RateLimiterDemoController.java │ │ │ ├── RetryDemoController.java │ │ │ ├── ThreadPoolBulkheadDemoController.java │ │ │ └── TimeLimiterDemoController.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-24-resilience4j-demo02/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springcloud/ │ │ └── labx24/ │ │ └── resilience4jdemo/ │ │ ├── DemoApplication.java │ │ ├── config/ │ │ │ └── Resilience4jConfig.java │ │ └── controller/ │ │ └── DemoController.java │ ├── labx-24-user-service/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── cn/ │ │ └── iocoder/ │ │ └── springcloud/ │ │ └── labx24/ │ │ └── userservice/ │ │ └── UserServiceApplication.java │ ├── pom.xml │ └── 《芋道 Spring Cloud 服务容错 Resilience4j 入门》.md ├── labx-25/ │ ├── labx-25-sc-zookeeper-discovery-demo01-consumer/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx25/ │ │ │ └── zookeeperdemo/ │ │ │ └── consumer/ │ │ │ └── DemoConsumerApplication.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-25-sc-zookeeper-discovery-demo01-provider/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx25/ │ │ │ └── zookeeperdemo/ │ │ │ └── provider/ │ │ │ └── DemoProviderApplication.java │ │ └── resources/ │ │ └── application.yaml │ ├── pom.xml │ └── 《芋道 Spring Cloud 注册中心 ZooKeeper 入门》.md ├── labx-26/ │ ├── labx-26-sc-zookeeper-config-auto-refresh/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx26/ │ │ │ └── zookeeperdemo/ │ │ │ ├── DemoApplication.java │ │ │ ├── config/ │ │ │ │ └── OrderProperties.java │ │ │ ├── controller/ │ │ │ │ └── DemoController.java │ │ │ └── listener/ │ │ │ └── DemoEnvironmentChangeListener.java │ │ └── resources/ │ │ └── bootstrap.yaml │ ├── labx-26-sc-zookeeper-config-demo/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx26/ │ │ │ └── zookeeperdemo/ │ │ │ ├── DemoApplication.java │ │ │ ├── config/ │ │ │ │ └── OrderProperties.java │ │ │ └── controller/ │ │ │ └── DemoController.java │ │ └── resources/ │ │ └── bootstrap.yaml │ ├── pom.xml │ └── 《芋道 Spring Cloud 配置中心 ZooKeeper 入门》.md ├── labx-27/ │ ├── labx-27-sc-consul-discovery-demo01-consumer/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx27/ │ │ │ └── consuldemo/ │ │ │ └── consumer/ │ │ │ └── DemoConsumerApplication.java │ │ └── resources/ │ │ └── application.yaml │ ├── labx-27-sc-consul-discovery-demo01-provider/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx27/ │ │ │ └── consuldemo/ │ │ │ └── provider/ │ │ │ └── DemoProviderApplication.java │ │ └── resources/ │ │ └── application.yaml │ ├── pom.xml │ └── 《芋道 Spring Cloud 注册中心 Consul 入门》.md ├── labx-28/ │ ├── labx-28-sc-consul-config-auto-refresh/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx28/ │ │ │ └── consuldemo/ │ │ │ ├── DemoApplication.java │ │ │ ├── config/ │ │ │ │ └── OrderProperties.java │ │ │ ├── controller/ │ │ │ │ └── DemoController.java │ │ │ └── listener/ │ │ │ └── DemoEnvironmentChangeListener.java │ │ └── resources/ │ │ └── bootstrap.yaml │ ├── labx-28-sc-consul-config-demo/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx28/ │ │ │ └── consuldemo/ │ │ │ ├── DemoApplication.java │ │ │ ├── config/ │ │ │ │ └── OrderProperties.java │ │ │ └── controller/ │ │ │ └── DemoController.java │ │ └── resources/ │ │ └── bootstrap.yaml │ ├── pom.xml │ └── 《芋道 Spring Cloud 配置中心 Consul 入门》.md ├── labx-29-spring-cloud-consul-bus/ │ ├── labx-29-sc-bus-consul-demo-listener/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx29/ │ │ │ └── listenerdemo/ │ │ │ ├── ListenerDemoApplication.java │ │ │ ├── event/ │ │ │ │ └── UserRegisterEvent.java │ │ │ └── listener/ │ │ │ └── UserRegisterListener.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-29-sc-bus-consul-demo-listener-actuator/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ ├── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springcloud/ │ │ │ │ └── labx29/ │ │ │ │ └── listenerdemo/ │ │ │ │ ├── ListenerDemoApplication.java │ │ │ │ ├── event/ │ │ │ │ │ └── UserRegisterEvent.java │ │ │ │ └── listener/ │ │ │ │ └── UserRegisterListener.java │ │ │ └── org/ │ │ │ └── springframework/ │ │ │ └── cloud/ │ │ │ └── consul/ │ │ │ └── binder/ │ │ │ └── ConsulSendingHandler.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-29-sc-bus-consul-demo-publisher/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ ├── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springcloud/ │ │ │ │ └── labx29/ │ │ │ │ └── publisherdemo/ │ │ │ │ ├── PublisherDemoApplication.java │ │ │ │ ├── controller/ │ │ │ │ │ └── DemoController.java │ │ │ │ └── event/ │ │ │ │ └── UserRegisterEvent.java │ │ │ └── org/ │ │ │ └── springframework/ │ │ │ └── cloud/ │ │ │ └── consul/ │ │ │ └── binder/ │ │ │ └── ConsulSendingHandler.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-29-sc-config-server-git-auto-refresh-by-bus/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx29/ │ │ │ └── configserverdemo/ │ │ │ └── ConfigServerApplication.java │ │ └── resources/ │ │ └── application.yml │ ├── labx-29-sc-config-user-application-auto-refresh-by-bus/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── cn/ │ │ │ └── iocoder/ │ │ │ └── springcloud/ │ │ │ └── labx29/ │ │ │ └── userapplication/ │ │ │ ├── UserApplication.java │ │ │ ├── config/ │ │ │ │ └── OrderProperties.java │ │ │ └── controller/ │ │ │ └── DemoController.java │ │ └── resources/ │ │ ├── application.yml │ │ └── bootstrap.yml │ ├── pom.xml │ └── 《芋道 Spring Cloud 消息总线 Bus Consul 入门》.md ├── labx-30-spring-cloud-grpc/ │ ├── labx-30-grpc-cloud/ │ │ ├── labx-30-grpc-cloud-application/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springcloud/ │ │ │ │ └── labx30/ │ │ │ │ └── demo/ │ │ │ │ ├── DemoApplication.java │ │ │ │ └── controller/ │ │ │ │ └── DemoController.java │ │ │ └── resources/ │ │ │ └── application.yml │ │ ├── labx-30-grpc-cloud-user-service/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── cn/ │ │ │ │ └── iocoder/ │ │ │ │ └── springcloud/ │ │ │ │ └── labx30/ │ │ │ │ └── userservice/ │ │ │ │ ├── UserServiceApplication.java │ │ │ │ └── rpc/ │ │ │ │ └── UserServiceGrpcImpl.java │ │ │ └── resources/ │ │ │ └── application.yml │ │ ├── labx-30-grpc-cloud-user-service-api/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── proto/ │ │ │ └── UserService.proto │ │ └── pom.xml │ ├── pom.xml │ └── 《芋道 Spring Cloud gRPC 入门》.md └── pom.xml ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Created by .ignore support plugin (hsz.mobi) ### Java template # Compiled class file *.class # Log file *.log # BlueJ files *.ctxt # Mobile Tools for Java (J2ME) .mtj.tmp/ # Package Files # *.jar *.war *.nar *.ear *.zip *.tar.gz *.rar # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* SpringBoot-Labs.ipr SpringBoot-Labs.iws target/** *.lst *.iml *ipr *.iws ### IntelliJ IDEA .idea target ================================================ FILE: README.md ================================================ > 友情提示:因为提供了 50000+ 行示例代码,所以艿艿默认注释了所有 Maven Module。 > > 胖友可以根据自己的需要,修改 [`pom.xml`](https://github.com/YunaiV/SpringBoot-Labs/blob/master/pom.xml) 即可。 一个涵盖六个主流技术栈的**正经**仓库: * [《Spring Boot 专栏》](https://github.com/YunaiV/SpringBoot-Labs#spring-boot-%E4%B8%93%E6%A0%8F) * [《Spring Cloud Alibaba 专栏》](https://github.com/YunaiV/SpringBoot-Labs#spring-cloud-alibaba-%E4%B8%93%E6%A0%8F) * [《Spring Cloud 专栏》](https://github.com/YunaiV/SpringBoot-Labs#spring-cloud-%E4%B8%93%E6%A0%8F) * [《Dubbo 专栏》](https://github.com/YunaiV/SpringBoot-Labs#Dubbo-%E4%B8%93%E6%A0%8F) * [《消息队列 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) * [《分布式事务专栏》](https://github.com/YunaiV/SpringBoot-Labs#分布式事务专栏) 作为一个热爱**深夜**撸码的 18 岁头发茂密的可爱小男孩,希望大佬能够**一键三连**。 ![一间三连](http://static.iocoder.cn/github-star.jpg) > **亲,一键三连啊** # 交流群 扫码如下**二维码**,回复「艿艿」关键字。 可以添加 18 岁的我为好友,并拉你进一个~~交流~~**装逼群**。 ![骚气的二维码](http://www.iocoder.cn/images/common/erweima.jpg) 😈 等后面,艿艿**头发**重新长一点出来,给旁友们录制点视频哈! # Spring Boot 专栏 基于 Spring Boot 2.X 版本的**深度**入门教程。 市面上的 Spring Boot **基础**入门文章很多,但是**深度**入门文章却很少。对于很多开发者来说,入门即是其对某个技术栈的最终理解,一方面是开发者“比较懒”,另一方面是文章作者把 Spring Boot 入门写的太浅,又或者不够全面。 因此,艿艿开始了这个 Spring Boot 专栏,一个**深度**且**全面**的 Spring Boot 2.X 入门。 * 在带你快速学会 SpringMVC API 接口的编写的同时,我还想告诉你还有全局返回、全局异常、拦截器、跨域处理等等功能。 * 在带你快速学会 MQ 消息的发送与消费的同时,我还想告诉你 MQ 还有集群消费、广播消费、顺序消息、定时消息、事务消息、消费重试等等特性。 * 在带你快速学会 Job 任务的编写的同时,我还想告诉你还有 Quartz 单体、Quartz 集群、XXL-JOB 等等企业使用更多的调度平台。 * ... 让我们一起愉快的挖坑,挖深坑,哇哈哈。 ## 打好基础 * [《芋道 Spring Boot 快速入门》](http://www.iocoder.cn/Spring-Boot/quick-start/?github) * [《芋道 Spring Boot 自动配置原理》](http://www.iocoder.cn/Spring-Boot/autoconfigure/?github) 对应 [lab-47](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-47) * [《芋道 Spring Boot Jar 启动原理》](http://www.iocoder.cn/Spring-Boot/jar/?github) * [《芋道 Spring Boot 调试环境》](http://www.iocoder.cn/Spring-Boot/build-debugging-environment-2-6-0/?github) ## 开发工具 * [《芋道 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) * [《芋道 Spring Boot 消除冗余代码 Lombok 入门》](http://www.iocoder.cn/Spring-Boot/Lombok/?github) 对应 [lab-49](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-49) * [《芋道 Spring Boot 对象转换 MapStruct 入门》](http://www.iocoder.cn/Spring-Boot/MapStruct/?github) 对应 [lab-55](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-55) ## Web 开发 * [《芋道 Spring Boot SpringMVC 入门》](http://www.iocoder.cn/Spring-Boot/SpringMVC/?github) 对应 [lab-23](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-23) * [《芋道 Spring Boot WebFlux 入门》](http://www.iocoder.cn/Spring-Boot/WebFlux/?github) 对应 [lab-27](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-27) * [《芋道 Spring Boot 分布式 Session 入门》](http://www.iocoder.cn/Spring-Boot/Distributed-Session/?github) 对应 [lab-26](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-26) * [《芋道 Spring Boot API 接口文档 Swagger 入门》](http://www.iocoder.cn/Spring-Boot/Swagger/?github) 对应 [lab-24](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-24) * [《芋道 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) * [《芋道 Spring Boot 参数校验 Validation 入门》](http://www.iocoder.cn/Spring-Boot/Validation/?github) 对应 [lab-22](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-22) * [《芋道 Spring Boot WebSocket 入门》](http://www.iocoder.cn/Spring-Boot/WebSocket/?github) 对应 [lab-25](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-25) * [《性能测试 —— 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) * [《性能测试 —— SpringMVC、Webflux 基准测试》](http://www.iocoder.cn/Performance-Testing/SpringMVC-Webflux-benchmark/?github) 对应 [lab-06](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-06) * [《芋道 Spring Boot API 接口文档 JApiDocs 入门》](http://www.iocoder.cn/Spring-Boot/JApiDocs/?github) 对应 [lab-24](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-24) * [《芋道 Spring Boot API 接口文档 ShowDoc 入门》](http://www.iocoder.cn/Spring-Boot/ShowDoc/?github) 对应 [lab-24](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-24) * [《芋道 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/) ## RPC 开发 * [《芋道 Spring Boot Netty 入门》](http://www.iocoder.cn/Spring-Boot/Netty/?github) 对应 [lab-67](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-67) * [《芋道 Spring Boot Dubbo 入门》](http://www.iocoder.cn/Spring-Boot/Dubbo/?github) 对应 [lab-30](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-30) * [《芋道 Spring Boot 声明式调用 Feign 入门》](http://www.iocoder.cn/Spring-Boot/Feign/?github) 对应 [lab-58](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-58) * [《芋道 Spring Boot gRPC 入门》](http://www.iocoder.cn/Spring-Boot/gRPC/?github) 对应 [lab-64](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-64) * [《芋道 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) * [《芋道 Spring Boot SOFARPC 入门》](http://www.iocoder.cn/Spring-Boot/SOFARPC/?github) 对应 [lab-62](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-62) * [《芋道 Spring Boot Motan 入门》](http://www.iocoder.cn/Spring-Boot/Motan/?github) 对应 [lab-63](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-63) * 《芋道 Spring Boot RSocket 入门》计划中... * 《芋道 Spring Boot Tars 入门》计划中... ## 文件存储 * [《芋道 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) ## 数据访问 **关系数据库** * [《芋道 Spring Boot 数据库连接池入门》](http://www.iocoder.cn/Spring-Boot/datasource-pool/?github) 对应 [lab-19](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-19) * [《芋道 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) * [《芋道 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) * [《芋道 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) * [《芋道 Spring Boot 多数据源(读写分离)入门》](http://www.iocoder.cn/Spring-Boot/dynamic-datasource/?github) 对应 [lab-17](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-17) * [《芋道 Spring Boot 分库分表入门》](http://www.iocoder.cn/Spring-Boot/sharding-datasource/?github) 对应 [lab-18](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-18) * [《芋道 Spring Boot 数据库版本管理入门》](http://www.iocoder.cn/Spring-Boot/database-version-control/?github) 对应 [lab-20](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-20) * [《芋道 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) **非关系数据库** * [《芋道 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) * [《芋道 Spring Boot 缓存 Cache 入门》](http://www.iocoder.cn/Spring-Boot/Cache/?github) 对应 [lab-21](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-21) * [《芋道 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) * [《芋道 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) * [《芋道 Spring Boot Solr 入门》](http://www.iocoder.cn/Spring-Boot/Solr/?github) 对应 [lab-66](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-66) ## 事务管理 * [《芋道 Spring Boot 分布式事务 Seata 入门》](http://www.iocoder.cn/Spring-Boot/Seata/?github) 对应 [lab-52](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-52) ## 安全控制 * [《芋道 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) * [《芋道 Spring Boot 安全框架 Shiro 入门》](http://www.iocoder.cn/Spring-Boot/Shiro/?github) 对应 [lab-33](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-33) **OAuth 2.0** * [《芋道 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) * [《芋道 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) * [《芋道 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) ## 定时任务与异步任务 * [《芋道 Spring Boot 定时任务入门》](http://www.iocoder.cn/Spring-Boot/Job/?github) 对应 [lab-28](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-28) * [《芋道 Spring Boot 异步任务入门》](http://www.iocoder.cn/Spring-Boot/Async-Job/?github) 对应 [lab-29](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-29) ## 消息队列 * [《芋道 Spring Boot 消息队列 RocketMQ 入门》](http://www.iocoder.cn/Spring-Boot/RocketMQ/?github) 对应 [lab-31](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-31) * [《芋道 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) * [《芋道 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) * [《芋道 Spring Boot 消息队列 ActiveMQ 入门》](http://www.iocoder.cn/Spring-Boot/ActiveMQ/?github) 对应 [lab-32](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-32) * [《芋道 Spring Boot 事件机制 Event 入门》](http://www.iocoder.cn/Spring-Boot/Event/?github) 对应 对应 [lab-54](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-54) ## 配置中心 * [《芋道 Spring Boot 配置文件入门》](http://www.iocoder.cn/Spring-Boot/config-file/?github) 对应 [lab-43](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-43) * [《芋道 Spring Boot 配置中心 Apollo 入门》](http://www.iocoder.cn/Spring-Boot/config-apollo/?github) 对应 [lab-45](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-45) * [《芋道 Spring Boot 配置中心 Nacos 入门》](http://www.iocoder.cn/Spring-Boot/config-nacos/?github) 对应 [lab-44](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-44) ## 注册中心 * [《芋道 Spring Boot 注册中心 Nacos 入门》](http://www.iocoder.cn/Spring-Boot/registry-nacos/?github) 对应 [lab-44](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-44) ## 持续交付 * [《芋道 Spring Boot 持续交付 Jenkins 入门》](http://www.iocoder.cn/Spring-Boot/Jenkins/?github) 对应 [lab-41](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-41) * [《芋道 Spring Boot 单元测试 Test 入门》](http://www.iocoder.cn/Spring-Boot/Unit-Test/?github) 对应 [lab-42](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-42) * 《芋道 Spring Boot 容器 Docker 入门》计划中... ## 服务容错 * [《芋道 Spring Boot 服务容错 Sentinel 入门》](http://www.iocoder.cn/Spring-Boot/Sentinel/?github) 对应 [lab-46](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-46) * [《芋道 Spring Boot 服务容错 Hystrix 入门》](http://www.iocoder.cn/Spring-Boot/Hystrix/?github) 对应 [lab-57](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-57) * [《芋道 Spring Boot 服务容错 Resilience4j 入门》](http://www.iocoder.cn/Spring-Boot/Resilience4j/?github) 对应 [lab-59](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-59) ## 监控管理 * [《芋道 Spring Boot 异常管理平台 Sentry 入门》](http://www.iocoder.cn/Spring-Boot/Sentry/?github) 对应 [lab-51](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-51) * [《芋道 Spring Boot 监控端点 Actuator 入门》](http://www.iocoder.cn/Spring-Boot/Actuator/?github) 对应 [lab-34](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-34) * [《芋道 Spring Boot 监控工具 Admin 入门》](http://www.iocoder.cn/Spring-Boot/Admin/?github) 对应 [lab-35](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-35) * [《芋道 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) * [《芋道 Spring Boot 监控平台 CAT 入门》](http://www.iocoder.cn/Spring-Boot/CAT/?github) 对应 [lab-61](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-61) ## 日志管理 * [《芋道 Spring Boot 日志集成 Logging 入门》](http://www.iocoder.cn/Spring-Boot/Logging/?github) 对应 [lab-37](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-37) * [《芋道 Spring Boot 日志平台 ELK + Filebeat 入门》](http://www.iocoder.cn/Spring-Boot/ELK/?github) 对应 [lab-38](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-38) * 《芋道 Spring Boot 日志平台 Loki 入门》计划中... ## 链路追踪 * [《芋道 Spring Boot 链路追踪 SkyWalking 入门》](http://www.iocoder.cn/Spring-Boot/SkyWalking/?github) 对应 [lab-39](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-39) * [《芋道 Spring Boot 链路追踪 Zipkin 入门》](http://www.iocoder.cn/Spring-Boot/Zipkin/?github) 对应 [lab-40](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-40) * 《芋道 Spring Boot 链路追踪 Pinpoint 入门》计划中... * 《芋道 Spring Boot 链路追踪 Elastic APM 入门》计划中... # Spring Cloud Alibaba 专栏 ## Spring Cloud Alibaba 全家桶 * [《芋道 Spring Cloud Alibaba 介绍》](http://www.iocoder.cn/Spring-Cloud-Alibaba/intro/?github) * [《芋道 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) * [《芋道 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) * [《芋道 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) * [《芋道 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) * [《芋道 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) * [《芋道 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) * [《芋道 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) ## 推荐搭配食用 * [《芋道 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) * [《芋道 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) * [《芋道 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) * [《芋道 Spring Cloud 链路追踪 SkyWalking 入门》](http://www.iocoder.cn/Spring-Cloud/SkyWalking/?github) 对应 [labx-14](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-14) * [《芋道 Dubbo Admin 快速入门》](http://www.iocoder.cn/Dubbo/Admin/?github) * [《芋道 Dubbo Swagger 快速入门》](http://www.iocoder.cn/Dubbo/Swagger/?github) 对应 [swagger-dubbo](https://github.com/YunaiV/swagger-dubbo) # Spring Cloud 专栏 ## 注册中心 * [《芋道 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) * [《芋道 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) * [《芋道 Spring Cloud 注册中心 Zookeeper 入门》](http://www.iocoder.cn/Spring-Cloud/ZooKeeper-Discovery/?github) 对应 [labx-25](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-25) * [《芋道 Spring Cloud 注册中心 Consul 入门》](http://www.iocoder.cn/Spring-Cloud/Consul-Discovery/?github) 对应 [labx-27](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-27) * [《芋道 Spring Cloud 注册中心 Etcd 入门》](http://www.iocoder.cn/Spring-Cloud/Etcd/?github) ## 服务调用 * [《芋道 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) * [《芋道 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) * [《芋道 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) * [《芋道 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) ## 服务容错 * [《芋道 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) * [《芋道 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) * [《芋道 Spring Cloud 服务容错 Resilience4j 入门》](http://www.iocoder.cn/Spring-Cloud/Resilience4j/?github) 对应 [lab-59](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-59) * 《芋道 Spring Cloud 服务容错 Spring Cloud CircuitBreaker》计划中... ## API 网关 * [《芋道 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) * [《芋道 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) * [《性能测试 —— 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) > 如下非 Spring Cloud 网关,先放在这里... * [《芋道 APISIX 极简入门(国产微服务网关)》](http://www.iocoder.cn/APISIX/install/?github) 对应 [lab-56](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-56) * [《芋道 Soul 极简入门(国产微服务网关)》](http://www.iocoder.cn/Soul/install/?github) 对应 [lab-60](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-60) * [《芋道 Kong 极简入门(微服务网关)》](http://www.iocoder.cn/Kong/install/?github) 对应 [lab-56](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-56) ## 配置中心 * [《芋道 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) * [《芋道 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) * [《芋道 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) * [《芋道 Spring Cloud 配置中心 Zookeeper 入门》](http://www.iocoder.cn/Spring-Cloud/ZooKeeper-Config/?github) 对应 [labx-26](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-26) * [《芋道 Spring Cloud 配置中心 Consul 入门》](http://www.iocoder.cn/Spring-Cloud/Consul-Config/?github) 对应 [labx-28](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-28) * [《芋道 Spring Cloud 配置中心 Etcd 入门》](http://www.iocoder.cn/Spring-Cloud/Etcd/?github) ## 消息队列 **Spring Cloud Stream** * [《芋道 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) * [《芋道 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) * [《芋道 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) * [《芋道 Spring Cloud 消息队列 ActiveMQ 入门》](http://www.iocoder.cn/Spring-Cloud/ActiveMQ/?github) **Spring Cloud Bus** * [《芋道 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) * [《芋道 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) * [《芋道 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) * [《芋道 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) ## 分布式事务 * [《芋道 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) ## 监控管理 * [《芋道 Spring Boot 异常管理平台 Sentry 入门》](http://www.iocoder.cn/Spring-Boot/Sentry/?github) 对应 [lab-51](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-51) * [《芋道 Spring Boot 监控端点 Actuator 入门》](http://www.iocoder.cn/Spring-Boot/Actuator/?github) 对应 [lab-34](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-34) * [《芋道 Spring Cloud 监控工具 Admin 入门》](http://www.iocoder.cn/Spring-Cloud/SB-Admin/?github) 对应 [labx-15](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-15) * [《芋道 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) ## 持续交付 * [《芋道 Spring Cloud 持续交付 Jenkins 入门》](http://www.iocoder.cn/Spring-Cloud/Jenkins/?github) 对应 [labx-16](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-16) * [《芋道 Spring Boot 单元测试 Test 入门》](http://www.iocoder.cn/Spring-Boot/Unit-Test/?github) 对应 [lab-42](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-42) * 《芋道 Spring Cloud 容器 Docker 入门》计划中... ## 链路追踪 * [《芋道 Spring Cloud 链路追踪 SkyWalking 入门》](http://www.iocoder.cn/Spring-Cloud/SkyWalking/?github) 对应 [labx-14](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-14) * [《芋道 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) # Dubbo 专栏 ## 基础入门 * [《芋道 Spring Boot Dubbo 入门》](http://www.iocoder.cn/Spring-Boot/Dubbo/?github) 对应 [lab-30](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-30) * [《芋道 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) * [《性能测试 —— Dubbo 基准测试》](http://www.iocoder.cn/Performance-Testing/Dubbo-benchmark/?github) * [《芋道 Dubbo Swagger 快速入门》](http://www.iocoder.cn/Dubbo/Swagger/?github) 对应 [swagger-dubbo](https://github.com/YunaiV/swagger-dubbo) ## 注册中心 **[Zookeeper](http://www.iocoder.cn/Zookeeper/install/?github)** * [《芋道 Spring Boot Dubbo 入门》](http://www.iocoder.cn/Spring-Boot/Dubbo/?github)的[「2. XML 配置」](#)和[「3. 注解配置」](#)小节 **[Nacos](http://www.iocoder.cn/Nacos/install/?github)** * [《芋道 Spring Boot Dubbo 入门》](http://www.iocoder.cn/Spring-Boot/Dubbo/?github)的[「6. 整合 Nacos」](#)小节 * [《芋道 Spring Cloud Alibaba 服务调用 Dubbo 入门》](http://www.iocoder.cn/Spring-Cloud-Alibaba/Dubbo/?github)的[「2. 快速入门」](#)小节 ## 服务容错 **[Sentinel](http://www.iocoder.cn/Sentinel/install/?github)** * [《芋道 Spring Boot Dubbo 入门》](http://www.iocoder.cn/Spring-Boot/Dubbo/?github)的[「7. 整合 Sentinel」](#)小节 * [《芋道 Spring Cloud Alibaba 服务调用 Dubbo 入门》](http://www.iocoder.cn/Spring-Cloud-Alibaba/Dubbo/?github)的[「6. 整合 Sentinel」](#)小节 **[Hystrix](http://www.iocoder.cn/categories/Hystrix/?github)** * [《芋道 Spring Boot 服务容错 Hystrix 入门》](http://www.iocoder.cn/Spring-Boot/Hystrix/?github)的[「6. 集成到 Dubbo」](#)小节 * [《芋道 Spring Cloud Netflix 服务容错 Hystrix 入门》](http://www.iocoder.cn/Spring-Cloud/Netflix-Hystrix/?github)的[「10. 集成到 Dubbo」](#)小节 **[Resilience4j](http://www.iocoder.cn/categories/Resilience4j/?github)** * [《芋道 Spring Boot 服务容错 Resilience4j 入门》](http://www.iocoder.cn/Spring-Boot/Resilience4j/?github)的[「10. 集成到 Dubbo」](#)小节 ## API 网关 * [《芋道 Soul 极简入门(国产微服务网关)》](http://www.iocoder.cn/Soul/install/?github)的[「3. 接入 Dubbo 应用」](#)小节 ## 分布式事务 **[Seata](http://www.iocoder.cn/Seata/install/?github)** * [《芋道 Dubbo 分布式事务 Seata 入门》](http://www.iocoder.cn/Dubbo/Seata/?github) 对应 [lab-53](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-53) * [《芋道 Spring Cloud Alibaba 分布式事务 Seata 入门》](http://www.iocoder.cn/Spring-Cloud-Alibaba/Seata/?github)的[「2. AT 模式 + Dubbo」](#)小节 **[TCC Transaction](http://www.iocoder.cn/categories/TCC-Transaction/?github)** * [《TCC-Transaction 源码分析 —— Dubbo 支持》](http://www.iocoder.cn/TCC-Transaction/dubbo-support/?self) ## 链路追踪 **[SkyWalking](http://www.iocoder.cn/SkyWalking/install/?github)** * [《芋道 Spring Boot 链路追踪 SkyWalking 入门》](http://www.iocoder.cn/Spring-Boot/SkyWalking/?github) 的[「16. Dubbo 示例」](#)小节 * [《芋道 Spring Cloud 链路追踪 SkyWalking 入门》](http://www.iocoder.cn/Spring-Cloud/SkyWalking/?github) 的[「7. Dubbo 示例」](#)小节 **[Zipkin](http://www.iocoder.cn/Zipkin/install/?github)** * [《芋道 Spring Boot 链路追踪 Zipkin》](http://www.iocoder.cn/Spring-Cloud/Spring-Cloud-Sleuth/?github) 的[「13. Dubbo 示例」](#)小节 * [《芋道 Spring Cloud 链路追踪 Spring Cloud Sleuth》](http://www.iocoder.cn/Spring-Cloud/Spring-Cloud-Sleuth/?github) 的[「7. Dubbo 示例」](#)小节 ## 监控管理 **[CAT](http://www.iocoder.cn/CAT/install/?github)** * [《芋道 Spring Boot 监控平台 CAT 入门》](http://www.iocoder.cn/Spring-Boot/CAT/?github) 的[「13. Dubbo 集成」](#)小节 **[Dubbo Admin](http://www.iocoder.cn/Dubbo/Admin/?github)** * [《芋道 Dubbo Admin 快速入门》](http://www.iocoder.cn/Dubbo/Admin/?github) # 消息队列 MQ 专栏 ## RocketMQ * [《RocketMQ 极简入门》](http://www.iocoder.cn/RocketMQ/install/?github) * [《芋道 Spring Boot 消息队列 RocketMQ 入门》](http://www.iocoder.cn/Spring-Boot/RocketMQ/?github) 对应 [lab-31](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-31) * [《芋道 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) * [《芋道 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) * [《芋道 RocketMQ 源码解析系列》](http://www.iocoder.cn/categories/RocketMQ/?github) * [《性能测试 —— RocketMQ 基准测试》](http://www.iocoder.cn/Performance-Testing/RocketMQ-benchmark/?github) * [《RocketMQ 书单整理》](http://www.iocoder.cn/Books/RocketMQ-books-recommended/?github) ## RabbitMQ * [《RabbitMQ 极简入门》](http://www.iocoder.cn/RabbitMQ/install/?github) * [《芋道 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) * [《芋道 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) * [《芋道 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) * [《RabbitMQ 书单整理》](http://www.iocoder.cn/Books/RabbitMQ-books-recommended/?github) ## Kafka * [《Kafka 极简入门》](http://www.iocoder.cn/Kafka/install/?github) * [《芋道 Spring Boot 消息队列 Kafka 入门》](http://www.iocoder.cn/Spring-Boot/Kafka/?github) 对应 [lab-03](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-03) * [《芋道 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) * [《芋道 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) * [《Kafka 书单整理》](http://www.iocoder.cn/Books/Kafka-books-recommended/?github) ## ActiveMQ * [《ActiveMQ 极简入门》](http://www.iocoder.cn/ActiveMQ/install/?github) * [《芋道 Spring Boot 消息队列 ActiveMQ 入门》](http://www.iocoder.cn/Spring-Boot/ActiveMQ/?github) 对应 [lab-32](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-32) * [《芋道 Spring Cloud 消息队列 ActiveMQ 入门》](http://www.iocoder.cn/Spring-Cloud/ActiveMQ/?github) # 分布式事务专栏 目前分布式事务的解决方案有 AT、TCC、Saga、MQ、XA、BED 六种。 ## AT 方案 * [《芋道 Spring Boot 分布式事务 Seata 入门》](http://www.iocoder.cn/Spring-Boot/Seata/?github)的[「2. AT 模式 + 多数据源」](#)小节,实现单体 Spring Boot 项目在多数据源下的分布式事务 * [《芋道 Spring Boot 分布式事务 Seata 入门》](http://www.iocoder.cn/Spring-Boot/Seata/?github)的[「AT 模式 + HttpClient 远程调用」](#)小节,实现多个 Spring Boot 项目的分布式事务 * [《芋道 Dubbo 分布式事务 Seata 入门》](http://www.iocoder.cn/Dubbo/Seata/?github) 的[「2. AT 模式」](#)小节,实现多个 Dubbo 服务的分布式事务。 * [《芋道 Spring Cloud Alibaba 分布式事务 Seata 入门》](http://www.iocoder.cn/Spring-Cloud-Alibaba/Seata/?github)的[「3. AT 模式 + Feign」](#)小节,实现多个 Spring Cloud 服务下的分布式事务。 ## TCC 方案 * [《TCC-Transaction 源码解析系列》](http://www.iocoder.cn/categories/TCC-Transaction/?github) ## Saga 方案 ## MQ 方案 * [《芋道 Spring Boot 消息队列 RocketMQ 入门》](http://www.iocoder.cn/Spring-Boot/RocketMQ/?github) 的[「9. 事务消息」](#)小节 * [《芋道 Spring Cloud Alibaba 消息队列 RocketMQ 入门》](http://www.iocoder.cn/Spring-Cloud-Alibaba/RocketMQ/?github) 的[「10. 事务消息」](#)小节 * [《RocketMQ 源码分析 —— 事务消息》](http://www.iocoder.cn/RocketMQ/message-transaction/?github) ## XA 方案 * [《MyCAT 源码分析 —— XA分布式事务》](http://www.iocoder.cn/MyCAT/xa-distributed-transaction/?github) ## BED 方案 * [《Sharding-JDBC 源码分析 —— 分布式事务(一)之最大努力型》](http://www.iocoder.cn/Sharding-JDBC/transaction-bed/?github) --------- 如下是草稿目录,未来需要整理下 # lab-50 Email 示例 # lab-69-proxy 动态代理 ================================================ FILE: httpRequests/2020-12-20T004250.200.json ================================================ { "userId": 1, "token": "token001" } ================================================ FILE: httpRequests/2020-12-20T004328.500.json ================================================ { "timestamp": "2020-12-19T16:43:28.332+0000", "status": 500, "error": "Internal Server Error", "message": "小朋友,你的账号密码不正确哟!", "path": "/user/login" } ================================================ FILE: httpRequests/2020-12-20T004337.200.json ================================================ { "userId": 1, "token": "token001" } ================================================ FILE: httpRequests/2020-12-20T004347.200.json ================================================ { "userId": 1, "token": "token001" } ================================================ FILE: httpRequests/2020-12-20T004347.500.json ================================================ { "timestamp": "2020-12-19T16:43:47.777+0000", "status": 500, "error": "Internal Server Error", "message": "小朋友,你的账号密码不正确哟!", "path": "/user/login" } ================================================ FILE: httpRequests/2020-12-20T004358.200.json ================================================ { "userId": 1, "token": "token001" } ================================================ FILE: httpRequests/2020-12-20T004358.500.json ================================================ { "timestamp": "2020-12-19T16:43:58.210+0000", "status": 500, "error": "Internal Server Error", "message": "小朋友,你的账号密码不正确哟!", "path": "/user/login" } ================================================ FILE: httpRequests/2020-12-20T004401.200.json ================================================ { "userId": 1, "token": "token001" } ================================================ FILE: httpRequests/2020-12-20T004401.500.json ================================================ { "timestamp": "2020-12-19T16:44:01.275+0000", "status": 500, "error": "Internal Server Error", "message": "小朋友,你的账号密码不正确哟!", "path": "/user/login" } ================================================ FILE: httpRequests/2020-12-20T004538.200.json ================================================ { "userId": 1, "token": "token001" } ================================================ FILE: httpRequests/2020-12-20T004547.500.json ================================================ { "timestamp": "2020-12-19T16:45:47.364+0000", "status": 500, "error": "Internal Server Error", "message": "小朋友,你的账号密码不正确哟!", "path": "/user/login" } ================================================ FILE: httpRequests/2020-12-20T004638.500.json ================================================ { "timestamp": "2020-12-19T16:46:38.505+0000", "status": 500, "error": "Internal Server Error", "message": "小朋友,你的账号密码不正确哟!", "path": "/user/login" } ================================================ FILE: httpRequests/2020-12-20T004645.500.json ================================================ { "timestamp": "2020-12-19T16:46:45.875+0000", "status": 500, "error": "Internal Server Error", "message": "小朋友,你的账号密码不正确哟!", "path": "/user/login" } ================================================ FILE: httpRequests/2020-12-20T004809.200.json ================================================ { "userId": 1, "token": "token001" } ================================================ FILE: httpRequests/2020-12-20T004813.500.json ================================================ { "timestamp": "2020-12-19T16:48:13.674+0000", "status": 500, "error": "Internal Server Error", "message": "小朋友,你的账号密码不正确哟!", "path": "/user/login" } ================================================ FILE: httpRequests/2020-12-20T010724.200.json ================================================ { "userId": 1, "token": "token001" } ================================================ FILE: httpRequests/2020-12-20T010738.200.json ================================================ { "userId": 1, "token": "token001" } ================================================ FILE: httpRequests/2020-12-20T010809.200.json ================================================ { "userId": 1, "token": "token001" } ================================================ FILE: httpRequests/2020-12-20T010823.500.json ================================================ { "timestamp": "2020-12-19T17:08:23.036+0000", "status": 500, "error": "Internal Server Error", "message": "小朋友,你的账号密码不正确哟!", "path": "/user/login" } ================================================ FILE: httpRequests/2020-12-20T010840.200.json ================================================ { "userId": 1, "token": "token001" } ================================================ FILE: httpRequests/2020-12-20T011020.500.json ================================================ { "timestamp": "2020-12-19T17:10:20.615+0000", "status": 500, "error": "Internal Server Error", "message": "小朋友,你的账号密码不正确哟!", "path": "/user/login" } ================================================ FILE: httpRequests/2020-12-20T011347.500.json ================================================ { "timestamp": "2020-12-19T17:13:47.777+0000", "status": 500, "error": "Internal Server Error", "message": "小朋友,你没有登录哟!", "path": "/user/get-current" } ================================================ FILE: httpRequests/2020-12-20T011526.200.json ================================================ { "userId": 1, "token": "token001" } ================================================ FILE: httpRequests/2020-12-20T011530.200.json ================================================ { "gender": 1, "nickname": "芋道源码", "id": 1 } ================================================ FILE: httpRequests/2020-12-20T011541.400.json ================================================ { "timestamp": "2020-12-19T17:15:40.992+0000", "status": 400, "error": "Bad Request", "message": "Missing request header 'Authorization' for method parameter of type String", "path": "/user/get-current" } ================================================ FILE: httpRequests/2020-12-20T011551.500.json ================================================ { "timestamp": "2020-12-19T17:15:51.919+0000", "status": 500, "error": "Internal Server Error", "message": "小朋友,你没有登录哟!", "path": "/user/get-current" } ================================================ FILE: httpRequests/2020-12-20T011556.200.json ================================================ { "gender": 1, "nickname": "芋道源码", "id": 1 } ================================================ FILE: httpRequests/2020-12-20T011628-1.200.json ================================================ { "gender": 1, "nickname": "芋道源码", "id": 1 } ================================================ FILE: httpRequests/2020-12-20T011628.200.json ================================================ { "userId": 1, "token": "token001" } ================================================ FILE: httpRequests/2020-12-20T011646.200.json ================================================ { "userId": 1, "token": "token001" } ================================================ FILE: httpRequests/2020-12-20T011650.200.json ================================================ { "userId": 1, "token": "token001" } ================================================ FILE: httpRequests/2020-12-20T011653.500.json ================================================ { "timestamp": "2020-12-19T17:16:53.489+0000", "status": 500, "error": "Internal Server Error", "message": "小朋友,你没有登录哟!", "path": "/user/get-current" } ================================================ FILE: httpRequests/2020-12-20T011818.200.json ================================================ { "userId": 1, "token": "token001" } ================================================ FILE: httpRequests/2020-12-20T011843.200.json ================================================ { "userId": 1, "token": "token001" } ================================================ FILE: httpRequests/2020-12-20T011847.500.json ================================================ { "timestamp": "2020-12-19T17:18:47.862+0000", "status": 500, "error": "Internal Server Error", "message": "小朋友,你没有登录哟!", "path": "/user/get-current" } ================================================ FILE: httpRequests/2020-12-20T012507.200.json ================================================ { "userId": 1, "token": "token001" } ================================================ FILE: httpRequests/2020-12-20T012510.200.json ================================================ { "gender": 1, "nickname": "芋道源码", "id": 1 } ================================================ FILE: httpRequests/2020-12-20T012527.200.json ================================================ { "gender": 1, "nickname": "芋道源码", "id": 1 } ================================================ FILE: httpRequests/2020-12-20T012540.400.json ================================================ { "timestamp": "2020-12-19T17:25:40.604+0000", "status": 400, "error": "Bad Request", "message": "Missing request header 'Authorization' for method parameter of type String", "path": "/user/get-current" } ================================================ FILE: httpRequests/2020-12-20T012544.200.json ================================================ { "gender": 1, "nickname": "芋道源码", "id": 1 } ================================================ FILE: httpRequests/2020-12-20T012703.500.json ================================================ { "timestamp": "2020-12-19T17:27:03.272+0000", "status": 500, "error": "Internal Server Error", "message": "小朋友,你没有登录哟!", "path": "/user/get-current" } ================================================ FILE: httpRequests/2020-12-20T012708.200.json ================================================ { "userId": 1, "token": "token001" } ================================================ FILE: httpRequests/2020-12-20T012710.200.json ================================================ { "gender": 1, "nickname": "芋道源码", "id": 1 } ================================================ FILE: httpRequests/2020-12-20T013544.200.json ================================================ { "gender": 1, "nickname": "芋道源码", "id": 1 } ================================================ FILE: httpRequests/2020-12-20T013552.200.json ================================================ { "userId": 1, "token": "token001" } ================================================ FILE: httpRequests/2020-12-20T013558.500.json ================================================ { "timestamp": "2020-12-19T17:35:58.033+0000", "status": 500, "error": "Internal Server Error", "message": "小朋友,你没有登录哟!", "path": "/user/get-current" } ================================================ FILE: httpRequests/2020-12-20T013845.200.json ================================================ { "userId": 1, "token": "token001" } ================================================ FILE: httpRequests/2020-12-20T014019.200.json ================================================ { "gender": 1, "nickname": "芋道源码", "id": 1 } ================================================ FILE: httpRequests/2020-12-20T021415.200.json ================================================ { "userId": 1, "token": "token001" } ================================================ FILE: httpRequests/http-client.cookies ================================================ # domain path name value date ================================================ FILE: httpRequests/http-requests-log.http ================================================ POST http://127.0.0.1:8080/user/login Content-Type: application/x-www-form-urlencoded username=yudaoyuanma&password=123456 <> 2020-12-20T021415.200.json ### GET http://127.0.0.1:8080/user/get-current?full=true Authorization: token001 <> 2020-12-20T014019.200.json ### POST http://127.0.0.1:8080/user/login Content-Type: application/x-www-form-urlencoded username=yudaoyuanma&password=123456 <> 2020-12-20T013845.200.json ### GET http://127.0.0.1:8080/user/get-current?full=true Authorization: 1 <> 2020-12-20T013558.500.json ### POST http://127.0.0.1:8080/user/login Content-Type: application/x-www-form-urlencoded username=yudaoyuanma&password=123456 <> 2020-12-20T013552.200.json ### GET http://127.0.0.1:8080/user/get-current?full=true Authorization: token001 <> 2020-12-20T013544.200.json ### GET http://127.0.0.1:8080/user/get-current?full=true Authorization: token001 <> 2020-12-20T012710.200.json ### POST http://127.0.0.1:8080/user/login Content-Type: application/x-www-form-urlencoded username=yudaoyuanma&password=123456 <> 2020-12-20T012708.200.json ### GET http://127.0.0.1:8080/user/get-current?full=true Authorization: {{token_from_server}} <> 2020-12-20T012703.500.json ### GET http://127.0.0.1:8080/user/get-current?full=true Authorization: {{token_from_server}} ### GET http://127.0.0.1:8080/user/get-current?full=true Authorization: token001 <> 2020-12-20T012544.200.json ### GET http://127.0.0.1:8080/user/get-current?full=true <> 2020-12-20T012540.400.json ### GET http://127.0.0.1:8080/user/get-current?full=true Authorization: token001 <> 2020-12-20T012527.200.json ### GET http://127.0.0.1:8080/user/get-current?full=true Authorization: token001 <> 2020-12-20T012510.200.json ### POST http://127.0.0.1:8080/user/login Content-Type: application/x-www-form-urlencoded username=yudaoyuanma&password=123456 <> 2020-12-20T012507.200.json ### GET http://127.0.0.1:8080/user/get-current?full=true Authorization: 1 <> 2020-12-20T011847.500.json ### POST http://127.0.0.1:8080/user/login Content-Type: application/x-www-form-urlencoded username=yudaoyuanma&password=123456 <> 2020-12-20T011843.200.json ### POST http://127.0.0.1:8080/user/login Content-Type: application/x-www-form-urlencoded username=yudaoyuanma&password=123456 <> 2020-12-20T011818.200.json ### GET http://127.0.0.1:8080/user/get-current?full=true Authorization: 0 <> 2020-12-20T011653.500.json ### POST http://127.0.0.1:8080/user/login Content-Type: application/x-www-form-urlencoded username=yudaoyuanma&password=123456 <> 2020-12-20T011650.200.json ### POST http://127.0.0.1:8080/user/login Content-Type: application/x-www-form-urlencoded username=yudaoyuanma&password=123456 <> 2020-12-20T011646.200.json ### GET http://127.0.0.1:8080/user/get-current?full=true Authorization: token001 <> 2020-12-20T011628-1.200.json ### POST http://127.0.0.1:8080/user/login Content-Type: application/x-www-form-urlencoded username=yudaoyuanma&password=123456 <> 2020-12-20T011628.200.json ### GET http://127.0.0.1:8080/user/get-current?full=true Authorization: token001 <> 2020-12-20T011556.200.json ### GET http://127.0.0.1:8080/user/get-current?full=true Authorization: {{token_from_server1}} <> 2020-12-20T011551.500.json ### GET http://127.0.0.1:8080/user/get-current?full=true <> 2020-12-20T011541.400.json ### GET http://127.0.0.1:8080/user/get-current?full=true Authorization: token001 <> 2020-12-20T011530.200.json ### POST http://127.0.0.1:8080/user/login Content-Type: application/x-www-form-urlencoded username=yudaoyuanma&password=123456 <> 2020-12-20T011526.200.json ### GET http://127.0.0.1:8080/user/get-current?full=true Authorization: token002 <> 2020-12-20T011347.500.json ### POST http://127.0.0.1:8080/user/login Content-Type: application/x-www-form-urlencoded username=yudaoyuanma&password=buzhidao <> 2020-12-20T011020.500.json ### POST http://127.0.0.1:8080/user/login Content-Type: application/x-www-form-urlencoded username=yudaoyuanma&password=123456 <> 2020-12-20T010840.200.json ### POST http://127.0.0.1:8080/user/login Content-Type: application/x-www-form-urlencoded username=yudaoyuanma&password=buzhidao <> 2020-12-20T010823.500.json ### POST http://127.0.0.1:8080/user/login Content-Type: application/x-www-form-urlencoded username=yudaoyuanma&password=123456 <> 2020-12-20T010809.200.json ### POST http://127.0.0.1:8080/user/login Content-Type: application/x-www-form-urlencoded username=yudaoyuanma&password=123456 <> 2020-12-20T010738.200.json ### POST http://127.0.0.1:8080/user/login Content-Type: application/x-www-form-urlencoded username=yudaoyuanma&password=123456 <> 2020-12-20T010724.200.json ### POST http://127.0.0.1:8080/user/login Content-Type: application/x-www-form-urlencoded username=yudaoyuanma&password=buzhidao <> 2020-12-20T004813.500.json ### POST http://127.0.0.1:8080/user/login Content-Type: application/x-www-form-urlencoded username=yudaoyuanma&password=123456 <> 2020-12-20T004809.200.json ### POST http://127.0.0.1:8080/user/login Content-Type: application/x-www-form-urlencoded username=yudaoyuanma&password=buzhidao <> 2020-12-20T004645.500.json ### POST http://127.0.0.1:8080/user/login Content-Type: application/x-www-form-urlencoded username=yudaoyuanma&password=buzhidao <> 2020-12-20T004638.500.json ### POST http://127.0.0.1:8080/user/login Content-Type: application/x-www-form-urlencoded username=yudaoyuanma&password=buzhidao <> 2020-12-20T004547.500.json ### POST http://127.0.0.1:8080/user/login Content-Type: application/x-www-form-urlencoded username=yudaoyuanma&password=123456 <> 2020-12-20T004538.200.json ### POST http://127.0.0.1:8080/user/login Content-Type: application/x-www-form-urlencoded username=yudaoyuanma&password=buzhidao <> 2020-12-20T004401.500.json ### POST http://127.0.0.1:8080/user/login Content-Type: application/x-www-form-urlencoded username=yudaoyuanma&password=123456 <> 2020-12-20T004401.200.json ### POST http://127.0.0.1:8080/user/login Content-Type: application/x-www-form-urlencoded username=yudaoyuanma&password=buzhidao <> 2020-12-20T004358.500.json ### POST http://127.0.0.1:8080/user/login Content-Type: application/x-www-form-urlencoded username=yudaoyuanma&password=123456 <> 2020-12-20T004358.200.json ### POST http://127.0.0.1:8080/user/login Content-Type: application/x-www-form-urlencoded username=yudaoyuanma&password=buzhidao <> 2020-12-20T004347.500.json ### POST http://127.0.0.1:8080/user/login Content-Type: application/x-www-form-urlencoded username=yudaoyuanma&password=123456 <> 2020-12-20T004347.200.json ### POST http://127.0.0.1:8080/user/login Content-Type: application/x-www-form-urlencoded username=yudaoyuanma&password=123456 <> 2020-12-20T004337.200.json ### POST http://127.0.0.1:8080/user/login Content-Type: application/x-www-form-urlencoded username=yudaoyuanma&password=buzhidao <> 2020-12-20T004328.500.json ### POST http://127.0.0.1:8080/user/login Content-Type: application/x-www-form-urlencoded username=yudaoyuanma&password=123456 <> 2020-12-20T004250.200.json ### ================================================ FILE: lab-01-spring-security/lab-01-springsecurity-demo/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.10.RELEASE 4.0.0 lab-01-springsecurity-demo org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-security ================================================ FILE: lab-01-spring-security/lab-01-springsecurity-demo/src/main/java/cn/iocoder/springboot/lab01/springsecurity/Application.java ================================================ package cn.iocoder.springboot.lab01.springsecurity; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-01-spring-security/lab-01-springsecurity-demo/src/main/java/cn/iocoder/springboot/lab01/springsecurity/controller/AdminController.java ================================================ package cn.iocoder.springboot.lab01.springsecurity.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/admin") public class AdminController { @GetMapping("/demo") public String demo() { return "示例返回"; } } ================================================ FILE: lab-01-spring-security/lab-01-springsecurity-demo/src/main/resources/application.yaml ================================================ spring: # Spring Security 配置项,对应 SecurityProperties 配置类 security: # 配置默认的 InMemoryUserDetailsManager 的用户账号与密码。 user: name: user # 账号 password: user # 密码 roles: ADMIN # 拥有角色 ================================================ FILE: lab-01-spring-security/lab-01-springsecurity-demo-role/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.10.RELEASE 4.0.0 lab-01-springsecurity-demo-role org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-security ================================================ FILE: lab-01-spring-security/lab-01-springsecurity-demo-role/src/main/java/cn/iocoder/springboot/lab01/springsecurity/Application.java ================================================ package cn.iocoder.springboot.lab01.springsecurity; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-01-spring-security/lab-01-springsecurity-demo-role/src/main/java/cn/iocoder/springboot/lab01/springsecurity/config/SecurityConfig.java ================================================ package cn.iocoder.springboot.lab01.springsecurity.config; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.password.NoOpPasswordEncoder; @Configuration @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http // 配置请求地址的权限 .authorizeRequests() .antMatchers("/test/demo").permitAll() // 所有用户可访问 .antMatchers("/test/admin").hasRole("ADMIN") // 需要 ADMIN 角色 .antMatchers("/test/normal").access("hasRole('ROLE_NORMAL')") // 需要 NORMAL 角色。 // 任何请求,访问的用户都需要经过认证 .anyRequest().authenticated() .and() // 设置 Form 表单登陆 .formLogin() // .loginPage("/login") // 登陆 URL 地址 .permitAll() // 所有用户可访问 .and() // 配置退出相关 .logout() // .logoutUrl("/logout") // 退出 URL 地址 .permitAll(); // 所有用户可访问 } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth. // 使用内存中的 InMemoryUserDetailsManager inMemoryAuthentication() // 不使用 PasswordEncoder 密码编码器 .passwordEncoder(NoOpPasswordEncoder.getInstance()) // 配置 admin 用户 .withUser("admin").password("admin").roles("ADMIN") // 配置 normal 用户 .and().withUser("normal").password("normal").roles("NORMAL"); } } ================================================ FILE: lab-01-spring-security/lab-01-springsecurity-demo-role/src/main/java/cn/iocoder/springboot/lab01/springsecurity/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab01.springsecurity.controller; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.security.PermitAll; @RestController @RequestMapping("/demo") public class DemoController { @PermitAll @GetMapping("/echo") public String demo() { return "示例返回"; } @GetMapping("/home") public String home() { return "我是首页"; } @PreAuthorize("hasRole('ROLE_ADMIN')") @GetMapping("/admin") public String admin() { return "我是管理员"; } @PreAuthorize("hasRole('ROLE_NORMAL')") @GetMapping("/normal") public String normal() { return "我是普通用户"; } } ================================================ FILE: lab-01-spring-security/lab-01-springsecurity-demo-role/src/main/java/cn/iocoder/springboot/lab01/springsecurity/controller/TestController.java ================================================ package cn.iocoder.springboot.lab01.springsecurity.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/test") public class TestController { @GetMapping("/demo") public String demo() { return "示例返回"; } @GetMapping("/home") public String home() { return "我是首页"; } @GetMapping("/admin") public String admin() { return "我是管理员"; } @GetMapping("/normal") public String normal() { return "我是普通用户"; } } ================================================ FILE: lab-01-spring-security/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-02-spring-security pom lab-01-springsecurity-demo lab-01-springsecurity-demo-role ================================================ FILE: lab-01-spring-security/《芋道 Spring Boot 安全框架 Spring Security 入门》.md ================================================ ================================================ FILE: lab-02-spring-security-oauth/authorization-code-server/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 1.5.16.RELEASE 4.0.0 authorization-code-server org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-security org.springframework.security.oauth spring-security-oauth2 ================================================ FILE: lab-02-spring-security-oauth/authorization-code-server/src/main/java/cn/iocoder/springboot/labs/lab01/Application.java ================================================ package cn.iocoder.springboot.labs.lab01; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-02-spring-security-oauth/authorization-code-server/src/main/java/cn/iocoder/springboot/labs/lab01/authorization/OAuth2AuthorizationServer.java ================================================ package cn.iocoder.springboot.labs.lab01.authorization; import org.springframework.context.annotation.Configuration; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; // 授权服务器配置 @Configuration @EnableAuthorizationServer public class OAuth2AuthorizationServer extends AuthorizationServerConfigurerAdapter { @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("clientapp").secret("112233") // Client 账号、密码。 .redirectUris("http://localhost:9001/callback") // 配置回调地址,选填。 .authorizedGrantTypes("authorization_code") // 授权码模式 .scopes("read_userinfo", "read_contacts") // 可授权的 Scope // .and().withClient() // 可以继续配置新的 Client ; } } ================================================ FILE: lab-02-spring-security-oauth/authorization-code-server/src/main/java/cn/iocoder/springboot/labs/lab01/resource/OAuth2ResourceServer.java ================================================ package cn.iocoder.springboot.labs.lab01.resource; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; // 资源服务配置 @Configuration @EnableResourceServer public class OAuth2ResourceServer extends ResourceServerConfigurerAdapter { @Override public void configure(HttpSecurity http) throws Exception { http.authorizeRequests() // 对 "/api/**" 开启认证 .anyRequest() .authenticated() .and() .requestMatchers() .antMatchers("/api/**"); } } // 实际,OAuth2ResourceServer 不是和 OAuth2AuthorizationServer 一起。 // 主要考虑,简化 demo ,所以改成这样。 ================================================ FILE: lab-02-spring-security-oauth/authorization-code-server/src/main/java/cn/iocoder/springboot/labs/lab01/resource/api/ExampleController.java ================================================ package cn.iocoder.springboot.labs.lab01.resource.api; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * 示例模块 Controller */ @RestController @RequestMapping("/api/example") public class ExampleController { @RequestMapping("/hello") public String hello() { return "world"; } } ================================================ FILE: lab-02-spring-security-oauth/authorization-code-server/src/main/resources/application.properties ================================================ # Spring Security Setting security.user.name=yunai security.user.password=1024 ================================================ FILE: lab-02-spring-security-oauth/client-credentials-server/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 1.5.16.RELEASE 4.0.0 client-credentials-server org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-security org.springframework.security.oauth spring-security-oauth2 ================================================ FILE: lab-02-spring-security-oauth/client-credentials-server/src/main/java/lab01/Application.java ================================================ package lab01; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-02-spring-security-oauth/client-credentials-server/src/main/java/lab01/authorization/OAuth2AuthorizationServer.java ================================================ package lab01.authorization; import org.springframework.context.annotation.Configuration; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; // 授权服务器配置 @Configuration @EnableAuthorizationServer public class OAuth2AuthorizationServer extends AuthorizationServerConfigurerAdapter { @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("clientapp").secret("112233") // Client 账号、密码。 .authorizedGrantTypes("client_credentials") // 授权码模式 .scopes("read_userinfo", "read_contacts") // 可授权的 Scope // .and().withClient() // 可以继续配置新的 Client ; } } ================================================ FILE: lab-02-spring-security-oauth/client-credentials-server/src/main/java/lab01/resource/OAuth2ResourceServer.java ================================================ package lab01.resource; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; // 资源服务配置 @Configuration @EnableResourceServer public class OAuth2ResourceServer extends ResourceServerConfigurerAdapter { @Override public void configure(HttpSecurity http) throws Exception { http.authorizeRequests() // 对 "/api/**" 开启认证 .anyRequest() .authenticated() .and() .requestMatchers() .antMatchers("/api/**"); } } // 实际,OAuth2ResourceServer 不是和 OAuth2AuthorizationServer 一起。 // 主要考虑,简化 demo ,所以改成这样。 ================================================ FILE: lab-02-spring-security-oauth/client-credentials-server/src/main/java/lab01/resource/api/ExampleController.java ================================================ package lab01.resource.api; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * 示例模块 Controller */ @RestController @RequestMapping("/api/example") public class ExampleController { @RequestMapping("/hello") public String hello() { return "world"; } } ================================================ FILE: lab-02-spring-security-oauth/implicit-server/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 1.5.16.RELEASE 4.0.0 implicit-server org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-security org.springframework.security.oauth spring-security-oauth2 ================================================ FILE: lab-02-spring-security-oauth/implicit-server/src/main/java/lab01/Application.java ================================================ package lab01; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-02-spring-security-oauth/implicit-server/src/main/java/lab01/authorization/OAuth2AuthorizationServer.java ================================================ package lab01.authorization; import org.springframework.context.annotation.Configuration; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; // 授权服务器配置 @Configuration @EnableAuthorizationServer public class OAuth2AuthorizationServer extends AuthorizationServerConfigurerAdapter { @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("clientapp").secret("112233") // Client 账号、密码。 .redirectUris("http://localhost:9001/callback") // 配置回调地址,选填。 .authorizedGrantTypes("implicit") // 授权码模式 .scopes("read_userinfo", "read_contacts") // 可授权的 Scope // .and().withClient() // 可以继续配置新的 Client ; } } ================================================ FILE: lab-02-spring-security-oauth/implicit-server/src/main/java/lab01/resource/OAuth2ResourceServer.java ================================================ package lab01.resource; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; // 资源服务配置 @Configuration @EnableResourceServer public class OAuth2ResourceServer extends ResourceServerConfigurerAdapter { @Override public void configure(HttpSecurity http) throws Exception { http.authorizeRequests() // 对 "/api/**" 开启认证 .anyRequest() .authenticated() .and() .requestMatchers() .antMatchers("/api/**") // .and() // .cors() ; } } // 实际,OAuth2ResourceServer 不是和 OAuth2AuthorizationServer 一起。 // 主要考虑,简化 demo ,所以改成这样。 ================================================ FILE: lab-02-spring-security-oauth/implicit-server/src/main/java/lab01/resource/api/ExampleController.java ================================================ package lab01.resource.api; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * 示例模块 Controller */ @RestController @RequestMapping("/api/example") public class ExampleController { // @CrossOrigin @RequestMapping("/hello") public String hello() { return "world"; } } ================================================ FILE: lab-02-spring-security-oauth/implicit-server/src/main/resources/application.properties ================================================ # Spring Security Setting security.user.name=yunai security.user.password=1024 ================================================ FILE: lab-02-spring-security-oauth/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-02-spring-security-oauth pom authorization-code-server resource-owner-password-credentials-server implicit-server client-credentials-server resource-owner-password-credentials-server-with-refresh-token resource-owner-password-credentials-server-with-revoke-token resource-owner-password-credentials-server-by-jdbc-token-store ================================================ FILE: lab-02-spring-security-oauth/resource-owner-password-credentials-server/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 1.5.16.RELEASE 4.0.0 resource-owner-password-credentials-server org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-security org.springframework.security.oauth spring-security-oauth2 ================================================ FILE: lab-02-spring-security-oauth/resource-owner-password-credentials-server/src/main/java/lab01/Application.java ================================================ package lab01; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-02-spring-security-oauth/resource-owner-password-credentials-server/src/main/java/lab01/authorization/OAuth2AuthorizationServer.java ================================================ package lab01.authorization; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; // 授权服务器配置 @Configuration @EnableAuthorizationServer public class OAuth2AuthorizationServer extends AuthorizationServerConfigurerAdapter { // 用户认证 @Autowired private AuthenticationManager authenticationManager; @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.authenticationManager(authenticationManager); } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("clientapp").secret("112233") // Client 账号、密码。 .authorizedGrantTypes("password") // 密码模式 .scopes("read_userinfo", "read_contacts") // 可授权的 Scope // .and().withClient() // 可以继续配置新的 Client ; } } ================================================ FILE: lab-02-spring-security-oauth/resource-owner-password-credentials-server/src/main/java/lab01/resource/OAuth2ResourceServer.java ================================================ package lab01.resource; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; // 资源服务配置 @Configuration @EnableResourceServer public class OAuth2ResourceServer extends ResourceServerConfigurerAdapter { @Override public void configure(HttpSecurity http) throws Exception { http.authorizeRequests() // 对 "/api/**" 开启认证 .anyRequest() .authenticated() .and() .requestMatchers() .antMatchers("/api/**"); } } // 实际,OAuth2ResourceServer 不是和 OAuth2AuthorizationServer 一起。 // 主要考虑,简化 demo ,所以改成这样。 ================================================ FILE: lab-02-spring-security-oauth/resource-owner-password-credentials-server/src/main/java/lab01/resource/api/ExampleController.java ================================================ package lab01.resource.api; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * 示例模块 Controller */ @RestController @RequestMapping("/api/example") public class ExampleController { @RequestMapping("/hello") public String hello() { return "world"; } } ================================================ FILE: lab-02-spring-security-oauth/resource-owner-password-credentials-server/src/main/resources/application.properties ================================================ # Spring Security Setting security.user.name=yunai security.user.password=1024 ================================================ FILE: lab-02-spring-security-oauth/resource-owner-password-credentials-server-by-jdbc-token-store/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 1.5.16.RELEASE 4.0.0 resource-owner-password-credentials-server-by-jdbc-token-store org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-security org.springframework.security.oauth spring-security-oauth2 org.springframework spring-jdbc mysql mysql-connector-java runtime ================================================ FILE: lab-02-spring-security-oauth/resource-owner-password-credentials-server-by-jdbc-token-store/src/main/java/lab02/Application.java ================================================ package lab02; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-02-spring-security-oauth/resource-owner-password-credentials-server-by-jdbc-token-store/src/main/java/lab02/authorization/OAuth2AuthorizationServer.java ================================================ package lab02.authorization; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import org.springframework.jdbc.datasource.DriverManagerDataSource; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore; import javax.sql.DataSource; // 授权服务器配置 @Configuration @EnableAuthorizationServer public class OAuth2AuthorizationServer extends AuthorizationServerConfigurerAdapter { @Autowired private Environment env; // 用户认证 @Autowired private AuthenticationManager authenticationManager; @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.tokenStore(tokenStore()) // 设置 tokenStore .authenticationManager(authenticationManager); // 设置 authenticationManager } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { // clients.inMemory() // .withClient("clientapp").secret("112233") // Client 账号、密码。 // .authorizedGrantTypes("password") // 密码模式 // .scopes("read_userinfo", "read_contacts") // 可授权的 Scope //// .and().withClient() // 可以继续配置新的 Client // ; // 加载 ClientDetails clients.jdbc(dataSource()); } @Bean public DataSource dataSource() { final DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName(env.getProperty("jdbc.driverClassName")); dataSource.setUrl(env.getProperty("jdbc.url")); dataSource.setUsername(env.getProperty("jdbc.user")); dataSource.setPassword(env.getProperty("jdbc.pass")); return dataSource; } @Bean public TokenStore tokenStore() { return new JdbcTokenStore(dataSource()); } } ================================================ FILE: lab-02-spring-security-oauth/resource-owner-password-credentials-server-by-jdbc-token-store/src/main/java/lab02/resource/OAuth2ResourceServer.java ================================================ package lab02.resource; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; // 资源服务配置 @Configuration @EnableResourceServer public class OAuth2ResourceServer extends ResourceServerConfigurerAdapter { @Override public void configure(HttpSecurity http) throws Exception { http.authorizeRequests() // 对 "/api/**" 开启认证 .anyRequest() .authenticated() .and() .requestMatchers() .antMatchers("/api/**"); } } // 实际,OAuth2ResourceServer 不是和 OAuth2AuthorizationServer 一起。 // 主要考虑,简化 demo ,所以改成这样。 ================================================ FILE: lab-02-spring-security-oauth/resource-owner-password-credentials-server-by-jdbc-token-store/src/main/java/lab02/resource/api/ExampleController.java ================================================ package lab02.resource.api; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * 示例模块 Controller */ @RestController @RequestMapping("/api/example") public class ExampleController { @RequestMapping("/hello") public String hello() { return "world"; } } ================================================ FILE: lab-02-spring-security-oauth/resource-owner-password-credentials-server-by-jdbc-token-store/src/main/resources/application.properties ================================================ # Spring Security Setting security.user.name=yunai security.user.password=1024 ##################### MySQL ##################### jdbc.driverClassName=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://127.0.0.1:33061/oauth2 jdbc.user=root jdbc.pass=123456 ================================================ FILE: lab-02-spring-security-oauth/resource-owner-password-credentials-server-by-jdbc-token-store/src/main/resources/data.sql ================================================ INSERT INTO oauth_client_details (client_id, client_secret, scope, authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, refresh_token_validity, additional_information, autoapprove) VALUES ('fooClientIdPassword', 'secret', 'foo,read,write', 'password,authorization_code,refresh_token', null, null, 36000, 36000, null, true); INSERT INTO oauth_client_details (client_id, client_secret, scope, authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, refresh_token_validity, additional_information, autoapprove) VALUES ('sampleClientId', 'secret', 'read,write,foo,bar', 'implicit', null, null, 36000, 36000, null, false); INSERT INTO oauth_client_details (client_id, client_secret, scope, authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, refresh_token_validity, additional_information, autoapprove) VALUES ('barClientIdPassword', 'secret', 'bar,read,write', 'password,authorization_code,refresh_token', null, null, 36000, 36000, null, true); ================================================ FILE: lab-02-spring-security-oauth/resource-owner-password-credentials-server-by-jdbc-token-store/src/main/resources/schema.sql ================================================ --------------- MySQL --------------- drop table if exists oauth_client_details; create table oauth_client_details ( client_id VARCHAR(255) PRIMARY KEY, resource_ids VARCHAR(255), client_secret VARCHAR(255), scope VARCHAR(255), authorized_grant_types VARCHAR(255), web_server_redirect_uri VARCHAR(255), authorities VARCHAR(255), access_token_validity INTEGER, refresh_token_validity INTEGER, additional_information VARCHAR(4096), autoapprove VARCHAR(255) ); create table if not exists oauth_client_token ( token_id VARCHAR(255), token LONG VARBINARY, authentication_id VARCHAR(255) PRIMARY KEY, user_name VARCHAR(255), client_id VARCHAR(255) ); create table if not exists oauth_access_token ( token_id VARCHAR(255), token LONG VARBINARY, authentication_id VARCHAR(255) PRIMARY KEY, user_name VARCHAR(255), client_id VARCHAR(255), authentication LONG VARBINARY, refresh_token VARCHAR(255) ); create table if not exists oauth_refresh_token ( token_id VARCHAR(255), token LONG VARBINARY, authentication LONG VARBINARY ); create table if not exists oauth_code ( code VARCHAR(255), authentication LONG VARBINARY ); create table if not exists oauth_approvals ( userId VARCHAR(255), clientId VARCHAR(255), scope VARCHAR(255), status VARCHAR(10), expiresAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, lastModifiedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); create table if not exists ClientDetails ( appId VARCHAR(255) PRIMARY KEY, resourceIds VARCHAR(255), appSecret VARCHAR(255), scope VARCHAR(255), grantTypes VARCHAR(255), redirectUrl VARCHAR(255), authorities VARCHAR(255), access_token_validity INTEGER, refresh_token_validity INTEGER, additionalInformation VARCHAR(4096), autoApproveScopes VARCHAR(255) ); ================================================ FILE: lab-02-spring-security-oauth/resource-owner-password-credentials-server-with-refresh-token/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 1.5.16.RELEASE 4.0.0 resource-owner-password-credentials-server-with-refresh-token org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-security org.springframework.security.oauth spring-security-oauth2 ================================================ FILE: lab-02-spring-security-oauth/resource-owner-password-credentials-server-with-refresh-token/src/main/java/lab01/Application.java ================================================ package lab01; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-02-spring-security-oauth/resource-owner-password-credentials-server-with-refresh-token/src/main/java/lab01/authorization/OAuth2AuthorizationServer.java ================================================ package lab01.authorization; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; // 授权服务器配置 @Configuration @EnableAuthorizationServer public class OAuth2AuthorizationServer extends AuthorizationServerConfigurerAdapter { // 用户认证 @Autowired private AuthenticationManager authenticationManager; @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.authenticationManager(authenticationManager); } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("clientapp").secret("112233") // Client 账号、密码。 .authorizedGrantTypes("password", "refresh_token") // 密码模式 .scopes("read_userinfo", "read_contacts") // 可授权的 Scope .refreshTokenValiditySeconds(1200) // 1200 秒过期 // .and().withClient() // 可以继续配置新的 Client ; } } ================================================ FILE: lab-02-spring-security-oauth/resource-owner-password-credentials-server-with-refresh-token/src/main/java/lab01/resource/OAuth2ResourceServer.java ================================================ package lab01.resource; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; // 资源服务配置 @Configuration @EnableResourceServer public class OAuth2ResourceServer extends ResourceServerConfigurerAdapter { @Override public void configure(HttpSecurity http) throws Exception { http.authorizeRequests() // 对 "/api/**" 开启认证 .anyRequest() .authenticated() .and() .requestMatchers() .antMatchers("/api/**"); } } // 实际,OAuth2ResourceServer 不是和 OAuth2AuthorizationServer 一起。 // 主要考虑,简化 demo ,所以改成这样。 ================================================ FILE: lab-02-spring-security-oauth/resource-owner-password-credentials-server-with-refresh-token/src/main/java/lab01/resource/api/ExampleController.java ================================================ package lab01.resource.api; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * 示例模块 Controller */ @RestController @RequestMapping("/api/example") public class ExampleController { @RequestMapping("/hello") public String hello() { return "world"; } } ================================================ FILE: lab-02-spring-security-oauth/resource-owner-password-credentials-server-with-refresh-token/src/main/resources/application.properties ================================================ # Spring Security Setting security.user.name=yunai security.user.password=1024 ================================================ FILE: lab-02-spring-security-oauth/resource-owner-password-credentials-server-with-revoke-token/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 1.5.16.RELEASE 4.0.0 resource-owner-password-credentials-server-with-revoke-token org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-security org.springframework.security.oauth spring-security-oauth2 ================================================ FILE: lab-02-spring-security-oauth/resource-owner-password-credentials-server-with-revoke-token/src/main/java/lab2/Application.java ================================================ package lab2; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-02-spring-security-oauth/resource-owner-password-credentials-server-with-revoke-token/src/main/java/lab2/authorization/OAuth2AuthorizationServer.java ================================================ package lab2.authorization; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; // 授权服务器配置 @Configuration @EnableAuthorizationServer public class OAuth2AuthorizationServer extends AuthorizationServerConfigurerAdapter { // 用户认证 @Autowired private AuthenticationManager authenticationManager; @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.authenticationManager(authenticationManager); } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("clientapp").secret("112233") // Client 账号、密码。 .authorizedGrantTypes("password", "refresh_token") // 密码模式 .scopes("read_userinfo", "read_contacts") // 可授权的 Scope .refreshTokenValiditySeconds(1200) // 1200 秒过期 // .and().withClient() // 可以继续配置新的 Client ; } } ================================================ FILE: lab-02-spring-security-oauth/resource-owner-password-credentials-server-with-revoke-token/src/main/java/lab2/authorization/token/TokenController.java ================================================ package lab2.authorization.token; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.oauth2.common.DefaultOAuth2RefreshToken; import org.springframework.security.oauth2.provider.token.ConsumerTokenServices; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class TokenController { @Autowired private ConsumerTokenServices tokenServices; @Autowired(required = false) private TokenStore tokenStore; @RequestMapping(method = RequestMethod.POST, value = "api/access_token/revoke") public String revokeToken(@RequestParam("token") String token) { tokenServices.revokeToken(token); return token; } @RequestMapping(method = RequestMethod.POST, value = "api/refresh_token/revoke") public String revokeRefreshToken(@RequestParam("token") String token) { tokenStore.removeRefreshToken(new DefaultOAuth2RefreshToken(token)); return token; } } ================================================ FILE: lab-02-spring-security-oauth/resource-owner-password-credentials-server-with-revoke-token/src/main/java/lab2/resource/OAuth2ResourceServer.java ================================================ package lab2.resource; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; // 资源服务配置 @Configuration @EnableResourceServer public class OAuth2ResourceServer extends ResourceServerConfigurerAdapter { @Override public void configure(HttpSecurity http) throws Exception { http.authorizeRequests() // 对 "/api/**" 开启认证 .anyRequest() .authenticated() .and() .requestMatchers() .antMatchers("/api/**"); } } // 实际,OAuth2ResourceServer 不是和 OAuth2AuthorizationServer 一起。 // 主要考虑,简化 demo ,所以改成这样。 ================================================ FILE: lab-02-spring-security-oauth/resource-owner-password-credentials-server-with-revoke-token/src/main/java/lab2/resource/api/ExampleController.java ================================================ package lab2.resource.api; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * 示例模块 Controller */ @RestController @RequestMapping("/api/example") public class ExampleController { @RequestMapping("/hello") public String hello() { return "world"; } } ================================================ FILE: lab-02-spring-security-oauth/resource-owner-password-credentials-server-with-revoke-token/src/main/resources/application.properties ================================================ # Spring Security Setting security.user.name=yunai security.user.password=1024 ================================================ FILE: lab-02-spring-security-oauth/《芋道 Spring Security OAuth2 入门(新)》.md ================================================ 对应 [lab-68](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-68) 示例。 ================================================ FILE: lab-02-spring-security-oauth/《芋道 Spring Security OAuth2 入门(老)》.md ================================================ ================================================ FILE: lab-03-kafka/lab-03-kafka-demo/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-03-kafka-demo org.springframework.kafka spring-kafka 2.3.3.RELEASE org.springframework.boot spring-boot-starter-json org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-03-kafka/lab-03-kafka-demo/src/main/java/cn/iocoder/springboot/lab03/kafkademo/Application.java ================================================ package cn.iocoder.springboot.lab03.kafkademo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-03-kafka/lab-03-kafka-demo/src/main/java/cn/iocoder/springboot/lab03/kafkademo/config/KafkaConfiguration.java ================================================ package cn.iocoder.springboot.lab03.kafkademo.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.kafka.core.KafkaTemplate; import org.springframework.kafka.listener.*; import org.springframework.util.backoff.BackOff; import org.springframework.util.backoff.FixedBackOff; @Configuration public class KafkaConfiguration { @Bean @Primary public ErrorHandler kafkaErrorHandler(KafkaTemplate template) { // 创建 DeadLetterPublishingRecoverer 对象 ConsumerRecordRecoverer recoverer = new DeadLetterPublishingRecoverer(template); // 创建 FixedBackOff 对象 BackOff backOff = new FixedBackOff(10 * 1000L, 3L); // 创建 SeekToCurrentErrorHandler 对象 return new SeekToCurrentErrorHandler(recoverer, backOff); } // @Bean // @Primary // public BatchErrorHandler kafkaBatchErrorHandler() { // // 创建 SeekToCurrentBatchErrorHandler 对象 // SeekToCurrentBatchErrorHandler batchErrorHandler = new SeekToCurrentBatchErrorHandler(); // // 创建 FixedBackOff 对象 // BackOff backOff = new FixedBackOff(10 * 1000L, 3L); // batchErrorHandler.setBackOff(backOff); // // 返回 // return batchErrorHandler; // } } ================================================ FILE: lab-03-kafka/lab-03-kafka-demo/src/main/java/cn/iocoder/springboot/lab03/kafkademo/consumer/Demo01AConsumer.java ================================================ package cn.iocoder.springboot.lab03.kafkademo.consumer; import cn.iocoder.springboot.lab03.kafkademo.message.Demo01Message; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.kafka.annotation.KafkaListener; import org.springframework.stereotype.Component; @Component public class Demo01AConsumer { private Logger logger = LoggerFactory.getLogger(getClass()); @KafkaListener(topics = Demo01Message.TOPIC, groupId = "demo01-A-consumer-group-" + Demo01Message.TOPIC) public void onMessage(ConsumerRecord record) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), record); } // @KafkaListener(topics = Demo01Message.TOPIC, // groupId = "demo01-B-consumer-group-" + Demo01Message.TOPIC) // public void onMessage(ConsumerRecord record) throws InterruptedException { // logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), record.partition()); // Thread.sleep(10 * 1000L); // Thread.sleep(1L); // logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), record.partition()); // } } ================================================ FILE: lab-03-kafka/lab-03-kafka-demo/src/main/java/cn/iocoder/springboot/lab03/kafkademo/consumer/Demo01Consumer.java ================================================ package cn.iocoder.springboot.lab03.kafkademo.consumer; import cn.iocoder.springboot.lab03.kafkademo.message.Demo01Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.kafka.annotation.KafkaListener; import org.springframework.stereotype.Component; @Component public class Demo01Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @KafkaListener(topics = Demo01Message.TOPIC, groupId = "demo01-consumer-group-" + Demo01Message.TOPIC) public void onMessage(Demo01Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: lab-03-kafka/lab-03-kafka-demo/src/main/java/cn/iocoder/springboot/lab03/kafkademo/consumer/Demo04Consumer.java ================================================ package cn.iocoder.springboot.lab03.kafkademo.consumer; import cn.iocoder.springboot.lab03.kafkademo.message.Demo04Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.kafka.annotation.KafkaListener; import org.springframework.stereotype.Component; import java.util.concurrent.atomic.AtomicInteger; @Component public class Demo04Consumer { private AtomicInteger count = new AtomicInteger(0); private Logger logger = LoggerFactory.getLogger(getClass()); @KafkaListener(topics = Demo04Message.TOPIC, groupId = "demo04-consumer-group-" + Demo04Message.TOPIC) public void onMessage(Demo04Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); // 注意,此处抛出一个 RuntimeException 异常,模拟消费失败 throw new RuntimeException("我就是故意抛出一个异常"); } } ================================================ FILE: lab-03-kafka/lab-03-kafka-demo/src/main/java/cn/iocoder/springboot/lab03/kafkademo/message/Demo01Message.java ================================================ package cn.iocoder.springboot.lab03.kafkademo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { public static final String TOPIC = "DEMO_01"; /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: lab-03-kafka/lab-03-kafka-demo/src/main/java/cn/iocoder/springboot/lab03/kafkademo/message/Demo04Message.java ================================================ package cn.iocoder.springboot.lab03.kafkademo.message; /** * 示例 04 的 Message 消息 */ public class Demo04Message { public static final String TOPIC = "DEMO_04"; /** * 编号 */ private Integer id; public Demo04Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo04Message{" + "id=" + id + '}'; } } ================================================ FILE: lab-03-kafka/lab-03-kafka-demo/src/main/java/cn/iocoder/springboot/lab03/kafkademo/producer/Demo01Producer.java ================================================ package cn.iocoder.springboot.lab03.kafkademo.producer; import cn.iocoder.springboot.lab03.kafkademo.message.Demo01Message; import org.springframework.kafka.core.KafkaTemplate; import org.springframework.kafka.support.SendResult; import org.springframework.stereotype.Component; import org.springframework.util.concurrent.ListenableFuture; import javax.annotation.Resource; import java.util.concurrent.ExecutionException; @Component public class Demo01Producer { @Resource private KafkaTemplate kafkaTemplate; public SendResult syncSend(Integer id) throws ExecutionException, InterruptedException { // 创建 Demo01Message 消息 Demo01Message message = new Demo01Message(); message.setId(id); // 同步发送消息 return kafkaTemplate.send(Demo01Message.TOPIC, message).get(); } public ListenableFuture> asyncSend(Integer id) { // 创建 Demo01Message 消息 Demo01Message message = new Demo01Message(); message.setId(id); // 异步发送消息 return kafkaTemplate.send(Demo01Message.TOPIC, message); } } ================================================ FILE: lab-03-kafka/lab-03-kafka-demo/src/main/java/cn/iocoder/springboot/lab03/kafkademo/producer/Demo04Producer.java ================================================ package cn.iocoder.springboot.lab03.kafkademo.producer; import cn.iocoder.springboot.lab03.kafkademo.message.Demo04Message; import org.springframework.kafka.core.KafkaTemplate; import org.springframework.kafka.support.SendResult; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.util.concurrent.ExecutionException; @Component public class Demo04Producer { @Resource private KafkaTemplate kafkaTemplate; public SendResult syncSend(Integer id) throws ExecutionException, InterruptedException { // 创建 Demo04Message 消息 Demo04Message message = new Demo04Message(); message.setId(id); // 同步发送消息 return kafkaTemplate.send(Demo04Message.TOPIC, message).get(); } } ================================================ FILE: lab-03-kafka/lab-03-kafka-demo/src/main/resources/application.yaml ================================================ spring: # Kafka 配置项,对应 KafkaProperties 配置类 kafka: bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 # Kafka Producer 配置项 producer: acks: 1 # 0-不应答。1-leader 应答。all-所有 leader 和 follower 应答。 retries: 3 # 发送失败时,重试发送的次数 key-serializer: org.apache.kafka.common.serialization.StringSerializer # 消息的 key 的序列化 value-serializer: org.springframework.kafka.support.serializer.JsonSerializer # 消息的 value 的序列化 # Kafka Consumer 配置项 consumer: auto-offset-reset: earliest # 设置消费者分组最初的消费进度为 earliest 。可参考博客 https://blog.csdn.net/lishuangzhe7047/article/details/74530417 理解 key-deserializer: org.apache.kafka.common.serialization.StringDeserializer value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer properties: spring: json: trusted: packages: cn.iocoder.springboot.lab03.kafkademo.message # Kafka Consumer Listener 监听器配置 listener: missing-topics-fatal: false # 消费监听接口监听的主题不存在时,默认会报错。所以通过设置为 false ,解决报错 logging: level: org: springframework: kafka: ERROR # spring-kafka INFO 日志太多了,所以我们限制只打印 ERROR 级别 apache: kafka: ERROR # kafka INFO 日志太多了,所以我们限制只打印 ERROR 级别 ================================================ FILE: lab-03-kafka/lab-03-kafka-demo/src/main/resources/application_bak.yaml ================================================ spring: kafka: bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 # Kafka Producer 配置项 producer: acks: 1 # 0-不应答。1-leader 应答。all-所有 leader 和 follower 应答。 retries: 3 # 发送失败时,重试发送的次数 batch-size: 16384 # 每次批量发送消息的最大数量 buffer-memory: 33554432 # 每次批量发送消息的最大内存 key-serializer: org.apache.kafka.common.serialization.StringSerializer # 消息的 key 的序列化 value-serializer: org.springframework.kafka.support.serializer.JsonSerializer # 消息的 value 的序列化 # Kafka Consumer 配置项 consumer: enable-auto-commit: false # true-使用 kafka 默认自带的提交模式。false-使用 Spring-Kafka 的自动提交 offset 机制。建议设置为 false 使用 kafka-spring 的机制,分析见 https://juejin.im/entry/5a6e8dea518825732472710c 。 auto-commit-interval: 1000 # 在开启 enable-auto-commit 时,自动提交消费进度频率,单位:毫秒。如果 enable-auto-commit 为 false 时候,可以不设置 auto-offset-reset: earliest # 设置消费者分组最初的消费进度为 earliest 。可参考博客 https://blog.csdn.net/lishuangzhe7047/article/details/74530417 理解 key-deserializer: org.apache.kafka.common.serialization.StringDeserializer value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer properties: spring: json: trusted: packages: cn.iocoder.springboot.lab03.kafkademo.message # Kafka Consumer Listener 监听器配置 listener: concurrency: 10 # 每个消费者监听器最大并发数,默认为 1 。可以通过设置 n ,这样对于每个监听器就会使用 n 个线程消费消息,提高整体消费速度。详细可参考博客 https://www.jianshu.com/p/ad0e5424edbd 理解。 logging: level: org: springframework: kafka: ERROR # spring-kafka INFO 日志太多了,所以我们限制只打印 ERROR 级别 apache: kafka: ERROR # kafka INFO 日志太多了,所以我们限制只打印 ERROR 级别 ================================================ FILE: lab-03-kafka/lab-03-kafka-demo/src/test/java/cn/iocoder/springboot/lab03/kafkademo/package-info.java ================================================ package cn.iocoder.springboot.lab03.kafkademo; ================================================ FILE: lab-03-kafka/lab-03-kafka-demo/src/test/java/cn/iocoder/springboot/lab03/kafkademo/producer/Demo01ProducerTest.java ================================================ package cn.iocoder.springboot.lab03.kafkademo.producer; import cn.iocoder.springboot.lab03.kafkademo.Application; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.kafka.support.SendResult; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.util.concurrent.ListenableFutureCallback; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class Demo01ProducerTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private Demo01Producer producer; @Test public void testSyncSend() throws ExecutionException, InterruptedException { int id = (int) (System.currentTimeMillis() / 1000); SendResult result = producer.syncSend(id); logger.info("[testSyncSend][发送编号:[{}] 发送结果:[{}]]", id, result); // 阻塞等待,保证消费 new CountDownLatch(1).await(); } @Test public void testASyncSend() throws InterruptedException { int id = (int) (System.currentTimeMillis() / 1000); producer.asyncSend(id).addCallback(new ListenableFutureCallback>() { @Override public void onFailure(Throwable e) { logger.info("[testASyncSend][发送编号:[{}] 发送异常]]", id, e); } @Override public void onSuccess(SendResult result) { logger.info("[testASyncSend][发送编号:[{}] 发送成功,结果为:[{}]]", id, result); } }); // 阻塞等待,保证消费 new CountDownLatch(1).await(); } // // @Test // public void testOnewaySend() throws InterruptedException { // int id = (int) (System.currentTimeMillis() / 1000); // producer.onewaySend(id); // logger.info("[testOnewaySend][发送编号:[{}] 发送完成]", id); // // // 阻塞等待,保证消费 // new CountDownLatch(1).await(); // } // @Test // public void testSyncSendMore() throws ExecutionException, InterruptedException { // for (int i = 0; i < 1000; i++) { // int id = (int) (System.currentTimeMillis() / 1000); // SendResult result = producer.syncSend(id); // logger.info("[testSyncSend][发送编号:[{}] 发送结果:[{}]]", id, result); // Thread.sleep(10); // } // // // 阻塞等待,保证消费 // new CountDownLatch(1).await(); // } // // @Test // public void block() throws InterruptedException { // // 阻塞等待,保证消费 // new CountDownLatch(1).await(); // } } ================================================ FILE: lab-03-kafka/lab-03-kafka-demo/src/test/java/cn/iocoder/springboot/lab03/kafkademo/producer/Demo04ProducerTest.java ================================================ package cn.iocoder.springboot.lab03.kafkademo.producer; import cn.iocoder.springboot.lab03.kafkademo.Application; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.kafka.support.SendResult; import org.springframework.test.context.junit4.SpringRunner; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class Demo04ProducerTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private Demo04Producer producer; @Test public void testSyncSend() throws ExecutionException, InterruptedException { int id = (int) (System.currentTimeMillis() / 1000); SendResult result = producer.syncSend(id); logger.info("[testSyncSend][发送编号:[{}] 发送结果:[{}]]", id, result); // 阻塞等待,保证消费 new CountDownLatch(1).await(); } @Test public void testSyncSendX() throws ExecutionException, InterruptedException { for (int i = 0; i < 100; i++) { int id = (int) (System.currentTimeMillis() / 1000); SendResult result = producer.syncSend(id); logger.info("[testSyncSend][发送编号:[{}] 发送结果:[{}]]", id, result); Thread.sleep(10 * 1000L); } // 阻塞等待,保证消费 new CountDownLatch(1).await(); } } ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-ack/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-03-kafka-demo-ack org.springframework.kafka spring-kafka 2.3.3.RELEASE org.springframework.boot spring-boot-starter-json org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-ack/src/main/java/cn/iocoder/springboot/lab03/kafkademo/Application.java ================================================ package cn.iocoder.springboot.lab03.kafkademo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-ack/src/main/java/cn/iocoder/springboot/lab03/kafkademo/consumer/Demo08Consumer.java ================================================ package cn.iocoder.springboot.lab03.kafkademo.consumer; import cn.iocoder.springboot.lab03.kafkademo.message.Demo08Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.kafka.annotation.KafkaListener; import org.springframework.kafka.support.Acknowledgment; import org.springframework.stereotype.Component; @Component public class Demo08Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @KafkaListener(topics = Demo08Message.TOPIC, groupId = "demo08-consumer-group-" + Demo08Message.TOPIC) public void onMessage(Demo08Message message, Acknowledgment acknowledgment) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); // 提交消费进度 if (message.getId() % 2 == 1) { acknowledgment.acknowledge(); } } } ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-ack/src/main/java/cn/iocoder/springboot/lab03/kafkademo/message/Demo08Message.java ================================================ package cn.iocoder.springboot.lab03.kafkademo.message; /** * 示例 08 的 Message 消息 */ public class Demo08Message { public static final String TOPIC = "DEMO_08"; /** * 编号 */ private Integer id; public Demo08Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo08Message{" + "id=" + id + '}'; } } ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-ack/src/main/java/cn/iocoder/springboot/lab03/kafkademo/producer/Demo08Producer.java ================================================ package cn.iocoder.springboot.lab03.kafkademo.producer; import cn.iocoder.springboot.lab03.kafkademo.message.Demo08Message; import org.springframework.kafka.core.KafkaTemplate; import org.springframework.kafka.support.SendResult; import org.springframework.stereotype.Component; import org.springframework.util.concurrent.ListenableFuture; import javax.annotation.Resource; import java.util.concurrent.ExecutionException; @Component public class Demo08Producer { @Resource private KafkaTemplate kafkaTemplate; public SendResult syncSend(Integer id) throws ExecutionException, InterruptedException { // 创建 Demo08Message 消息 Demo08Message message = new Demo08Message(); message.setId(id); // 同步发送消息 return kafkaTemplate.send(Demo08Message.TOPIC, message).get(); } } ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-ack/src/main/resources/application.yaml ================================================ spring: # Kafka 配置项,对应 KafkaProperties 配置类 kafka: bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 # Kafka Producer 配置项 producer: acks: 1 # 0-不应答。1-leader 应答。all-所有 leader 和 follower 应答。 retries: 3 # 发送失败时,重试发送的次数 key-serializer: org.apache.kafka.common.serialization.StringSerializer # 消息的 key 的序列化 value-serializer: org.springframework.kafka.support.serializer.JsonSerializer # 消息的 value 的序列化 # Kafka Consumer 配置项 consumer: auto-offset-reset: earliest # 设置消费者分组最初的消费进度为 earliest 。可参考博客 https://blog.csdn.net/lishuangzhe7047/article/details/74530417 理解 key-deserializer: org.apache.kafka.common.serialization.StringDeserializer value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer enable-auto-commit: false # 使用 Spring-Kafka 的消费进度的提交机制 properties: spring: json: trusted: packages: cn.iocoder.springboot.lab03.kafkademo.message # Kafka Consumer Listener 监听器配置 listener: missing-topics-fatal: false # 消费监听接口监听的主题不存在时,默认会报错。所以通过设置为 false ,解决报错 ack-mode: MANUAL logging: level: org: springframework: kafka: ERROR # spring-kafka INFO 日志太多了,所以我们限制只打印 ERROR 级别 apache: kafka: ERROR # kafka INFO 日志太多了,所以我们限制只打印 ERROR 级别 ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-ack/src/test/java/cn/iocoder/springboot/lab03/kafkademo/package-info.java ================================================ package cn.iocoder.springboot.lab03.kafkademo; ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-ack/src/test/java/cn/iocoder/springboot/lab03/kafkademo/producer/Demo08ProducerTest.java ================================================ package cn.iocoder.springboot.lab03.kafkademo.producer; import cn.iocoder.springboot.lab03.kafkademo.Application; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.kafka.support.SendResult; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.util.concurrent.ListenableFutureCallback; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class Demo08ProducerTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private Demo08Producer producer; @Test public void testSyncSend() throws ExecutionException, InterruptedException { for (int id = 1; id <= 2; id++) { SendResult result = producer.syncSend(id); logger.info("[testSyncSend][发送编号:[{}] 发送结果:[{}]]", id, result); } // 阻塞等待,保证消费 new CountDownLatch(1).await(); } } ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-batch/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-03-kafka-demo-batch org.springframework.kafka spring-kafka 2.3.3.RELEASE org.springframework.boot spring-boot-starter-json org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-batch/src/main/java/cn/iocoder/springboot/lab03/kafkademo/Application.java ================================================ package cn.iocoder.springboot.lab03.kafkademo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-batch/src/main/java/cn/iocoder/springboot/lab03/kafkademo/consumer/Demo02Consumer.java ================================================ package cn.iocoder.springboot.lab03.kafkademo.consumer; import cn.iocoder.springboot.lab03.kafkademo.message.Demo02Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.kafka.annotation.KafkaListener; import org.springframework.stereotype.Component; @Component public class Demo02Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @KafkaListener(topics = Demo02Message.TOPIC, groupId = "demo02-consumer-group-" + Demo02Message.TOPIC) public void onMessage(Demo02Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-batch/src/main/java/cn/iocoder/springboot/lab03/kafkademo/message/Demo02Message.java ================================================ package cn.iocoder.springboot.lab03.kafkademo.message; /** * 示例 01 的 Message 消息 */ public class Demo02Message { public static final String TOPIC = "DEMO_02"; /** * 编号 */ private Integer id; public Demo02Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-batch/src/main/java/cn/iocoder/springboot/lab03/kafkademo/producer/Demo02Producer.java ================================================ package cn.iocoder.springboot.lab03.kafkademo.producer; import cn.iocoder.springboot.lab03.kafkademo.message.Demo02Message; import org.springframework.kafka.core.KafkaTemplate; import org.springframework.kafka.support.SendResult; import org.springframework.stereotype.Component; import org.springframework.util.concurrent.ListenableFuture; import javax.annotation.Resource; @Component public class Demo02Producer { @Resource private KafkaTemplate kafkaTemplate; public ListenableFuture> asyncSend(Integer id) { // 创建 Demo02Message 消息 Demo02Message message = new Demo02Message(); message.setId(id); // 异步发送消息 return kafkaTemplate.send(Demo02Message.TOPIC, message); } } ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-batch/src/main/resources/application.yaml ================================================ spring: # Kafka 配置项,对应 KafkaProperties 配置类 kafka: bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 # Kafka Producer 配置项 producer: acks: 1 # 0-不应答。1-leader 应答。all-所有 leader 和 follower 应答。 retries: 3 # 发送失败时,重试发送的次数 key-serializer: org.apache.kafka.common.serialization.StringSerializer # 消息的 key 的序列化 value-serializer: org.springframework.kafka.support.serializer.JsonSerializer # 消息的 value 的序列化 batch-size: 16384 # 每次批量发送消息的最大数量 buffer-memory: 33554432 # 每次批量发送消息的最大内存 properties: linger: ms: 30000 # 批处理延迟时间上限。这里配置为 30 * 1000 ms 过后,不管是否消息数量是否到达 batch-size 或者消息大小到达 buffer-memory 后,都直接发送一次请求。 # Kafka Consumer 配置项 consumer: auto-offset-reset: earliest # 设置消费者分组最初的消费进度为 earliest 。可参考博客 https://blog.csdn.net/lishuangzhe7047/article/details/74530417 理解 key-deserializer: org.apache.kafka.common.serialization.StringDeserializer value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer properties: spring: json: trusted: packages: cn.iocoder.springboot.lab03.kafkademo.message # Kafka Consumer Listener 监听器配置 listener: missing-topics-fatal: false # 消费监听接口监听的主题不存在时,默认会报错。所以通过设置为 false ,解决报错 logging: level: org: springframework: kafka: ERROR # spring-kafka INFO 日志太多了,所以我们限制只打印 ERROR 级别 apache: kafka: ERROR # kafka INFO 日志太多了,所以我们限制只打印 ERROR 级别 ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-batch/src/test/java/cn/iocoder/springboot/lab03/kafkademo/package-info.java ================================================ package cn.iocoder.springboot.lab03.kafkademo; ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-batch/src/test/java/cn/iocoder/springboot/lab03/kafkademo/producer/Demo02ProducerTest.java ================================================ package cn.iocoder.springboot.lab03.kafkademo.producer; import cn.iocoder.springboot.lab03.kafkademo.Application; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.kafka.support.SendResult; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.util.concurrent.ListenableFutureCallback; import java.util.concurrent.CountDownLatch; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class Demo02ProducerTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private Demo02Producer producer; @Test public void testASyncSend() throws InterruptedException { logger.info("[testASyncSend][开始执行]"); for (int i = 0; i < 3; i++) { int id = (int) (System.currentTimeMillis() / 1000); producer.asyncSend(id).addCallback(new ListenableFutureCallback>() { @Override public void onFailure(Throwable e) { logger.info("[testASyncSend][发送编号:[{}] 发送异常]]", id, e); } @Override public void onSuccess(SendResult result) { logger.info("[testASyncSend][发送编号:[{}] 发送成功,结果为:[{}]]", id, result); } }); // 故意每条消息之间,隔离 10 秒 Thread.sleep(10 * 1000L); } // 阻塞等待,保证消费 new CountDownLatch(1).await(); } } ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-batch-consume/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-03-kafka-demo-batch-consume org.springframework.kafka spring-kafka 2.3.3.RELEASE org.springframework.boot spring-boot-starter-json org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-batch-consume/src/main/java/cn/iocoder/springboot/lab03/kafkademo/Application.java ================================================ package cn.iocoder.springboot.lab03.kafkademo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-batch-consume/src/main/java/cn/iocoder/springboot/lab03/kafkademo/consumer/Demo02Consumer.java ================================================ package cn.iocoder.springboot.lab03.kafkademo.consumer; import cn.iocoder.springboot.lab03.kafkademo.message.Demo02Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.kafka.annotation.KafkaListener; import org.springframework.stereotype.Component; import java.util.List; @Component public class Demo02Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @KafkaListener(topics = Demo02Message.TOPIC, groupId = "demo02-consumer-group-" + Demo02Message.TOPIC) public void onMessage(List messages) { logger.info("[onMessage][线程编号:{} 消息数量:{}]", Thread.currentThread().getId(), messages.size()); } } ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-batch-consume/src/main/java/cn/iocoder/springboot/lab03/kafkademo/message/Demo02Message.java ================================================ package cn.iocoder.springboot.lab03.kafkademo.message; /** * 示例 01 的 Message 消息 */ public class Demo02Message { public static final String TOPIC = "DEMO_02"; /** * 编号 */ private Integer id; public Demo02Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-batch-consume/src/main/java/cn/iocoder/springboot/lab03/kafkademo/producer/Demo02Producer.java ================================================ package cn.iocoder.springboot.lab03.kafkademo.producer; import cn.iocoder.springboot.lab03.kafkademo.message.Demo02Message; import org.springframework.kafka.core.KafkaTemplate; import org.springframework.kafka.support.SendResult; import org.springframework.stereotype.Component; import org.springframework.util.concurrent.ListenableFuture; import javax.annotation.Resource; @Component public class Demo02Producer { @Resource private KafkaTemplate kafkaTemplate; public ListenableFuture> asyncSend(Integer id) { // 创建 Demo02Message 消息 Demo02Message message = new Demo02Message(); message.setId(id); // 异步发送消息 return kafkaTemplate.send(Demo02Message.TOPIC, message); } } ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-batch-consume/src/main/resources/application.yaml ================================================ spring: # Kafka 配置项,对应 KafkaProperties 配置类 kafka: bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 # Kafka Producer 配置项 producer: acks: 1 # 0-不应答。1-leader 应答。all-所有 leader 和 follower 应答。 retries: 3 # 发送失败时,重试发送的次数 key-serializer: org.apache.kafka.common.serialization.StringSerializer # 消息的 key 的序列化 value-serializer: org.springframework.kafka.support.serializer.JsonSerializer # 消息的 value 的序列化 batch-size: 16384 # 每次批量发送消息的最大数量 buffer-memory: 33554432 # 每次批量发送消息的最大内存 properties: linger: ms: 30000 # 批处理延迟时间上限。这里配置为 30 * 1000 ms 过后,不管是否消息数量是否到达 batch-size 或者消息大小到达 buffer-memory 后,都直接发送一次请求。 # Kafka Consumer 配置项 consumer: auto-offset-reset: earliest # 设置消费者分组最初的消费进度为 earliest 。可参考博客 https://blog.csdn.net/lishuangzhe7047/article/details/74530417 理解 key-deserializer: org.apache.kafka.common.serialization.StringDeserializer value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer fetch-max-wait: 10000 # poll 一次拉取的阻塞的最大时长,单位:毫秒。这里指的是阻塞拉取需要满足至少 fetch-min-size 条消息 fetch-min-size: 10 # poll 一次消息拉取的最小数量 max-poll-records: 100 # poll 一次消息拉取的最大数量 properties: spring: json: trusted: packages: cn.iocoder.springboot.lab03.kafkademo.message # Kafka Consumer Listener 监听器配置 listener: type: BATCH # 监听器类型,默认为 SINGLE ,只监听单条消息。这里我们配置 BATCH ,监听多条消息,批量消费 missing-topics-fatal: false # 消费监听接口监听的主题不存在时,默认会报错。所以通过设置为 false ,解决报错 logging: level: org: springframework: kafka: ERROR # spring-kafka INFO 日志太多了,所以我们限制只打印 ERROR 级别 apache: kafka: ERROR # kafka INFO 日志太多了,所以我们限制只打印 ERROR 级别 ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-batch-consume/src/test/java/cn/iocoder/springboot/lab03/kafkademo/package-info.java ================================================ package cn.iocoder.springboot.lab03.kafkademo; ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-batch-consume/src/test/java/cn/iocoder/springboot/lab03/kafkademo/producer/Demo02ProducerTest.java ================================================ package cn.iocoder.springboot.lab03.kafkademo.producer; import cn.iocoder.springboot.lab03.kafkademo.Application; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.kafka.support.SendResult; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.util.concurrent.ListenableFutureCallback; import java.util.concurrent.CountDownLatch; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class Demo02ProducerTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private Demo02Producer producer; @Test public void testASyncSend() throws InterruptedException { logger.info("[testASyncSend][开始执行]"); for (int i = 0; i < 3; i++) { int id = (int) (System.currentTimeMillis() / 1000); producer.asyncSend(id).addCallback(new ListenableFutureCallback>() { @Override public void onFailure(Throwable e) { logger.info("[testASyncSend][发送编号:[{}] 发送异常]]", id, e); } @Override public void onSuccess(SendResult result) { logger.info("[testASyncSend][发送编号:[{}] 发送成功,结果为:[{}]]", id, result); } }); // 故意每条消息之间,隔离 10 秒 Thread.sleep(10 * 1000L); } // 阻塞等待,保证消费 new CountDownLatch(1).await(); } } ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-broadcast/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-03-kafka-demo-broadcast org.springframework.kafka spring-kafka 2.3.3.RELEASE org.springframework.boot spring-boot-starter-json org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-broadcast/src/main/java/cn/iocoder/springboot/lab03/kafkademo/Application.java ================================================ package cn.iocoder.springboot.lab03.kafkademo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-broadcast/src/main/java/cn/iocoder/springboot/lab03/kafkademo/consumer/Demo05Consumer.java ================================================ package cn.iocoder.springboot.lab03.kafkademo.consumer; import cn.iocoder.springboot.lab03.kafkademo.message.Demo05Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.kafka.annotation.KafkaListener; import org.springframework.stereotype.Component; @Component public class Demo05Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @KafkaListener(topics = Demo05Message.TOPIC, groupId = "demo05-consumer-group-" + Demo05Message.TOPIC + "-" + "#{T(java.util.UUID).randomUUID()}") public void onMessage(Demo05Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-broadcast/src/main/java/cn/iocoder/springboot/lab03/kafkademo/message/Demo05Message.java ================================================ package cn.iocoder.springboot.lab03.kafkademo.message; /** * 示例 05 的 Message 消息 */ public class Demo05Message { public static final String TOPIC = "DEMO_05"; /** * 编号 */ private Integer id; public Demo05Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo05Message{" + "id=" + id + '}'; } } ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-broadcast/src/main/java/cn/iocoder/springboot/lab03/kafkademo/producer/Demo05Producer.java ================================================ package cn.iocoder.springboot.lab03.kafkademo.producer; import cn.iocoder.springboot.lab03.kafkademo.message.Demo05Message; import org.springframework.kafka.core.KafkaTemplate; import org.springframework.kafka.support.SendResult; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.util.concurrent.ExecutionException; @Component public class Demo05Producer { @Resource private KafkaTemplate kafkaTemplate; public SendResult syncSend(Integer id) throws ExecutionException, InterruptedException { // 创建 Demo05Message 消息 Demo05Message message = new Demo05Message(); message.setId(id); // 同步发送消息 return kafkaTemplate.send(Demo05Message.TOPIC, message).get(); } } ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-broadcast/src/main/resources/application.yaml ================================================ spring: # Kafka 配置项,对应 KafkaProperties 配置类 kafka: bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 # Kafka Producer 配置项 producer: acks: 1 # 0-不应答。1-leader 应答。all-所有 leader 和 follower 应答。 retries: 3 # 发送失败时,重试发送的次数 key-serializer: org.apache.kafka.common.serialization.StringSerializer # 消息的 key 的序列化 value-serializer: org.springframework.kafka.support.serializer.JsonSerializer # 消息的 value 的序列化 # Kafka Consumer 配置项 consumer: auto-offset-reset: latest # 设置消费者分组最初的消费进度为 latest 。可参考博客 https://blog.csdn.net/lishuangzhe7047/article/details/74530417 理解 key-deserializer: org.apache.kafka.common.serialization.StringDeserializer value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer properties: spring: json: trusted: packages: cn.iocoder.springboot.lab03.kafkademo.message # Kafka Consumer Listener 监听器配置 listener: missing-topics-fatal: false # 消费监听接口监听的主题不存在时,默认会报错。所以通过设置为 false ,解决报错 logging: level: org: springframework: kafka: ERROR # spring-kafka INFO 日志太多了,所以我们限制只打印 ERROR 级别 apache: kafka: ERROR # kafka INFO 日志太多了,所以我们限制只打印 ERROR 级别 ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-broadcast/src/test/java/cn/iocoder/springboot/lab03/kafkademo/package-info.java ================================================ package cn.iocoder.springboot.lab03.kafkademo; ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-broadcast/src/test/java/cn/iocoder/springboot/lab03/kafkademo/producer/Demo05ProducerTest.java ================================================ package cn.iocoder.springboot.lab03.kafkademo.producer; import cn.iocoder.springboot.lab03.kafkademo.Application; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.kafka.support.SendResult; import org.springframework.test.context.junit4.SpringRunner; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class Demo05ProducerTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private Demo05Producer producer; @Test public void test() throws InterruptedException { // 阻塞等待,保证消费 new CountDownLatch(1).await(); } @Test public void testSyncSend() throws ExecutionException, InterruptedException { int id = (int) (System.currentTimeMillis() / 1000); SendResult result = producer.syncSend(id); logger.info("[testSyncSend][发送编号:[{}] 发送结果:[{}]]", id, result); // 阻塞等待,保证消费 new CountDownLatch(1).await(); } } ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-concurrency/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-03-kafka-demo-concurrency org.springframework.kafka spring-kafka 2.3.3.RELEASE org.springframework.boot spring-boot-starter-json org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-concurrency/src/main/java/cn/iocoder/springboot/lab03/kafkademo/Application.java ================================================ package cn.iocoder.springboot.lab03.kafkademo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-concurrency/src/main/java/cn/iocoder/springboot/lab03/kafkademo/consumer/Demo06Consumer.java ================================================ package cn.iocoder.springboot.lab03.kafkademo.consumer; import cn.iocoder.springboot.lab03.kafkademo.message.Demo06Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.kafka.annotation.KafkaListener; import org.springframework.stereotype.Component; @Component public class Demo06Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @KafkaListener(topics = Demo06Message.TOPIC, groupId = "demo06-consumer-group-" + Demo06Message.TOPIC, concurrency = "2") public void onMessage(Demo06Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-concurrency/src/main/java/cn/iocoder/springboot/lab03/kafkademo/message/Demo06Message.java ================================================ package cn.iocoder.springboot.lab03.kafkademo.message; /** * 示例 06 的 Message 消息 */ public class Demo06Message { public static final String TOPIC = "DEMO_06"; /** * 编号 */ private Integer id; public Demo06Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo06Message{" + "id=" + id + '}'; } } ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-concurrency/src/main/java/cn/iocoder/springboot/lab03/kafkademo/producer/Demo06Producer.java ================================================ package cn.iocoder.springboot.lab03.kafkademo.producer; import cn.iocoder.springboot.lab03.kafkademo.message.Demo06Message; import org.springframework.kafka.core.KafkaTemplate; import org.springframework.kafka.support.SendResult; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.util.concurrent.ExecutionException; @Component public class Demo06Producer { @Resource private KafkaTemplate kafkaTemplate; public SendResult syncSend(Integer id) throws ExecutionException, InterruptedException { // 创建 Demo01Message 消息 Demo06Message message = new Demo06Message(); message.setId(id); // 同步发送消息 return kafkaTemplate.send(Demo06Message.TOPIC, message).get(); } public SendResult syncSendOrderly(Integer id) throws ExecutionException, InterruptedException { // 创建 Demo01Message 消息 Demo06Message message = new Demo06Message(); message.setId(id); // 同步发送消息 // 因为我们使用 String 的方式序列化 key ,所以需要将 id 转换成 String return kafkaTemplate.send(Demo06Message.TOPIC, String.valueOf(id), message).get(); } } ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-concurrency/src/main/resources/application.yaml ================================================ spring: # Kafka 配置项,对应 KafkaProperties 配置类 kafka: bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 # Kafka Producer 配置项 producer: acks: 1 # 0-不应答。1-leader 应答。all-所有 leader 和 follower 应答。 retries: 3 # 发送失败时,重试发送的次数 key-serializer: org.apache.kafka.common.serialization.StringSerializer # 消息的 key 的序列化 value-serializer: org.springframework.kafka.support.serializer.JsonSerializer # 消息的 value 的序列化 # Kafka Consumer 配置项 consumer: auto-offset-reset: earliest # 设置消费者分组最初的消费进度为 earliest 。可参考博客 https://blog.csdn.net/lishuangzhe7047/article/details/74530417 理解 key-deserializer: org.apache.kafka.common.serialization.StringDeserializer value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer properties: spring: json: trusted: packages: cn.iocoder.springboot.lab03.kafkademo.message # Kafka Consumer Listener 监听器配置 listener: missing-topics-fatal: false # 消费监听接口监听的主题不存在时,默认会报错。所以通过设置为 false ,解决报错 logging: level: org: springframework: kafka: ERROR # spring-kafka INFO 日志太多了,所以我们限制只打印 ERROR 级别 apache: kafka: ERROR # kafka INFO 日志太多了,所以我们限制只打印 ERROR 级别 ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-concurrency/src/test/java/cn/iocoder/springboot/lab03/kafkademo/package-info.java ================================================ package cn.iocoder.springboot.lab03.kafkademo; ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-concurrency/src/test/java/cn/iocoder/springboot/lab03/kafkademo/producer/Demo06ProducerTest.java ================================================ package cn.iocoder.springboot.lab03.kafkademo.producer; import cn.iocoder.springboot.lab03.kafkademo.Application; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.kafka.support.SendResult; import org.springframework.test.context.junit4.SpringRunner; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class Demo06ProducerTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private Demo06Producer producer; @Test public void testSyncSend() throws ExecutionException, InterruptedException { for (int i = 0; i < 10; i++) { int id = (int) (System.currentTimeMillis() / 1000); SendResult result = producer.syncSend(id); // logger.info("[testSyncSend][发送编号:[{}] 发送结果:[{}]]", id, result); } // 阻塞等待,保证消费 new CountDownLatch(1).await(); } @Test public void testSyncSendOrderly() throws ExecutionException, InterruptedException { for (int i = 0; i < 10; i++) { int id = 1; SendResult result = producer.syncSendOrderly(id); logger.info("[testSyncSend][发送编号:[{}] 发送队列:[{}]]", id, result.getRecordMetadata().partition()); } // 阻塞等待,保证消费 new CountDownLatch(1).await(); } } ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-transaction/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-03-kafka-demo-transaction org.springframework.kafka spring-kafka 2.3.3.RELEASE org.springframework.boot spring-boot-starter-json org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-transaction/src/main/java/cn/iocoder/springboot/lab03/kafkademo/Application.java ================================================ package cn.iocoder.springboot.lab03.kafkademo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-transaction/src/main/java/cn/iocoder/springboot/lab03/kafkademo/consumer/Demo07Consumer.java ================================================ package cn.iocoder.springboot.lab03.kafkademo.consumer; import cn.iocoder.springboot.lab03.kafkademo.message.Demo07Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.kafka.annotation.KafkaListener; import org.springframework.stereotype.Component; @Component public class Demo07Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @KafkaListener(topics = Demo07Message.TOPIC, groupId = "demo07-consumer-group-" + Demo07Message.TOPIC) public void onMessage(Demo07Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-transaction/src/main/java/cn/iocoder/springboot/lab03/kafkademo/message/Demo07Message.java ================================================ package cn.iocoder.springboot.lab03.kafkademo.message; /** * 示例 07 的 Message 消息 */ public class Demo07Message { public static final String TOPIC = "DEMO_07"; /** * 编号 */ private Integer id; public Demo07Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo07Message{" + "id=" + id + '}'; } } ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-transaction/src/main/java/cn/iocoder/springboot/lab03/kafkademo/producer/Demo07Producer.java ================================================ package cn.iocoder.springboot.lab03.kafkademo.producer; import cn.iocoder.springboot.lab03.kafkademo.message.Demo07Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.kafka.core.KafkaOperations; import org.springframework.kafka.core.KafkaTemplate; import org.springframework.kafka.support.SendResult; import org.springframework.stereotype.Component; import org.springframework.util.concurrent.ListenableFuture; import javax.annotation.Resource; import java.util.concurrent.ExecutionException; @Component public class Demo07Producer { private Logger logger = LoggerFactory.getLogger(getClass()); @Resource private KafkaTemplate kafkaTemplate; public SendResult syncSend(Integer id) throws ExecutionException, InterruptedException { // 创建 Demo07Message 消息 Demo07Message message = new Demo07Message(); message.setId(id); // 同步发送消息 return kafkaTemplate.send(Demo07Message.TOPIC, message).get(); } public String syncSendInTransaction(Integer id, Runnable runner) throws ExecutionException, InterruptedException { return kafkaTemplate.executeInTransaction(new KafkaOperations.OperationsCallback() { @Override public String doInOperations(KafkaOperations kafkaOperations) { // 创建 Demo07Message 消息 Demo07Message message = new Demo07Message(); message.setId(id); try { SendResult sendResult = kafkaOperations.send(Demo07Message.TOPIC, message).get(); logger.info("[doInOperations][发送编号:[{}] 发送结果:[{}]]", id, sendResult); } catch (Exception e) { throw new RuntimeException(e); } // 本地业务逻辑... biubiubiu runner.run(); // 返回结果 return "success"; } }); } } ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-transaction/src/main/resources/application.yaml ================================================ spring: # Kafka 配置项,对应 KafkaProperties 配置类 kafka: bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 # Kafka Producer 配置项 producer: acks: all # 0-不应答。1-leader 应答。all-所有 leader 和 follower 应答。 retries: 3 # 发送失败时,重试发送的次数 key-serializer: org.apache.kafka.common.serialization.StringSerializer # 消息的 key 的序列化 value-serializer: org.springframework.kafka.support.serializer.JsonSerializer # 消息的 value 的序列化 transaction-id-prefix: demo. # 事务编号前缀 # Kafka Consumer 配置项 consumer: auto-offset-reset: earliest # 设置消费者分组最初的消费进度为 earliest 。可参考博客 https://blog.csdn.net/lishuangzhe7047/article/details/74530417 理解 key-deserializer: org.apache.kafka.common.serialization.StringDeserializer value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer properties: spring: json: trusted: packages: cn.iocoder.springboot.lab03.kafkademo.message isolation: level: read_committed # 读取已提交的消息 # Kafka Consumer Listener 监听器配置 listener: missing-topics-fatal: false # 消费监听接口监听的主题不存在时,默认会报错。所以通过设置为 false ,解决报错 logging: level: org: springframework: kafka: ERROR # spring-kafka INFO 日志太多了,所以我们限制只打印 ERROR 级别 apache: kafka: ERROR # kafka INFO 日志太多了,所以我们限制只打印 ERROR 级别 ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-transaction/src/main/resources/application_bak.yaml ================================================ spring: kafka: bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 # Kafka Producer 配置项 producer: acks: 1 # 0-不应答。1-leader 应答。all-所有 leader 和 follower 应答。 retries: 3 # 发送失败时,重试发送的次数 batch-size: 16384 # 每次批量发送消息的最大数量 buffer-memory: 33554432 # 每次批量发送消息的最大内存 key-serializer: org.apache.kafka.common.serialization.StringSerializer # 消息的 key 的序列化 value-serializer: org.springframework.kafka.support.serializer.JsonSerializer # 消息的 value 的序列化 # Kafka Consumer 配置项 consumer: enable-auto-commit: false # true-使用 kafka 默认自带的提交模式。false-使用 Spring-Kafka 的自动提交 offset 机制。建议设置为 false 使用 kafka-spring 的机制,分析见 https://juejin.im/entry/5a6e8dea518825732472710c 。 auto-commit-interval: 1000 # 在开启 enable-auto-commit 时,自动提交消费进度频率,单位:毫秒。如果 enable-auto-commit 为 false 时候,可以不设置 auto-offset-reset: earliest # 设置消费者分组最初的消费进度为 earliest 。可参考博客 https://blog.csdn.net/lishuangzhe7047/article/details/74530417 理解 key-deserializer: org.apache.kafka.common.serialization.StringDeserializer value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer properties: spring: json: trusted: packages: cn.iocoder.springboot.lab03.kafkademo.message # Kafka Consumer Listener 监听器配置 listener: concurrency: 10 # 每个消费者监听器最大并发数,默认为 1 。可以通过设置 n ,这样对于每个监听器就会使用 n 个线程消费消息,提高整体消费速度。详细可参考博客 https://www.jianshu.com/p/ad0e5424edbd 理解。 # Producer 配置项 logging: level: org: springframework: kafka: ERROR # spring-kafka INFO 日志太多了,所以我们限制只打印 ERROR 级别 apache: kafka: ERROR # kafka INFO 日志太多了,所以我们限制只打印 ERROR 级别 ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-transaction/src/test/java/cn/iocoder/springboot/lab03/kafkademo/package-info.java ================================================ package cn.iocoder.springboot.lab03.kafkademo; ================================================ FILE: lab-03-kafka/lab-03-kafka-demo-transaction/src/test/java/cn/iocoder/springboot/lab03/kafkademo/producer/Demo07ProducerTest.java ================================================ package cn.iocoder.springboot.lab03.kafkademo.producer; import cn.iocoder.springboot.lab03.kafkademo.Application; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.kafka.support.SendResult; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.util.concurrent.ListenableFutureCallback; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class Demo07ProducerTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private Demo07Producer producer; @Test public void testSyncSend() throws ExecutionException, InterruptedException { int id = (int) (System.currentTimeMillis() / 1000); SendResult result = producer.syncSend(id); logger.info("[testSyncSend][发送编号:[{}] 发送结果:[{}]]", id, result); // 阻塞等待,保证消费 new CountDownLatch(1).await(); } @Test public void testSyncSendInTransaction() throws ExecutionException, InterruptedException { int id = (int) (System.currentTimeMillis() / 1000); producer.syncSendInTransaction(id, new Runnable() { @Override public void run() { logger.info("[run][我要开始睡觉了]"); try { Thread.sleep(10 * 1000L); } catch (InterruptedException e) { throw new RuntimeException(e); } logger.info("[run][我睡醒了]"); } }); // 阻塞等待,保证消费 new CountDownLatch(1).await(); } } ================================================ FILE: lab-03-kafka/lab-03-kafka-native/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-03-kafka-native org.apache.kafka kafka-clients 2.3.1 ================================================ FILE: lab-03-kafka/lab-03-kafka-native/src/main/java/cn/iocoder/springboot/lab03/kafkademo/ConsumerMain.java ================================================ package cn.iocoder.springboot.lab03.kafkademo; import org.apache.kafka.clients.consumer.Consumer; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.clients.consumer.ConsumerRecords; import org.apache.kafka.clients.consumer.KafkaConsumer; import org.apache.kafka.common.serialization.StringDeserializer; import java.time.Duration; import java.util.Collections; import java.util.Properties; public class ConsumerMain { private static Consumer createConsumer() { // 设置 Producer 的属性 Properties properties = new Properties(); properties.put("bootstrap.servers", "127.0.0.1:9092"); // 设置 Broker 的地址 properties.put("group.id", "demo-consumer-group"); // 消费者分组 properties.put("auto.offset.reset", "earliest"); // 设置消费者分组最初的消费进度为 earliest 。可参考博客 https://blog.csdn.net/lishuangzhe7047/article/details/74530417 理解 properties.put("enable.auto.commit", true); // 是否自动提交消费进度 properties.put("auto.commit.interval.ms", "1000"); // 自动提交消费进度频率 properties.put("key.deserializer", StringDeserializer.class.getName()); // 消息的 key 的反序列化方式 properties.put("value.deserializer", StringDeserializer.class.getName()); // 消息的 value 的反序列化方式 // 创建 KafkaProducer 对象 // 因为我们消息的 key 和 value 都使用 String 类型,所以创建的 Producer 是 的泛型。 return new KafkaConsumer<>(properties); } public static void main(String[] args) { // 创建 KafkaConsumer 对象 Consumer consumer = createConsumer(); // 订阅消息 consumer.subscribe(Collections.singleton("TestTopic")); // 拉取消息 while (true) { // 拉取消息。如果拉取不到消息,阻塞等待最多 10 秒,或者等待拉取到消息。 ConsumerRecords records = consumer.poll(Duration.ofSeconds(10)); // 遍历处理消息 records.forEach(new java.util.function.Consumer() { @Override public void accept(ConsumerRecord record) { System.out.println(record.key() + "\t" + record.value()); } }); } } } ================================================ FILE: lab-03-kafka/lab-03-kafka-native/src/main/java/cn/iocoder/springboot/lab03/kafkademo/ProducerMain.java ================================================ package cn.iocoder.springboot.lab03.kafkademo; import org.apache.kafka.clients.producer.KafkaProducer; import org.apache.kafka.clients.producer.Producer; import org.apache.kafka.clients.producer.ProducerRecord; import org.apache.kafka.clients.producer.RecordMetadata; import org.apache.kafka.common.serialization.StringSerializer; import java.util.Properties; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; public class ProducerMain { private static Producer createProducer() { // 设置 Producer 的属性 Properties properties = new Properties(); properties.put("bootstrap.servers", "127.0.0.1:9092"); // 设置 Broker 的地址 properties.put("acks", "1"); // 0-不应答。1-leader 应答。all-所有 leader 和 follower 应答。 properties.put("retries", 3); // 发送失败时,重试发送的次数 // properties.put("batch.size", 16384); // properties.put("linger.ms", 1); // properties.put("client.id", "DemoProducer"); // properties.put("buffer.memory", 33554432); properties.put("key.serializer", StringSerializer.class.getName()); // 消息的 key 的序列化方式 properties.put("value.serializer", StringSerializer.class.getName()); // 消息的 value 的序列化方式 // 创建 KafkaProducer 对象 // 因为我们消息的 key 和 value 都使用 String 类型,所以创建的 Producer 是 的泛型。 return new KafkaProducer<>(properties); } public static void main(String[] args) throws ExecutionException, InterruptedException { // 创建 KafkaProducer 对象 Producer producer = createProducer(); // 创建消息。传入的三个参数,分别是 Topic ,消息的 key ,消息的 message 。 ProducerRecord message = new ProducerRecord<>("TestTopic", "key", "yudaoyuanma"); // 同步发送消息 Future sendResultFuture = producer.send(message); RecordMetadata result = sendResultFuture.get(); System.out.println("message sent to " + result.topic() + ", partition " + result.partition() + ", offset " + result.offset()); } } ================================================ FILE: lab-03-kafka/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 1.5.9.RELEASE 4.0.0 lab-03-kafka pom lab-03-kafka-native lab-03-kafka-demo lab-03-kafka-demo-batch lab-03-kafka-demo-batch-consume lab-03-kafka-demo-broadcast lab-03-kafka-demo-concurrency lab-03-kafka-demo-transaction lab-03-kafka-demo-ack ================================================ FILE: lab-03-kafka/《芋道 Spring Boot 消息队列 Kafka 入门》.md ================================================ ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-consume-retry/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-04-rabbitmq-consume-retry org.springframework.boot spring-boot-starter-amqp org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-consume-retry/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/Application.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-consume-retry/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/config/RabbitConfig.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.config; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo07Message; import org.springframework.amqp.core.*; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class RabbitConfig { /** * Direct Exchange 示例的配置类 */ public static class DirectExchangeDemoConfiguration { // 创建 Queue @Bean public Queue demo07Queue() { return QueueBuilder.durable(Demo07Message.QUEUE) // durable: 是否持久化 .exclusive() // exclusive: 是否排它 .autoDelete() // autoDelete: 是否自动删除 .deadLetterExchange(Demo07Message.EXCHANGE) .deadLetterRoutingKey(Demo07Message.DEAD_ROUTING_KEY) .build(); } // 创建 Dead Queue @Bean public Queue demo07DeadQueue() { return new Queue(Demo07Message.DEAD_QUEUE, // Queue 名字 true, // durable: 是否持久化 false, // exclusive: 是否排它 false); // autoDelete: 是否自动删除 } // 创建 Direct Exchange @Bean public DirectExchange demo07Exchange() { return new DirectExchange(Demo07Message.EXCHANGE, true, // durable: 是否持久化 false); // exclusive: 是否排它 } // 创建 Binding // Exchange:Demo07Message.EXCHANGE // Routing key:Demo07Message.ROUTING_KEY // Queue:Demo07Message.QUEUE @Bean public Binding demo07Binding() { return BindingBuilder.bind(demo07Queue()).to(demo07Exchange()).with(Demo07Message.ROUTING_KEY); } // 创建 Dead Binding // Exchange:Demo07Message.EXCHANGE // Routing key:Demo07Message.DEAD_ROUTING_KEY // Queue:Demo07Message.DEAD_QUEUE @Bean public Binding demo07DeadBinding() { return BindingBuilder.bind(demo07DeadQueue()).to(demo07Exchange()).with(Demo07Message.DEAD_ROUTING_KEY); } } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-consume-retry/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/Demo07Consumer.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo07Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; @Component @RabbitListener(queues = Demo07Message.QUEUE) public class Demo07Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @RabbitHandler public void onMessage(Demo07Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); // 注意,此处抛出一个 RuntimeException 异常,模拟消费失败 throw new RuntimeException("我就是故意抛出一个异常"); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-consume-retry/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/Demo07DeadConsumer.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo07Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; @Component @RabbitListener(queues = Demo07Message.DEAD_QUEUE) public class Demo07DeadConsumer { private Logger logger = LoggerFactory.getLogger(getClass()); @RabbitHandler public void onMessage(Demo07Message message) { logger.info("[onMessage][【死信队列】线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-consume-retry/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/message/Demo07Message.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.message; import java.io.Serializable; public class Demo07Message implements Serializable { public static final String QUEUE = "QUEUE_DEMO_07"; // 正常队列 public static final String DEAD_QUEUE = "DEAD_QUEUE_DEMO_07"; // 死信队列 public static final String EXCHANGE = "EXCHANGE_DEMO_07"; public static final String ROUTING_KEY = "ROUTING_KEY_07"; // 正常路由键 public static final String DEAD_ROUTING_KEY = "DEAD_ROUTING_KEY_07"; // 死信路由键 /** * 编号 */ private Integer id; public Demo07Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo07Message{" + "id=" + id + '}'; } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-consume-retry/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo07Producer.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.producer; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo07Message; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class Demo07Producer { @Autowired private RabbitTemplate rabbitTemplate; public void syncSend(Integer id) { // 创建 Demo07Message 消息 Demo07Message message = new Demo07Message(); message.setId(id); // 同步发送消息 rabbitTemplate.convertAndSend(Demo07Message.EXCHANGE, Demo07Message.ROUTING_KEY, message); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-consume-retry/src/main/resources/application.yaml ================================================ spring: # RabbitMQ 配置项,对应 RabbitProperties 配置类 rabbitmq: host: 127.0.0.1 # RabbitMQ 服务的地址 port: 5672 # RabbitMQ 服务的端口 username: guest # RabbitMQ 服务的账号 password: guest # RabbitMQ 服务的密码 template: # 对应 RabbitProperties.Retry 类 retry: enabled: true # 开启发送机制 max-attempts: 3 # 最大重试次数。默认为 3 。 initial-interval: 1000 # 重试间隔,单位为毫秒。默认为 1000 。 listener: simple: # 对应 RabbitProperties.ListenerRetry 类 retry: enabled: true # 开启消费重试机制 max-attempts: 3 # 最大重试次数。默认为 3 。 initial-interval: 1000 # 重试间隔,单位为毫秒。默认为 1000 。 ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-consume-retry/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/package-info.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo; ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-consume-retry/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo07ProducerTest.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.producer; import cn.iocoder.springboot.lab04.rabbitmqdemo.Application; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.concurrent.CountDownLatch; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class Demo07ProducerTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private Demo07Producer producer; @Test public void testSyncSend() throws InterruptedException { int id = (int) (System.currentTimeMillis() / 1000); producer.syncSend(id); logger.info("[testSyncSend][发送编号:[{}] 发送成功]", id); // 阻塞等待,保证消费 new CountDownLatch(1).await(); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-04-rabbitmq-demo org.springframework.boot spring-boot-starter-amqp org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/Application.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableAsync; @SpringBootApplication @EnableAsync // 开启异步 public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/config/RabbitConfig.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.config; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo01Message; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo02Message; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo03Message; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo04Message; import org.springframework.amqp.core.*; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class RabbitConfig { /** * Direct Exchange 示例的配置类 */ public static class DirectExchangeDemoConfiguration { // 创建 Queue @Bean public Queue demo01Queue() { return new Queue(Demo01Message.QUEUE, // Queue 名字 true, // durable: 是否持久化 false, // exclusive: 是否排它 false); // autoDelete: 是否自动删除 } // 创建 Direct Exchange @Bean public DirectExchange demo01Exchange() { return new DirectExchange(Demo01Message.EXCHANGE, true, // durable: 是否持久化 false); // exclusive: 是否排它 } // 创建 Binding // Exchange:Demo01Message.EXCHANGE // Routing key:Demo01Message.ROUTING_KEY // Queue:Demo01Message.QUEUE @Bean public Binding demo01Binding() { return BindingBuilder.bind(demo01Queue()).to(demo01Exchange()).with(Demo01Message.ROUTING_KEY); } } /** * Topic Exchange 示例的配置类 */ public static class TopicExchangeDemoConfiguration { // 创建 Queue @Bean public Queue demo02Queue() { return new Queue(Demo02Message.QUEUE, // Queue 名字 true, // durable: 是否持久化 false, // exclusive: 是否排它 false); // autoDelete: 是否自动删除 } // 创建 Topic Exchange @Bean public TopicExchange demo02Exchange() { return new TopicExchange(Demo02Message.EXCHANGE, true, // durable: 是否持久化 false); // exclusive: 是否排它 } // 创建 Binding // Exchange:Demo02Message.EXCHANGE // Routing key:Demo02Message.ROUTING_KEY // Queue:Demo02Message.QUEUE @Bean public Binding demo02Binding() { return BindingBuilder.bind(demo02Queue()).to(demo02Exchange()).with(Demo02Message.ROUTING_KEY); } } /** * Fanout Exchange 示例的配置类 */ public static class FanoutExchangeDemoConfiguration { // 创建 Queue A @Bean public Queue demo03QueueA() { return new Queue(Demo03Message.QUEUE_A, // Queue 名字 true, // durable: 是否持久化 false, // exclusive: 是否排它 false); // autoDelete: 是否自动删除 } // 创建 Queue B @Bean public Queue demo03QueueB() { return new Queue(Demo03Message.QUEUE_B, // Queue 名字 true, // durable: 是否持久化 false, // exclusive: 是否排它 false); // autoDelete: 是否自动删除 } // 创建 Fanout Exchange @Bean public FanoutExchange demo03Exchange() { return new FanoutExchange(Demo03Message.EXCHANGE, true, // durable: 是否持久化 false); // exclusive: 是否排它 } // 创建 Binding A // Exchange:Demo03Message.EXCHANGE // Queue:Demo03Message.QUEUE_A @Bean public Binding demo03BindingA() { return BindingBuilder.bind(demo03QueueA()).to(demo03Exchange()); } // 创建 Binding B // Exchange:Demo03Message.EXCHANGE // Queue:Demo03Message.QUEUE_B @Bean public Binding demo03BindingB() { return BindingBuilder.bind(demo03QueueB()).to(demo03Exchange()); } } /** * Headers Exchange 示例的配置类 */ public static class HeadersExchangeDemoConfiguration { // 创建 Queue @Bean public Queue demo04Queue() { return new Queue(Demo04Message.QUEUE, // Queue 名字 true, // durable: 是否持久化 false, // exclusive: 是否排它 false); // autoDelete: 是否自动删除 } // 创建 Headers Exchange @Bean public HeadersExchange demo04Exchange() { return new HeadersExchange(Demo04Message.EXCHANGE, true, // durable: 是否持久化 false); // exclusive: 是否排它 } // 创建 Binding // Exchange:Demo04Message.EXCHANGE // Queue:Demo04Message.QUEUE // Headers: Demo04Message.HEADER_KEY + Demo04Message.HEADER_VALUE @Bean public Binding demo4Binding() { return BindingBuilder.bind(demo04Queue()).to(demo04Exchange()) .where(Demo04Message.HEADER_KEY).matches(Demo04Message.HEADER_VALUE); // 配置 Headers 匹配 } } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/Demo01Consumer.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo01Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; @Component @RabbitListener(queues = Demo01Message.QUEUE) public class Demo01Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @RabbitHandler public void onMessage(Demo01Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } // @RabbitHandler(isDefault = true) // public void onMessage(org.springframework.amqp.core.Message message) { // logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); // } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/Demo02Consumer.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo02Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; @Component @RabbitListener(queues = Demo02Message.QUEUE) public class Demo02Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @RabbitHandler public void onMessage(Demo02Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/Demo03ConsumerA.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo03Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; @Component @RabbitListener(queues = Demo03Message.QUEUE_A) public class Demo03ConsumerA { private Logger logger = LoggerFactory.getLogger(getClass()); @RabbitHandler public void onMessage(Demo03Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/Demo03ConsumerB.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo03Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; @Component @RabbitListener(queues = Demo03Message.QUEUE_B) public class Demo03ConsumerB { private Logger logger = LoggerFactory.getLogger(getClass()); @RabbitHandler public void onMessage(Demo03Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/Demo04Consumer.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo04Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; @Component @RabbitListener(queues = Demo04Message.QUEUE) public class Demo04Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @RabbitHandler public void onMessage(Demo04Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } // @RabbitHandler(isDefault = true) // public void onMessage(org.springframework.amqp.core.Message message) { // logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); // } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/message/Demo01Message.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.message; import java.io.Serializable; public class Demo01Message implements Serializable { public static final String QUEUE = "QUEUE_DEMO_01"; public static final String EXCHANGE = "EXCHANGE_DEMO_01"; public static final String ROUTING_KEY = "ROUTING_KEY_01"; /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/message/Demo02Message.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.message; import java.io.Serializable; public class Demo02Message implements Serializable { public static final String QUEUE = "QUEUE_DEMO_02"; public static final String EXCHANGE = "EXCHANGE_DEMO_02"; public static final String ROUTING_KEY = "#.yu.nai"; /** * 编号 */ private Integer id; public Demo02Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo02Message{" + "id=" + id + '}'; } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/message/Demo03Message.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.message; import java.io.Serializable; public class Demo03Message implements Serializable { public static final String QUEUE_A = "QUEUE_DEMO_03_A"; public static final String QUEUE_B = "QUEUE_DEMO_03_B"; public static final String EXCHANGE = "EXCHANGE_DEMO_03"; /** * 编号 */ private Integer id; public Demo03Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo03Message{" + "id=" + id + '}'; } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/message/Demo04Message.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.message; import java.io.Serializable; public class Demo04Message implements Serializable { public static final String QUEUE = "QUEUE_DEMO_04_A"; public static final String EXCHANGE = "EXCHANGE_DEMO_04"; public static final String HEADER_KEY = "color"; public static final String HEADER_VALUE = "red"; /** * 编号 */ private Integer id; public Demo04Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo04Message{" + "id=" + id + '}'; } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo01Producer.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.producer; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo01Message; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.AsyncResult; import org.springframework.stereotype.Component; import org.springframework.util.concurrent.ListenableFuture; @Component public class Demo01Producer { @Autowired private RabbitTemplate rabbitTemplate; public void syncSend(Integer id) { // 创建 Demo01Message 消息 Demo01Message message = new Demo01Message(); message.setId(id); // 同步发送消息 rabbitTemplate.convertAndSend(Demo01Message.EXCHANGE, Demo01Message.ROUTING_KEY, message); } public void syncSendDefault(Integer id) { // 创建 Demo01Message 消息 Demo01Message message = new Demo01Message(); message.setId(id); // 同步发送消息 rabbitTemplate.convertAndSend(Demo01Message.QUEUE, message); } @Async public ListenableFuture asyncSend(Integer id) { try { // 发送消息 this.syncSend(id); // 返回成功的 Future return AsyncResult.forValue(null); } catch (Throwable ex) { // 返回异常的 Future return AsyncResult.forExecutionException(ex); } } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo02Producer.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.producer; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo02Message; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class Demo02Producer { @Autowired private RabbitTemplate rabbitTemplate; public void syncSend(Integer id, String routingKey) { // 创建 Demo02Message 消息 Demo02Message message = new Demo02Message(); message.setId(id); // 同步发送消息 rabbitTemplate.convertAndSend(Demo02Message.EXCHANGE, routingKey, message); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo03Producer.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.producer; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo03Message; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class Demo03Producer { @Autowired private RabbitTemplate rabbitTemplate; public void syncSend(Integer id) { // 创建 Demo03Message 消息 Demo03Message message = new Demo03Message(); message.setId(id); // 同步发送消息 rabbitTemplate.convertAndSend(Demo03Message.EXCHANGE, null, message); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo04Producer.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.producer; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo04Message; import org.springframework.amqp.core.Message; import org.springframework.amqp.core.MessageProperties; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class Demo04Producer { @Autowired private RabbitTemplate rabbitTemplate; public void syncSend(Integer id, String headerValue) { // 创建 MessageProperties 属性 MessageProperties messageProperties = new MessageProperties(); messageProperties.setHeader(Demo04Message.HEADER_KEY, headerValue); // 设置 header // 创建 Message 消息 Message message = rabbitTemplate.getMessageConverter().toMessage( new Demo04Message().setId(id), messageProperties); // 同步发送消息 rabbitTemplate.send(Demo04Message.EXCHANGE, null, message); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo/src/main/resources/application.yaml ================================================ spring: # RabbitMQ 配置项,对应 RabbitProperties 配置类 rabbitmq: host: 127.0.0.1 # RabbitMQ 服务的地址 port: 5672 # RabbitMQ 服务的端口 username: guest # RabbitMQ 服务的账号 password: guest # RabbitMQ 服务的密码 ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/package-info.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo; ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo01ProducerTest.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.producer; import cn.iocoder.springboot.lab04.rabbitmqdemo.Application; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.util.concurrent.ListenableFutureCallback; import java.util.concurrent.CountDownLatch; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class Demo01ProducerTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private Demo01Producer producer; @Test public void testSyncSend() throws InterruptedException { int id = (int) (System.currentTimeMillis() / 1000); producer.syncSend(id); logger.info("[testSyncSend][发送编号:[{}] 发送成功]", id); // 阻塞等待,保证消费 new CountDownLatch(1).await(); } @Test public void tesSyncSendDefault() throws InterruptedException { int id = (int) (System.currentTimeMillis() / 1000); producer.syncSendDefault(id); logger.info("[tesSyncSendDefault][发送编号:[{}] 发送成功]", id); // 阻塞等待,保证消费 new CountDownLatch(1).await(); } @Test public void testAsyncSend() throws InterruptedException { int id = (int) (System.currentTimeMillis() / 1000); producer.asyncSend(id).addCallback(new ListenableFutureCallback() { @Override public void onFailure(Throwable e) { logger.info("[testASyncSend][发送编号:[{}] 发送异常]]", id, e); } @Override public void onSuccess(Void aVoid) { logger.info("[testASyncSend][发送编号:[{}] 发送成功,发送成功]", id); } }); logger.info("[testASyncSend][发送编号:[{}] 调用完成]", id); // 阻塞等待,保证消费 new CountDownLatch(1).await(); } @Test public void nothing() throws InterruptedException { // 阻塞等待,保证消费 new CountDownLatch(1).await(); } @Test public void nothing02() throws InterruptedException { for (int i = 0; i < 1000; i++) { int id = (int) (System.currentTimeMillis() / 1000); producer.syncSend(id); logger.info("[testSyncSend][发送编号:[{}] 发送成功]", id); Thread.sleep(5000L); } // 阻塞等待,保证消费 new CountDownLatch(1).await(); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo02ProducerTest.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.producer; import cn.iocoder.springboot.lab04.rabbitmqdemo.Application; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.concurrent.CountDownLatch; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class Demo02ProducerTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private Demo02Producer producer; @Test public void testSyncSendSuccess() throws InterruptedException { int id = (int) (System.currentTimeMillis() / 1000); producer.syncSend(id, "da.yu.nai"); logger.info("[testSyncSend][发送编号:[{}] 发送成功]", id); // 阻塞等待,保证消费 new CountDownLatch(1).await(); } @Test public void testSyncSendFailure() throws InterruptedException { int id = (int) (System.currentTimeMillis() / 1000); producer.syncSend(id, "yu.nai.shuai"); logger.info("[testSyncSend][发送编号:[{}] 发送成功]", id); // 阻塞等待,保证消费 new CountDownLatch(1).await(); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo03ProducerTest.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.producer; import cn.iocoder.springboot.lab04.rabbitmqdemo.Application; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.concurrent.CountDownLatch; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class Demo03ProducerTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private Demo03Producer producer; @Test public void testSyncSend() throws InterruptedException { int id = (int) (System.currentTimeMillis() / 1000); producer.syncSend(id); logger.info("[testSyncSend][发送编号:[{}] 发送成功]", id); // 阻塞等待,保证消费 new CountDownLatch(1).await(); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo04ProducerTest.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.producer; import cn.iocoder.springboot.lab04.rabbitmqdemo.Application; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo04Message; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.concurrent.CountDownLatch; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class Demo04ProducerTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private Demo04Producer producer; @Test public void testSyncSendSuccess() throws InterruptedException { int id = (int) (System.currentTimeMillis() / 1000); producer.syncSend(id, Demo04Message.HEADER_VALUE); logger.info("[testSyncSend][发送编号:[{}] 发送成功]", id); // 阻塞等待,保证消费 new CountDownLatch(1).await(); } @Test public void testSyncSendFailure() throws InterruptedException { int id = (int) (System.currentTimeMillis() / 1000); producer.syncSend(id, "error"); logger.info("[testSyncSend][发送编号:[{}] 发送成功]", id); // 阻塞等待,保证消费 new CountDownLatch(1).await(); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-ack/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-04-rabbitmq-demo-ack org.springframework.boot spring-boot-starter-amqp org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-ack/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/Application.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableAsync; @SpringBootApplication @EnableAsync // 开启异步 public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-ack/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/config/RabbitConfig.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.config; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo12Message; import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.BindingBuilder; import org.springframework.amqp.core.DirectExchange; import org.springframework.amqp.core.Queue; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class RabbitConfig { /** * Direct Exchange 示例的配置类 */ public static class DirectExchangeDemoConfiguration { // 创建 Queue @Bean public Queue demo12Queue() { return new Queue(Demo12Message.QUEUE, // Queue 名字 true, // durable: 是否持久化 false, // exclusive: 是否排它 false); // autoDelete: 是否自动删除 } // 创建 Direct Exchange @Bean public DirectExchange demo12Exchange() { return new DirectExchange(Demo12Message.EXCHANGE, true, // durable: 是否持久化 false); // exclusive: 是否排它 } // 创建 Binding // Exchange:Demo12Message.EXCHANGE // Routing key:Demo12Message.ROUTING_KEY // Queue:Demo12Message.QUEUE @Bean public Binding demo12Binding() { return BindingBuilder.bind(demo12Queue()).to(demo12Exchange()).with(Demo12Message.ROUTING_KEY); } } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-ack/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/Demo12Consumer.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo12Message; import com.rabbitmq.client.Channel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.amqp.support.AmqpHeaders; import org.springframework.messaging.handler.annotation.Header; import org.springframework.stereotype.Component; import java.io.IOException; @Component @RabbitListener(queues = Demo12Message.QUEUE) public class Demo12Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @RabbitHandler public void onMessage(Demo12Message message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) throws IOException { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); // 提交消费进度 if (message.getId() % 2 == 1) { // ack 确认消息 // 第二个参数 multiple ,用于批量确认消息,为了减少网络流量,手动确认可以被批处。 // 1. 当 multiple 为 true 时,则可以一次性确认 deliveryTag 小于等于传入值的所有消息 // 2. 当 multiple 为 false 时,则只确认当前 deliveryTag 对应的消息 channel.basicAck(deliveryTag, false); } } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-ack/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/message/Demo12Message.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.message; import java.io.Serializable; public class Demo12Message implements Serializable { public static final String QUEUE = "QUEUE_DEMO_12"; public static final String EXCHANGE = "EXCHANGE_DEMO_12"; public static final String ROUTING_KEY = "ROUTING_KEY_12"; /** * 编号 */ private Integer id; public Demo12Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo12Message{" + "id=" + id + '}'; } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-ack/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo12Producer.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.producer; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo12Message; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class Demo12Producer { @Autowired private RabbitTemplate rabbitTemplate; public void syncSend(Integer id) { // 创建 Demo12Message 消息 Demo12Message message = new Demo12Message(); message.setId(id); // 同步发送消息 rabbitTemplate.convertAndSend(Demo12Message.EXCHANGE, Demo12Message.ROUTING_KEY, message); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-ack/src/main/resources/application.yaml ================================================ spring: # RabbitMQ 配置项,对应 RabbitProperties 配置类 rabbitmq: host: 127.0.0.1 # RabbitMQ 服务的地址 port: 5672 # RabbitMQ 服务的端口 username: guest # RabbitMQ 服务的账号 password: guest # RabbitMQ 服务的密码 listener: simple: acknowledge-mode: manual # 配置 Consumer 手动提交 ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-ack/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/package-info.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo; ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-ack/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo12ProducerTest.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.producer; import cn.iocoder.springboot.lab04.rabbitmqdemo.Application; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.concurrent.CountDownLatch; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class Demo12ProducerTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private Demo12Producer producer; @Test public void testSyncSend() throws InterruptedException { for (int id = 1; id <= 2; id++) { producer.syncSend(id); logger.info("[testSyncSend][发送编号:[{}] 发送成功]", id); } // 阻塞等待,保证消费 new CountDownLatch(1).await(); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-batch/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-04-rabbitmq-demo-batch org.springframework.boot spring-boot-starter-amqp org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-batch/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/Application.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableAsync; @SpringBootApplication @EnableAsync // 开启异步 public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-batch/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/config/RabbitConfig.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.config; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo05Message; import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.BindingBuilder; import org.springframework.amqp.core.DirectExchange; import org.springframework.amqp.core.Queue; import org.springframework.amqp.rabbit.batch.BatchingStrategy; import org.springframework.amqp.rabbit.batch.SimpleBatchingStrategy; import org.springframework.amqp.rabbit.connection.ConnectionFactory; import org.springframework.amqp.rabbit.core.BatchingRabbitTemplate; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.TaskScheduler; import org.springframework.scheduling.concurrent.ConcurrentTaskScheduler; @Configuration public class RabbitConfig { /** * Direct Exchange 示例的配置类 */ public static class DirectExchangeDemoConfiguration { // 创建 Queue @Bean public Queue demo05Queue() { return new Queue(Demo05Message.QUEUE, // Queue 名字 true, // durable: 是否持久化 false, // exclusive: 是否排它 false); // autoDelete: 是否自动删除 } // 创建 Direct Exchange @Bean public DirectExchange demo05Exchange() { return new DirectExchange(Demo05Message.EXCHANGE, true, // durable: 是否持久化 false); // exclusive: 是否排它 } // 创建 Binding // Exchange:Demo05Message.EXCHANGE // Routing key:Demo05Message.ROUTING_KEY // Queue:Demo05Message.QUEUE @Bean public Binding demo05Binding() { return BindingBuilder.bind(demo05Queue()).to(demo05Exchange()).with(Demo05Message.ROUTING_KEY); } } @Bean public BatchingRabbitTemplate batchRabbitTemplate(ConnectionFactory connectionFactory) { // 创建 BatchingStrategy 对象,代表批量策略 int batchSize = 16384; // 超过收集的消息数量的最大条数。 int bufferLimit = 33554432; // 每次批量发送消息的最大内存 int timeout = 30000; // 超过收集的时间的最大等待时长,单位:毫秒 BatchingStrategy batchingStrategy = new SimpleBatchingStrategy(batchSize, bufferLimit, timeout); // 创建 TaskScheduler 对象,用于实现超时发送的定时器 TaskScheduler taskScheduler = new ConcurrentTaskScheduler(); // 创建 BatchingRabbitTemplate 对象 BatchingRabbitTemplate batchTemplate = new BatchingRabbitTemplate(batchingStrategy, taskScheduler); batchTemplate.setConnectionFactory(connectionFactory); return batchTemplate; } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-batch/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/Demo05Consumer.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo05Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; @Component @RabbitListener(queues = Demo05Message.QUEUE) public class Demo05Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @RabbitHandler public void onMessage(Demo05Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-batch/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/message/Demo05Message.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.message; import java.io.Serializable; public class Demo05Message implements Serializable { public static final String QUEUE = "QUEUE_DEMO_05"; public static final String EXCHANGE = "EXCHANGE_DEMO_05"; public static final String ROUTING_KEY = "ROUTING_KEY_05"; /** * 编号 */ private Integer id; public Demo05Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo05Message{" + "id=" + id + '}'; } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-batch/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo05Producer.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.producer; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo05Message; import org.springframework.amqp.rabbit.core.BatchingRabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class Demo05Producer { @Autowired private BatchingRabbitTemplate batchingRabbitTemplate; public void syncSend(Integer id) { // 创建 Demo05Message 消息 Demo05Message message = new Demo05Message(); message.setId(id); // 同步发送消息 batchingRabbitTemplate.convertAndSend(Demo05Message.EXCHANGE, Demo05Message.ROUTING_KEY, message); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-batch/src/main/resources/application.yaml ================================================ spring: # RabbitMQ 配置项,对应 RabbitProperties 配置类 rabbitmq: host: 127.0.0.1 # RabbitMQ 服务的地址 port: 5672 # RabbitMQ 服务的端口 username: guest # RabbitMQ 服务的账号 password: guest # RabbitMQ 服务的密码 ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-batch/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/package-info.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo; ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-batch/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo05ProducerTest.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.producer; import cn.iocoder.springboot.lab04.rabbitmqdemo.Application; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.concurrent.CountDownLatch; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class Demo05ProducerTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private Demo05Producer producer; @Test public void testSyncSend() throws InterruptedException { for (int i = 0; i < 3; i++) { // 同步发送消息 int id = (int) (System.currentTimeMillis() / 1000); producer.syncSend(id); // 故意每条消息之间,隔离 10 秒 logger.info("[testSyncSend][发送编号:[{}] 发送成功]", id); Thread.sleep(10 * 1000L); } // 阻塞等待,保证消费 new CountDownLatch(1).await(); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-batch-consume/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-04-rabbitmq-demo-batch-consume org.springframework.boot spring-boot-starter-amqp org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-batch-consume/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/Application.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableAsync; @SpringBootApplication @EnableAsync // 开启异步 public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-batch-consume/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/config/RabbitConfig.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.config; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo05Message; import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.BindingBuilder; import org.springframework.amqp.core.DirectExchange; import org.springframework.amqp.core.Queue; import org.springframework.amqp.rabbit.batch.BatchingStrategy; import org.springframework.amqp.rabbit.batch.SimpleBatchingStrategy; import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory; import org.springframework.amqp.rabbit.connection.ConnectionFactory; import org.springframework.amqp.rabbit.core.BatchingRabbitTemplate; import org.springframework.boot.autoconfigure.amqp.SimpleRabbitListenerContainerFactoryConfigurer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.TaskScheduler; import org.springframework.scheduling.concurrent.ConcurrentTaskScheduler; @Configuration public class RabbitConfig { /** * Direct Exchange 示例的配置类 */ public static class DirectExchangeDemoConfiguration { // 创建 Queue @Bean public Queue demo05Queue() { return new Queue(Demo05Message.QUEUE, // Queue 名字 true, // durable: 是否持久化 false, // exclusive: 是否排它 false); // autoDelete: 是否自动删除 } // 创建 Direct Exchange @Bean public DirectExchange demo05Exchange() { return new DirectExchange(Demo05Message.EXCHANGE, true, // durable: 是否持久化 false); // exclusive: 是否排它 } // 创建 Binding // Exchange:Demo05Message.EXCHANGE // Routing key:Demo05Message.ROUTING_KEY // Queue:Demo05Message.QUEUE @Bean public Binding demo05Binding() { return BindingBuilder.bind(demo05Queue()).to(demo05Exchange()).with(Demo05Message.ROUTING_KEY); } } @Bean public BatchingRabbitTemplate batchRabbitTemplate(ConnectionFactory connectionFactory) { // 创建 BatchingStrategy 对象,代表批量策略 int batchSize = 16384; // 超过收集的消息数量的最大条数。 int bufferLimit = 33554432; // 每次批量发送消息的最大内存 int timeout = 30000; // 超过收集的时间的最大等待时长,单位:毫秒 BatchingStrategy batchingStrategy = new SimpleBatchingStrategy(batchSize, bufferLimit, timeout); // 创建 TaskScheduler 对象,用于实现超时发送的定时器 TaskScheduler taskScheduler = new ConcurrentTaskScheduler(); // 创建 BatchingRabbitTemplate 对象 BatchingRabbitTemplate batchTemplate = new BatchingRabbitTemplate(batchingStrategy, taskScheduler); batchTemplate.setConnectionFactory(connectionFactory); return batchTemplate; } @Bean(name = "consumerBatchContainerFactory") public SimpleRabbitListenerContainerFactory consumerBatchContainerFactory( SimpleRabbitListenerContainerFactoryConfigurer configurer, ConnectionFactory connectionFactory) { // 创建 SimpleRabbitListenerContainerFactory 对象 SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory(); configurer.configure(factory, connectionFactory); // 额外添加批量消费的属性 factory.setBatchListener(true); return factory; } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-batch-consume/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/Demo05Consumer.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo05Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; import java.util.List; @Component @RabbitListener(queues = Demo05Message.QUEUE, containerFactory = "consumerBatchContainerFactory") public class Demo05Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @RabbitHandler public void onMessage(List messages) { logger.info("[onMessage][线程编号:{} 消息数量:{}]", Thread.currentThread().getId(), messages.size()); } // @RabbitHandler(isDefault = true) // public void onMessageX(List messages) { // logger.info("[onMessage][线程编号:{} 消息数量:{}]", Thread.currentThread().getId(), messages.size()); // } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-batch-consume/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/message/Demo05Message.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.message; import java.io.Serializable; public class Demo05Message implements Serializable { public static final String QUEUE = "QUEUE_DEMO_05"; public static final String EXCHANGE = "EXCHANGE_DEMO_05"; public static final String ROUTING_KEY = "ROUTING_KEY_05"; /** * 编号 */ private Integer id; public Demo05Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo05Message{" + "id=" + id + '}'; } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-batch-consume/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo06Producer.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.producer; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo05Message; import org.springframework.amqp.rabbit.core.BatchingRabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class Demo06Producer { @Autowired private BatchingRabbitTemplate batchingRabbitTemplate; public void syncSend(Integer id) { // 创建 Demo05Message 消息 Demo05Message message = new Demo05Message(); message.setId(id); // 同步发送消息 batchingRabbitTemplate.convertAndSend(Demo05Message.EXCHANGE, Demo05Message.ROUTING_KEY, message); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-batch-consume/src/main/resources/application.yaml ================================================ spring: # RabbitMQ 配置项,对应 RabbitProperties 配置类 rabbitmq: host: 127.0.0.1 # RabbitMQ 服务的地址 port: 5672 # RabbitMQ 服务的端口 username: guest # RabbitMQ 服务的账号 password: guest # RabbitMQ 服务的密 ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-batch-consume/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/package-info.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo; ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-batch-consume/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo05ProducerTest.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.producer; import cn.iocoder.springboot.lab04.rabbitmqdemo.Application; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.concurrent.CountDownLatch; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class Demo05ProducerTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private Demo06Producer producer; @Test public void testSyncSend() throws InterruptedException { for (int i = 0; i < 3; i++) { // 同步发送消息 int id = (int) (System.currentTimeMillis() / 1000); producer.syncSend(id); // 故意每条消息之间,隔离 10 秒 logger.info("[testSyncSend][发送编号:[{}] 发送成功]", id); Thread.sleep(10 * 1000L); } // 阻塞等待,保证消费 new CountDownLatch(1).await(); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-batch-consume-02/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-04-rabbitmq-demo-batch-consume-02 org.springframework.boot spring-boot-starter-amqp org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-batch-consume-02/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/Application.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableAsync; @SpringBootApplication @EnableAsync // 开启异步 public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-batch-consume-02/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/config/RabbitConfig.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.config; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo06Message; import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.BindingBuilder; import org.springframework.amqp.core.DirectExchange; import org.springframework.amqp.core.Queue; import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory; import org.springframework.amqp.rabbit.connection.ConnectionFactory; import org.springframework.boot.autoconfigure.amqp.SimpleRabbitListenerContainerFactoryConfigurer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class RabbitConfig { /** * Direct Exchange 示例的配置类 */ public static class DirectExchangeDemoConfiguration { // 创建 Queue @Bean public Queue demo06Queue() { return new Queue(Demo06Message.QUEUE, // Queue 名字 true, // durable: 是否持久化 false, // exclusive: 是否排它 false); // autoDelete: 是否自动删除 } // 创建 Direct Exchange @Bean public DirectExchange demo06Exchange() { return new DirectExchange(Demo06Message.EXCHANGE, true, // durable: 是否持久化 false); // exclusive: 是否排它 } // 创建 Binding // Exchange:Demo06Message.EXCHANGE // Routing key:Demo06Message.ROUTING_KEY // Queue:Demo06Message.QUEUE @Bean public Binding demo06Binding() { return BindingBuilder.bind(demo06Queue()).to(demo06Exchange()).with(Demo06Message.ROUTING_KEY); } } @Bean(name = "consumerBatchContainerFactory") public SimpleRabbitListenerContainerFactory consumerBatchContainerFactory( SimpleRabbitListenerContainerFactoryConfigurer configurer, ConnectionFactory connectionFactory) { // 创建 SimpleRabbitListenerContainerFactory 对象 SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory(); configurer.configure(factory, connectionFactory); // 额外添加批量消费的属性 factory.setBatchListener(true); factory.setBatchSize(10); factory.setReceiveTimeout(30 * 1000L); factory.setConsumerBatchEnabled(true); return factory; } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-batch-consume-02/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/Demo06Consumer.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo06Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; import java.util.List; @Component @RabbitListener(queues = Demo06Message.QUEUE, containerFactory = "consumerBatchContainerFactory") public class Demo06Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @RabbitHandler public void onMessage(List messages) { logger.info("[onMessage][线程编号:{} 消息数量:{}]", Thread.currentThread().getId(), messages.size()); } // @RabbitHandler(isDefault = true) // public void onMessageX(List messages) { // logger.info("[onMessage][线程编号:{} 消息数量:{}]", Thread.currentThread().getId(), messages.size()); // } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-batch-consume-02/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/message/Demo06Message.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.message; import java.io.Serializable; public class Demo06Message implements Serializable { public static final String QUEUE = "QUEUE_DEMO_06"; public static final String EXCHANGE = "EXCHANGE_DEMO_06"; public static final String ROUTING_KEY = "ROUTING_KEY_06"; /** * 编号 */ private Integer id; public Demo06Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo06Message{" + "id=" + id + '}'; } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-batch-consume-02/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo06Producer.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.producer; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo06Message; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class Demo06Producer { @Autowired private RabbitTemplate rabbitTemplate; public void syncSend(Integer id) { // 创建 Demo06Message 消息 Demo06Message message = new Demo06Message(); message.setId(id); // 同步发送消息 rabbitTemplate.convertAndSend(Demo06Message.EXCHANGE, Demo06Message.ROUTING_KEY, message); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-batch-consume-02/src/main/resources/application.yaml ================================================ spring: # RabbitMQ 配置项,对应 RabbitProperties 配置类 rabbitmq: host: 127.0.0.1 # RabbitMQ 服务的地址 port: 5672 # RabbitMQ 服务的端口 username: guest # RabbitMQ 服务的账号 password: guest # RabbitMQ 服务的密码 ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-batch-consume-02/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/package-info.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo; ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-batch-consume-02/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo06ProducerTest.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.producer; import cn.iocoder.springboot.lab04.rabbitmqdemo.Application; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.concurrent.CountDownLatch; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class Demo06ProducerTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private Demo06Producer producer; @Test public void testSyncSend01() throws InterruptedException { // 发送 3 条消息 this.testSyncSend(3); } @Test public void testSyncSen02() throws InterruptedException { // 发送 10 条消息 this.testSyncSend(10); } private void testSyncSend(int n) throws InterruptedException { for (int i = 0; i < n; i++) { // 同步发送消息 int id = (int) (System.currentTimeMillis() / 1000); producer.syncSend(id); logger.info("[testSyncSendMore][发送编号:[{}] 发送成功]", id); } // 阻塞等待,保证消费 new CountDownLatch(1).await(); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-concurrency/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-04-rabbitmq-demo-concurrency org.springframework.boot spring-boot-starter-amqp org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-concurrency/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/Application.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableAsync; @SpringBootApplication @EnableAsync // 开启异步 public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-concurrency/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/config/RabbitConfig.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.config; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo09Message; import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.BindingBuilder; import org.springframework.amqp.core.DirectExchange; import org.springframework.amqp.core.Queue; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class RabbitConfig { /** * Direct Exchange 示例的配置类 */ public static class DirectExchangeDemoConfiguration { // 创建 Queue @Bean public Queue demo09Queue() { return new Queue(Demo09Message.QUEUE, // Queue 名字 true, // durable: 是否持久化 false, // exclusive: 是否排它 false); // autoDelete: 是否自动删除 } // 创建 Direct Exchange @Bean public DirectExchange demo09Exchange() { return new DirectExchange(Demo09Message.EXCHANGE, true, // durable: 是否持久化 false); // exclusive: 是否排它 } // 创建 Binding // Exchange:Demo09Message.EXCHANGE // Routing key:Demo09Message.ROUTING_KEY // Queue:Demo09Message.QUEUE @Bean public Binding demo09Binding() { return BindingBuilder.bind(demo09Queue()).to(demo09Exchange()).with(Demo09Message.ROUTING_KEY); } } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-concurrency/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/Demo09Consumer.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo09Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; @Component //@RabbitListener(queues = Demo09Message.QUEUE) @RabbitListener(queues = Demo09Message.QUEUE, concurrency = "2") //@RabbitListener(queues = {"QUEUE_DEMO_01", "QUEUE_DEMO_02"}) public class Demo09Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @RabbitHandler public void onMessage(Demo09Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-concurrency/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/message/Demo09Message.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.message; import java.io.Serializable; public class Demo09Message implements Serializable { public static final String QUEUE = "QUEUE_DEMO_09"; public static final String EXCHANGE = "EXCHANGE_DEMO_09"; public static final String ROUTING_KEY = "ROUTING_KEY_09"; /** * 编号 */ private Integer id; public Demo09Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo09Message{" + "id=" + id + '}'; } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-concurrency/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo09Producer.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.producer; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo09Message; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class Demo09Producer { @Autowired private RabbitTemplate rabbitTemplate; public void syncSend(Integer id) { // 创建 Demo09Message 消息 Demo09Message message = new Demo09Message(); message.setId(id); // 同步发送消息 rabbitTemplate.convertAndSend(Demo09Message.EXCHANGE, Demo09Message.ROUTING_KEY, message); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-concurrency/src/main/resources/application.yaml ================================================ spring: # RabbitMQ 配置项,对应 RabbitProperties 配置类 rabbitmq: host: 127.0.0.1 # RabbitMQ 服务的地址 port: 5672 # RabbitMQ 服务的端口 username: guest # RabbitMQ 服务的账号 password: guest # RabbitMQ 服务的密码 listener: type: simple # 选择的 ListenerContainer 的类型。默认为 direct 类型 simple: concurrency: 2 # 每个 @ListenerContainer 的并发消费的线程数 max-concurrency: 10 # 每个 @ListenerCon 允许的并发消费的线程数 # direct: # consumers-per-queue: 2 # 对于每一个 @RabbitListener ,一个 Queue ,对应创建几个 Consumer 。 ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-concurrency/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/package-info.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo; ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-concurrency/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo09ProducerTest.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.producer; import cn.iocoder.springboot.lab04.rabbitmqdemo.Application; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.concurrent.CountDownLatch; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class Demo09ProducerTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private Demo09Producer producer; @Test public void testSyncSend() throws InterruptedException { for (int i = 0; i < 10; i++) { int id = (int) (System.currentTimeMillis() / 1000); producer.syncSend(id); // logger.info("[testSyncSend][发送编号:[{}] 发送成功]", id); } // 阻塞等待,保证消费 new CountDownLatch(1).await(); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-confirm/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-04-rabbitmq-demo-confirm org.springframework.boot spring-boot-starter-amqp org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-confirm/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/Application.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-confirm/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/config/RabbitConfig.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.config; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo13Message; import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.BindingBuilder; import org.springframework.amqp.core.DirectExchange; import org.springframework.amqp.core.Queue; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class RabbitConfig { /** * Direct Exchange 示例的配置类 */ public static class DirectExchangeDemoConfiguration { // 创建 Queue @Bean public Queue demo13Queue() { return new Queue(Demo13Message.QUEUE, // Queue 名字 true, // durable: 是否持久化 false, // exclusive: 是否排它 false); // autoDelete: 是否自动删除 } // 创建 Direct Exchange @Bean public DirectExchange demo13Exchange() { return new DirectExchange(Demo13Message.EXCHANGE, true, // durable: 是否持久化 false); // exclusive: 是否排它 } // 创建 Binding // Exchange:Demo13Message.EXCHANGE // Routing key:Demo13Message.ROUTING_KEY // Queue:Demo13Message.QUEUE @Bean public Binding demo13Binding() { return BindingBuilder.bind(demo13Queue()).to(demo13Exchange()).with(Demo13Message.ROUTING_KEY); } } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-confirm/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/Demo13Consumer.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo13Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; @Component @RabbitListener(queues = Demo13Message.QUEUE) public class Demo13Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @RabbitHandler public void onMessage(Demo13Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-confirm/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/message/Demo13Message.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.message; import java.io.Serializable; public class Demo13Message implements Serializable { public static final String QUEUE = "QUEUE_DEMO_13"; public static final String EXCHANGE = "EXCHANGE_DEMO_13"; public static final String ROUTING_KEY = "ROUTING_KEY_13"; /** * 编号 */ private Integer id; public Demo13Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo13Message{" + "id=" + id + '}'; } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-confirm/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo13Producer.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.producer; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo13Message; import com.rabbitmq.client.ConfirmCallback; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.core.RabbitOperations; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.io.IOException; @Component public class Demo13Producer { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private RabbitTemplate rabbitTemplate; public void syncSend(Integer id) { // 创建 Demo13Message 消息 Demo13Message message = new Demo13Message(); message.setId(id); // 同步发送消息 rabbitTemplate.invoke(new RabbitOperations.OperationsCallback() { @Override public Object doInRabbit(RabbitOperations operations) { // 同步发送消息 operations.convertAndSend(Demo13Message.EXCHANGE, Demo13Message.ROUTING_KEY, message); logger.info("[doInRabbit][发送消息完成]"); // 等待确认 operations.waitForConfirms(0); // timeout 参数,如果传递 0 ,表示无限等待 logger.info("[doInRabbit][等待 Confirm 完成]"); return null; } }, new ConfirmCallback() { @Override public void handle(long deliveryTag, boolean multiple) throws IOException { logger.info("[handle][Confirm 成功]"); } }, new ConfirmCallback() { @Override public void handle(long deliveryTag, boolean multiple) throws IOException { logger.info("[handle][Confirm 失败]"); } }); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-confirm/src/main/resources/application.yaml ================================================ spring: # RabbitMQ 配置项,对应 RabbitProperties 配置类 rabbitmq: host: 127.0.0.1 # RabbitMQ 服务的地址 port: 5672 # RabbitMQ 服务的端口 username: guest # RabbitMQ 服务的账号 password: guest # RabbitMQ 服务的密码 publisher-confirm-type: simple # 设置 Confirm 类型为 SIMPLE 。 ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-confirm/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/package-info.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo; ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-confirm/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo13ProducerTest.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.producer; import cn.iocoder.springboot.lab04.rabbitmqdemo.Application; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.concurrent.CountDownLatch; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class Demo13ProducerTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private Demo13Producer producer; @Test public void testSyncSend() throws InterruptedException { int id = (int) (System.currentTimeMillis() / 1000); producer.syncSend(id); logger.info("[testSyncSend][发送编号:[{}] 发送成功]", id); // 阻塞等待,保证消费 new CountDownLatch(1).await(); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-confirm-async/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-04-rabbitmq-demo-confirm-async org.springframework.boot spring-boot-starter-amqp org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-confirm-async/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/Application.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-confirm-async/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/config/RabbitConfig.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.config; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo13Message; import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.BindingBuilder; import org.springframework.amqp.core.DirectExchange; import org.springframework.amqp.core.Queue; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class RabbitConfig { /** * Direct Exchange 示例的配置类 */ public static class DirectExchangeDemoConfiguration { // 创建 Queue @Bean public Queue demo13Queue() { return new Queue(Demo13Message.QUEUE, // Queue 名字 true, // durable: 是否持久化 false, // exclusive: 是否排它 false); // autoDelete: 是否自动删除 } // 创建 Direct Exchange @Bean public DirectExchange demo13Exchange() { return new DirectExchange(Demo13Message.EXCHANGE, true, // durable: 是否持久化 false); // exclusive: 是否排它 } // 创建 Binding // Exchange:Demo13Message.EXCHANGE // Routing key:Demo13Message.ROUTING_KEY // Queue:Demo13Message.QUEUE @Bean public Binding demo13Binding() { return BindingBuilder.bind(demo13Queue()).to(demo13Exchange()).with(Demo13Message.ROUTING_KEY); } } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-confirm-async/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/Demo13Consumer.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo13Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; @Component @RabbitListener(queues = Demo13Message.QUEUE) public class Demo13Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @RabbitHandler public void onMessage(Demo13Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-confirm-async/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/core/RabbitProducerConfirmCallback.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.core; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.connection.CorrelationData; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.stereotype.Component; @Component public class RabbitProducerConfirmCallback implements RabbitTemplate.ConfirmCallback { private Logger logger = LoggerFactory.getLogger(getClass()); public RabbitProducerConfirmCallback(RabbitTemplate rabbitTemplate) { rabbitTemplate.setConfirmCallback(this); } @Override public void confirm(CorrelationData correlationData, boolean ack, String cause) { if (ack) { logger.info("[confirm][Confirm 成功 correlationData: {}]", correlationData); } else { logger.error("[confirm][Confirm 失败 correlationData: {} cause: {}]", correlationData, cause); } } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-confirm-async/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/core/RabbitProducerReturnCallback.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.core; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.core.Message; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.stereotype.Component; @Component public class RabbitProducerReturnCallback implements RabbitTemplate.ReturnCallback { private Logger logger = LoggerFactory.getLogger(getClass()); public RabbitProducerReturnCallback(RabbitTemplate rabbitTemplate) { rabbitTemplate.setReturnCallback(this); } @Override public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) { logger.error("[returnedMessage][message: [{}] replyCode: [{}] replyText: [{}] exchange: [{}] routingKey: [{}]]", message, replyCode, replyText, exchange, routingKey); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-confirm-async/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/message/Demo13Message.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.message; import java.io.Serializable; public class Demo13Message implements Serializable { public static final String QUEUE = "QUEUE_DEMO_13"; public static final String EXCHANGE = "EXCHANGE_DEMO_13"; public static final String ROUTING_KEY = "ROUTING_KEY_13"; /** * 编号 */ private Integer id; public Demo13Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo13Message{" + "id=" + id + '}'; } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-confirm-async/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo13Producer.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.producer; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo13Message; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class Demo13Producer { @Autowired private RabbitTemplate rabbitTemplate; public void syncSend(Integer id) { // 创建 Demo13Message 消息 Demo13Message message = new Demo13Message(); message.setId(id); // 同步发送消息 rabbitTemplate.convertAndSend(Demo13Message.EXCHANGE, Demo13Message.ROUTING_KEY, message); } public void syncSendReturn(Integer id) { // 创建 Demo13Message 消息 Demo13Message message = new Demo13Message(); message.setId(id); // 同步发送消息 rabbitTemplate.convertAndSend(Demo13Message.EXCHANGE, "error", message); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-confirm-async/src/main/resources/application.yaml ================================================ spring: # RabbitMQ 配置项,对应 RabbitProperties 配置类 rabbitmq: host: 127.0.0.1 # RabbitMQ 服务的地址 port: 5672 # RabbitMQ 服务的端口 username: guest # RabbitMQ 服务的账号 password: guest # RabbitMQ 服务的密码 publisher-confirm-type: correlated # 设置 Confirm 类型为 CORRELATED 。 publisher-returns: true # ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-confirm-async/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/package-info.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo; ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-confirm-async/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo13ProducerTest.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.producer; import cn.iocoder.springboot.lab04.rabbitmqdemo.Application; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.concurrent.CountDownLatch; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class Demo13ProducerTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private Demo13Producer producer; @Test public void testSyncSend() throws InterruptedException { int id = (int) (System.currentTimeMillis() / 1000); producer.syncSend(id); logger.info("[testSyncSend][发送编号:[{}] 发送成功]", id); // 阻塞等待,保证消费 new CountDownLatch(1).await(); } @Test public void testSyncSendReturn() throws InterruptedException { int id = (int) (System.currentTimeMillis() / 1000); producer.syncSendReturn(id); logger.info("[testSyncSendReturn][发送编号:[{}] 发送成功]", id); // 阻塞等待,保证消费 new CountDownLatch(1).await(); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-delay/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-04-rabbitmq-demo-delay org.springframework.boot spring-boot-starter-amqp org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-delay/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/Application.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-delay/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/config/RabbitConfig.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.config; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo08Message; import org.springframework.amqp.core.*; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class RabbitConfig { /** * Direct Exchange 示例的配置类 */ public static class DirectExchangeDemoConfiguration { // 创建 Queue @Bean public Queue demo08Queue() { return QueueBuilder.durable(Demo08Message.QUEUE) // durable: 是否持久化 .exclusive() // exclusive: 是否排它 .autoDelete() // autoDelete: 是否自动删除 .ttl(10 * 1000) // 设置队列里的默认过期时间为 10 秒 .deadLetterExchange(Demo08Message.EXCHANGE) .deadLetterRoutingKey(Demo08Message.DELAY_ROUTING_KEY) .build(); } // 创建 Delay Queue @Bean public Queue demo08DelayQueue() { return new Queue(Demo08Message.DELAY_QUEUE, // Queue 名字 true, // durable: 是否持久化 false, // exclusive: 是否排它 false); // autoDelete: 是否自动删除 } // 创建 Direct Exchange @Bean public DirectExchange demo08Exchange() { return new DirectExchange(Demo08Message.EXCHANGE, true, // durable: 是否持久化 false); // exclusive: 是否排它 } // 创建 Binding // Exchange:Demo08Message.EXCHANGE // Routing key:Demo08Message.ROUTING_KEY // Queue:Demo08Message.QUEUE @Bean public Binding demo08Binding() { return BindingBuilder.bind(demo08Queue()).to(demo08Exchange()).with(Demo08Message.ROUTING_KEY); } // 创建 Delay Binding // Exchange:Demo08Message.EXCHANGE // Routing key:Demo08Message.DELAY_ROUTING_KEY // Queue:Demo08Message.DELAY_QUEUE @Bean public Binding demo08DelayBinding() { return BindingBuilder.bind(demo08DelayQueue()).to(demo08Exchange()).with(Demo08Message.DELAY_ROUTING_KEY); } } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-delay/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/Demo08Consumer.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo08Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; @Component @RabbitListener(queues = Demo08Message.DELAY_QUEUE) public class Demo08Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @RabbitHandler public void onMessage(Demo08Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-delay/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/message/Demo08Message.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.message; import java.io.Serializable; public class Demo08Message implements Serializable { public static final String QUEUE = "QUEUE_DEMO_08"; // 正常队列 public static final String DELAY_QUEUE = "DELAY_QUEUE_DEMO_08"; // 延迟队列(死信队列) public static final String EXCHANGE = "EXCHANGE_DEMO_08"; public static final String ROUTING_KEY = "ROUTING_KEY_08"; // 正常路由键 public static final String DELAY_ROUTING_KEY = "DELAY_ROUTING_KEY_08"; // 延迟路由键(死信路由键) /** * 编号 */ private Integer id; public Demo08Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo08Message{" + "id=" + id + '}'; } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-delay/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo08Producer.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.producer; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo08Message; import org.springframework.amqp.AmqpException; import org.springframework.amqp.core.Message; import org.springframework.amqp.core.MessagePostProcessor; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class Demo08Producer { @Autowired private RabbitTemplate rabbitTemplate; public void syncSend(Integer id, Integer delay) { // 创建 Demo07Message 消息 Demo08Message message = new Demo08Message(); message.setId(id); // 同步发送消息 rabbitTemplate.convertAndSend(Demo08Message.EXCHANGE, Demo08Message.ROUTING_KEY, message, new MessagePostProcessor() { @Override public Message postProcessMessage(Message message) throws AmqpException { // 设置消息的 TTL 过期时间 if (delay != null && delay > 0) { message.getMessageProperties().setExpiration(String.valueOf(delay)); // Spring-AMQP API 设计有问题,所以传入了 String = = } return message; } }); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-delay/src/main/resources/application.yaml ================================================ spring: # RabbitMQ 配置项,对应 RabbitProperties 配置类 rabbitmq: host: 127.0.0.1 # RabbitMQ 服务的地址 port: 5672 # RabbitMQ 服务的端口 username: guest # RabbitMQ 服务的账号 password: guest # RabbitMQ 服务的密码 ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-delay/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/package-info.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo; ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-delay/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo08ProducerTest.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.producer; import cn.iocoder.springboot.lab04.rabbitmqdemo.Application; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.concurrent.CountDownLatch; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class Demo08ProducerTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private Demo08Producer producer; @Test public void testSyncSend01() throws InterruptedException { // 不设置消息的过期时间,使用队列默认的消息过期时间 this.testSyncSendDelay(null); } @Test public void testSyncSend02() throws InterruptedException { // 设置发送消息的过期时间为 5000 毫秒 this.testSyncSendDelay(5000); } private void testSyncSendDelay(Integer delay) throws InterruptedException { int id = (int) (System.currentTimeMillis() / 1000); producer.syncSend(id, delay); logger.info("[testSyncSendDelay][发送编号:[{}] 发送成功]", id); // 阻塞等待,保证消费 new CountDownLatch(1).await(); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-error-handler/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-04-rabbitmq-demo-error-handler org.springframework.boot spring-boot-starter-amqp org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-error-handler/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/Application.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-error-handler/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/config/RabbitConfig.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.config; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo16Message; import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.BindingBuilder; import org.springframework.amqp.core.DirectExchange; import org.springframework.amqp.core.Queue; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class RabbitConfig { /** * Direct Exchange 示例的配置类 */ public static class DirectExchangeDemoConfiguration { // 创建 Queue @Bean public Queue demo16Queue() { return new Queue(Demo16Message.QUEUE, // Queue 名字 true, // durable: 是否持久化 false, // exclusive: 是否排它 false); // autoDelete: 是否自动删除 } // 创建 Direct Exchange @Bean public DirectExchange demo16Exchange() { return new DirectExchange(Demo16Message.EXCHANGE, true, // durable: 是否持久化 false); // exclusive: 是否排它 } // 创建 Binding // Exchange:Demo16Message.EXCHANGE // Routing key:Demo16Message.ROUTING_KEY // Queue:Demo16Message.QUEUE @Bean public Binding demo16Binding() { return BindingBuilder.bind(demo16Queue()).to(demo16Exchange()).with(Demo16Message.ROUTING_KEY); } } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-error-handler/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/Demo16Consumer.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo16Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; @Component @RabbitListener(queues = Demo16Message.QUEUE, errorHandler = "rabbitListenerErrorHandler") //@RabbitListener(queues = Demo15Message.QUEUE) public class Demo16Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @RabbitHandler public void onMessage(Demo16Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); // 模拟消费异常 throw new RuntimeException("你猜"); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-error-handler/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/core/RabbitListenerErrorHandlerImpl.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.core; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.core.Message; import org.springframework.amqp.rabbit.listener.api.RabbitListenerErrorHandler; import org.springframework.amqp.rabbit.support.ListenerExecutionFailedException; import org.springframework.stereotype.Component; @Component("rabbitListenerErrorHandler") public class RabbitListenerErrorHandlerImpl implements RabbitListenerErrorHandler { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public Object handleError(Message amqpMessage, org.springframework.messaging.Message message, ListenerExecutionFailedException exception) { // 打印异常日志 logger.error("[handleError][amqpMessage:[{}] message:[{}]]", amqpMessage, message, exception); // 直接继续抛出异常 throw exception; } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-error-handler/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/core/RabbitLoggingErrorHandler.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.core; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory; import org.springframework.stereotype.Component; import org.springframework.util.ErrorHandler; @Component public class RabbitLoggingErrorHandler implements ErrorHandler { private Logger logger = LoggerFactory.getLogger(getClass()); public RabbitLoggingErrorHandler(SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory) { rabbitListenerContainerFactory.setErrorHandler(this); } @Override public void handleError(Throwable t) { logger.error("[handleError][发生异常]]", t); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-error-handler/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/message/Demo16Message.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.message; import java.io.Serializable; public class Demo16Message implements Serializable { public static final String QUEUE = "QUEUE_DEMO_16"; public static final String EXCHANGE = "EXCHANGE_DEMO_16"; public static final String ROUTING_KEY = "ROUTING_KEY_16"; /** * 编号 */ private Integer id; public Demo16Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo16Message{" + "id=" + id + '}'; } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-error-handler/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo16Producer.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.producer; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo16Message; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class Demo16Producer { @Autowired private RabbitTemplate rabbitTemplate; public void syncSend(Integer id) { // 创建 Demo16Message 消息 Demo16Message message = new Demo16Message(); message.setId(id); // 同步发送消息 rabbitTemplate.convertAndSend(Demo16Message.EXCHANGE, Demo16Message.ROUTING_KEY, message); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-error-handler/src/main/resources/application.yaml ================================================ spring: # RabbitMQ 配置项,对应 RabbitProperties 配置类 rabbitmq: host: 127.0.0.1 # RabbitMQ 服务的地址 port: 5672 # RabbitMQ 服务的端口 username: guest # RabbitMQ 服务的账号 password: guest # RabbitMQ 服务的密码 ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-error-handler/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/package-info.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo; ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-error-handler/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo16ProducerTest.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.producer; import cn.iocoder.springboot.lab04.rabbitmqdemo.Application; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.concurrent.CountDownLatch; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class Demo16ProducerTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private Demo16Producer producer; @Test public void testSyncSend() throws InterruptedException { int id = (int) (System.currentTimeMillis() / 1000); producer.syncSend(id); logger.info("[testSyncSend][发送编号:[{}] 发送成功]", id); // 阻塞等待,保证消费 new CountDownLatch(1).await(); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-json/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-04-rabbitmq-demo-json org.springframework.boot spring-boot-starter-amqp com.fasterxml.jackson.core jackson-databind 2.9.10.1 org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-json/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/Application.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-json/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/config/RabbitConfig.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.config; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo15Message; import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.BindingBuilder; import org.springframework.amqp.core.DirectExchange; import org.springframework.amqp.core.Queue; import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; import org.springframework.amqp.support.converter.MessageConverter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class RabbitConfig { /** * Direct Exchange 示例的配置类 */ public static class DirectExchangeDemoConfiguration { // 创建 Queue @Bean public Queue demo15Queue() { return new Queue(Demo15Message.QUEUE, // Queue 名字 true, // durable: 是否持久化 false, // exclusive: 是否排它 false); // autoDelete: 是否自动删除 } // 创建 Direct Exchange @Bean public DirectExchange demo15Exchange() { return new DirectExchange(Demo15Message.EXCHANGE, true, // durable: 是否持久化 false); // exclusive: 是否排它 } // 创建 Binding // Exchange:Demo15Message.EXCHANGE // Routing key:Demo15Message.ROUTING_KEY // Queue:Demo15Message.QUEUE @Bean public Binding demo15Binding() { return BindingBuilder.bind(demo15Queue()).to(demo15Exchange()).with(Demo15Message.ROUTING_KEY); } } @Bean public MessageConverter messageConverter() { return new Jackson2JsonMessageConverter(); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-json/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/Demo15Consumer.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo15Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.core.Message; import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; @Component @RabbitListener(queues = Demo15Message.QUEUE) public class Demo15Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @RabbitHandler(isDefault = true) public void onMessage(Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), new String(message.getBody())); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-json/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/message/Demo15Message.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.message; import java.io.Serializable; public class Demo15Message implements Serializable { public static final String QUEUE = "QUEUE_DEMO_15"; public static final String EXCHANGE = "EXCHANGE_DEMO_15"; public static final String ROUTING_KEY = "ROUTING_KEY_15"; /** * 编号 */ private Integer id; public Demo15Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo15Message{" + "id=" + id + '}'; } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-json/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo15Producer.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.producer; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo15Message; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class Demo15Producer { @Autowired private RabbitTemplate rabbitTemplate; public void syncSend(Integer id) { // 创建 Demo01Message 消息 Demo15Message message = new Demo15Message(); message.setId(id); // 同步发送消息 rabbitTemplate.convertAndSend(Demo15Message.EXCHANGE, Demo15Message.ROUTING_KEY, message); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-json/src/main/resources/application.yaml ================================================ spring: # RabbitMQ 配置项,对应 RabbitProperties 配置类 rabbitmq: host: 127.0.0.1 # RabbitMQ 服务的地址 port: 5672 # RabbitMQ 服务的端口 username: guest # RabbitMQ 服务的账号 password: guest # RabbitMQ 服务的密码 ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-json/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/package-info.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo; ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-json/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo15ProducerTest.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.producer; import cn.iocoder.springboot.lab04.rabbitmqdemo.Application; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.concurrent.CountDownLatch; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class Demo15ProducerTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private Demo15Producer producer; @Test public void testSyncSend() throws InterruptedException { int id = (int) (System.currentTimeMillis() / 1000); producer.syncSend(id); logger.info("[testSyncSend][发送编号:[{}] 发送成功]", id); // 阻塞等待,保证消费 new CountDownLatch(1).await(); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-message-model/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-04-rabbitmq-demo-message-model org.springframework.boot spring-boot-starter-amqp org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-message-model/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/Application.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-message-model/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/config/RabbitConfig.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.config; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.BroadcastMessage; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.ClusteringMessage; import org.springframework.amqp.core.TopicExchange; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class RabbitConfig { /** * 广播消费的示例的配置 */ public static class BroadcastingConfiguration { // 创建 Topic Exchange @Bean public TopicExchange broadcastingExchange() { return new TopicExchange(BroadcastMessage.EXCHANGE, true, // durable: 是否持久化 false); // exclusive: 是否排它 } } /** * 集群消费的示例的配置 */ public static class ClusteringConfiguration { // 创建 Topic Exchange @Bean public TopicExchange clusteringExchange() { return new TopicExchange(ClusteringMessage.EXCHANGE, true, // durable: 是否持久化 false); // exclusive: 是否排它 } } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-message-model/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/BroadcastConsumer.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.BroadcastMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.core.ExchangeTypes; import org.springframework.amqp.rabbit.annotation.*; import org.springframework.stereotype.Component; @Component @RabbitListener( bindings = @QueueBinding( value = @Queue( name = BroadcastMessage.QUEUE + "-" + "#{T(java.util.UUID).randomUUID()}", autoDelete = "true" ), exchange = @Exchange( name = BroadcastMessage.EXCHANGE, type = ExchangeTypes.TOPIC, declare = "false" ) ) ) public class BroadcastConsumer { private Logger logger = LoggerFactory.getLogger(getClass()); @RabbitHandler public void onMessage(BroadcastMessage message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-message-model/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/ClusteringConsumer.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.ClusteringMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.core.ExchangeTypes; import org.springframework.amqp.rabbit.annotation.*; import org.springframework.stereotype.Component; @Component @RabbitListener( bindings = @QueueBinding( value = @Queue( name = ClusteringMessage.QUEUE + "-" + "GROUP-01" ), exchange = @Exchange( name = ClusteringMessage.EXCHANGE, type = ExchangeTypes.TOPIC, declare = "false" ), key = "#" ) ) public class ClusteringConsumer { private Logger logger = LoggerFactory.getLogger(getClass()); @RabbitHandler public void onMessage(ClusteringMessage message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-message-model/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/message/BroadcastMessage.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.message; import java.io.Serializable; /** * 广播消费的消息示例 */ public class BroadcastMessage implements Serializable { public static final String QUEUE = "QUEUE_BROADCASTING"; public static final String EXCHANGE = "EXCHANGE_BROADCASTING"; /** * 编号 */ private Integer id; public BroadcastMessage setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "BroadcastMessage{" + "id=" + id + '}'; } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-message-model/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/message/ClusteringMessage.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.message; import java.io.Serializable; /** * 广播消费的消息示例 */ public class ClusteringMessage implements Serializable { public static final String QUEUE = "QUEUE_CLUSTERING"; public static final String EXCHANGE = "EXCHANGE_CLUSTERING"; /** * 编号 */ private Integer id; public ClusteringMessage setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "ClusteringtMessage{" + "id=" + id + '}'; } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-message-model/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/BroadcastProducer.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.producer; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.BroadcastMessage; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class BroadcastProducer { @Autowired private RabbitTemplate rabbitTemplate; public void syncSend(Integer id) { // 创建 BroadcastMessage 消息 BroadcastMessage message = new BroadcastMessage(); message.setId(id); // 同步发送消息 rabbitTemplate.convertAndSend(BroadcastMessage.EXCHANGE, null, message); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-message-model/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/ClusteringProducer.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.producer; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.ClusteringMessage; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class ClusteringProducer { @Autowired private RabbitTemplate rabbitTemplate; public void syncSend(Integer id) { // 创建 ClusteringMessage 消息 ClusteringMessage message = new ClusteringMessage(); message.setId(id); // 同步发送消息 rabbitTemplate.convertAndSend(ClusteringMessage.EXCHANGE, null, message); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-message-model/src/main/resources/application.yaml ================================================ spring: # RabbitMQ 配置项,对应 RabbitProperties 配置类 rabbitmq: host: 127.0.0.1 # RabbitMQ 服务的地址 port: 5672 # RabbitMQ 服务的端口 username: guest # RabbitMQ 服务的账号 password: guest # RabbitMQ 服务的密码 template: # 对应 RabbitProperties.Retry 类 retry: enabled: true # 开启发送机制 max-attempts: 3 # 最大重试次数。默认为 3 。 initial-interval: 1000 # 重试间隔,单位为毫秒。默认为 1000 。 listener: simple: # 对应 RabbitProperties.ListenerRetry 类 retry: enabled: true # 开启消费重试机制 max-attempts: 3 # 最大重试次数。默认为 3 。 initial-interval: 1000 # 重试间隔,单位为毫秒。默认为 1000 。 ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-message-model/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/package-info.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo; ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-message-model/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/BroadcastProducerTest.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.producer; import cn.iocoder.springboot.lab04.rabbitmqdemo.Application; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.concurrent.CountDownLatch; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class BroadcastProducerTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private BroadcastProducer producer; @Test public void mock() throws InterruptedException { // 阻塞等待,保证消费 new CountDownLatch(1).await(); } @Test public void testSyncSend() throws InterruptedException { for (int i = 0; i < 3; i++) { int id = (int) (System.currentTimeMillis() / 1000); producer.syncSend(id); logger.info("[testSyncSend][发送编号:[{}] 发送成功]", id); } // 阻塞等待,保证消费 new CountDownLatch(1).await(); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-message-model/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/ClusteringProducerTest.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.producer; import cn.iocoder.springboot.lab04.rabbitmqdemo.Application; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.concurrent.CountDownLatch; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class ClusteringProducerTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private ClusteringProducer producer; @Test public void mock() throws InterruptedException { // 阻塞等待,保证消费 new CountDownLatch(1).await(); } @Test public void testSyncSend() throws InterruptedException { // 发送 3 条消息 for (int i = 0; i < 3; i++) { int id = (int) (System.currentTimeMillis() / 1000); producer.syncSend(id); logger.info("[testSyncSend][发送编号:[{}] 发送成功]", id); } // 阻塞等待,保证消费 new CountDownLatch(1).await(); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-orderly/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-04-rabbitmq-demo-orderly org.springframework.boot spring-boot-starter-amqp org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-orderly/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/Application.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableAsync; /** * TODO RabbitMQ 顺序消息的示例,暂时未提供 */ @SpringBootApplication @EnableAsync // 开启异步 public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-orderly/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/config/RabbitConfig.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.config; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo10Message; import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.BindingBuilder; import org.springframework.amqp.core.DirectExchange; import org.springframework.amqp.core.Queue; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class RabbitConfig { /** * Direct Exchange 示例的配置类 */ public static class DirectExchangeDemoConfiguration { // 创建 Queue @Bean public Queue demo10Queue0() { return new Queue(Demo10Message.QUEUE_0); } @Bean public Queue demo10Queue1() { return new Queue(Demo10Message.QUEUE_1); } @Bean public Queue demo10Queue2() { return new Queue(Demo10Message.QUEUE_2); } @Bean public Queue demo10Queue3() { return new Queue(Demo10Message.QUEUE_3); } // 创建 Direct Exchange @Bean public DirectExchange demo10Exchange() { return new DirectExchange(Demo10Message.EXCHANGE, true, // durable: 是否持久化 false); // exclusive: 是否排它 } // 创建 Binding @Bean public Binding demo10Binding0() { return BindingBuilder.bind(demo10Queue0()).to(demo10Exchange()).with("0"); } @Bean public Binding demo10Binding1() { return BindingBuilder.bind(demo10Queue1()).to(demo10Exchange()).with("1"); } @Bean public Binding demo10Binding2() { return BindingBuilder.bind(demo10Queue2()).to(demo10Exchange()).with("2"); } @Bean public Binding demo10Binding3() { return BindingBuilder.bind(demo10Queue3()).to(demo10Exchange()).with("3"); } } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-orderly/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/Demo10Consumer.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo10Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.messaging.Message; import org.springframework.stereotype.Component; @Component @RabbitListener(queues = Demo10Message.QUEUE_0) @RabbitListener(queues = Demo10Message.QUEUE_1) @RabbitListener(queues = Demo10Message.QUEUE_2) @RabbitListener(queues = Demo10Message.QUEUE_3) public class Demo10Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @RabbitHandler(isDefault = true) public void onMessage(Message message) { logger.info("[onMessage][线程编号:{} Queue:{} 消息编号:{}]", Thread.currentThread().getId(), getQueue(message), message.getPayload().getId()); } private static String getQueue(Message message) { return message.getHeaders().get("amqp_consumerQueue", String.class); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-orderly/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/message/Demo10Message.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.message; import java.io.Serializable; public class Demo10Message implements Serializable { private static final String QUEUE_BASE = "QUEUE_DEMO_10-"; public static final String QUEUE_0 = QUEUE_BASE + "0"; public static final String QUEUE_1 = QUEUE_BASE + "1"; public static final String QUEUE_2 = QUEUE_BASE + "2"; public static final String QUEUE_3 = QUEUE_BASE + "3"; public static final int QUEUE_COUNT = 4; public static final String EXCHANGE = "EXCHANGE_DEMO_10"; /** * 编号 */ private Integer id; public Demo10Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo10Message{" + "id=" + id + '}'; } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-orderly/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo10Producer.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.producer; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo10Message; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class Demo10Producer { @Autowired private RabbitTemplate rabbitTemplate; public void syncSend(Integer id) { // 创建 Demo10Message 消息 Demo10Message message = new Demo10Message(); message.setId(id); // 同步发送消息 rabbitTemplate.convertAndSend(Demo10Message.EXCHANGE, this.getRoutingKey(id), message); } private String getRoutingKey(Integer id) { return String.valueOf(id % Demo10Message.QUEUE_COUNT); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-orderly/src/main/resources/application.yaml ================================================ spring: # RabbitMQ 配置项,对应 RabbitProperties 配置类 rabbitmq: host: 127.0.0.1 # RabbitMQ 服务的地址 port: 5672 # RabbitMQ 服务的端口 username: guest # RabbitMQ 服务的账号 password: guest # RabbitMQ 服务的密码 ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-orderly/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/package-info.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo; ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-orderly/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo10ProducerTest.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.producer; import cn.iocoder.springboot.lab04.rabbitmqdemo.Application; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.concurrent.CountDownLatch; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class Demo10ProducerTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private Demo10Producer producer; @Test public void testSyncSend() throws InterruptedException { for (int i = 0; i < 2; i++) { for (int id = 0; id < 4; id++) { producer.syncSend(id); // logger.info("[testSyncSend][发送编号:[{}] 发送成功]", id); } } // 阻塞等待,保证消费 new CountDownLatch(1).await(); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-rpc/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-04-rabbitmq-demo-rpc org.springframework.boot spring-boot-starter-amqp org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-rpc/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/Application.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableAsync; @SpringBootApplication @EnableAsync // 开启异步 public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-rpc/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/config/RabbitConfig.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.config; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo14Message; import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.BindingBuilder; import org.springframework.amqp.core.DirectExchange; import org.springframework.amqp.core.Queue; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class RabbitConfig { /** * Direct Exchange 示例的配置类 */ public static class DirectExchangeDemoConfiguration { // 创建 Queue @Bean public Queue demo01Queue() { return new Queue(Demo14Message.QUEUE, // Queue 名字 false, // durable: 是否持久化 false, // exclusive: 是否排它 false); // autoDelete: 是否自动删除 } // 创建 Direct Exchange @Bean public DirectExchange demo01Exchange() { return new DirectExchange(Demo14Message.EXCHANGE, false, // durable: 是否持久化 false); // autoDelete: 是否自动删除 } // 创建 Binding // Exchange:Demo01Message.EXCHANGE // Routing key:Demo01Message.ROUTING_KEY // Queue:Demo01Message.QUEUE @Bean public Binding demo01Binding() { return BindingBuilder.bind(demo01Queue()).to(demo01Exchange()).with(Demo14Message.ROUTING_KEY); } } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-rpc/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/Demo14Consumer.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo14Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; @Component @RabbitListener(queues = Demo14Message.QUEUE) public class Demo14Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @RabbitHandler public String onMessage(Demo14Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); // 返回结果 return "nicai"; } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-rpc/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/message/Demo14Message.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.message; import java.io.Serializable; public class Demo14Message implements Serializable { public static final String QUEUE = "QUEUE_DEMO_14"; public static final String EXCHANGE = "EXCHANGE_DEMO_14"; public static final String ROUTING_KEY = "ROUTING_KEY_14"; /** * 编号 */ private Integer id; public Demo14Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo14Message{" + "id=" + id + '}'; } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-rpc/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo14Producer.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.producer; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo14Message; import org.springframework.amqp.rabbit.connection.CorrelationData; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.UUID; @Component public class Demo14Producer { @Autowired private RabbitTemplate rabbitTemplate; public String syncSend(Integer id) { // 创建 Demo01Message 消息 Demo14Message message = new Demo14Message(); message.setId(id); // 创建 CorrelationData 对象 CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString()); // 同步发送消息,并接收结果 return (String) rabbitTemplate.convertSendAndReceive(Demo14Message.EXCHANGE, Demo14Message.ROUTING_KEY, message, correlationData); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-rpc/src/main/resources/application.yaml ================================================ spring: # RabbitMQ 配置项,对应 RabbitProperties 配置类 rabbitmq: host: 127.0.0.1 # RabbitMQ 服务的地址 port: 5672 # RabbitMQ 服务的端口 username: guest # RabbitMQ 服务的账号 password: guest # RabbitMQ 服务的密码 ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-rpc/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/package-info.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo; ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-rpc/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo14ProducerTest.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.producer; import cn.iocoder.springboot.lab04.rabbitmqdemo.Application; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.concurrent.CountDownLatch; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class Demo14ProducerTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private Demo14Producer producer; @Test public void testSyncSend() throws InterruptedException { int id = (int) (System.currentTimeMillis() / 1000); String result = producer.syncSend(id); logger.info("[testSyncSend][发送编号:[{}] 发送成功 消费结果:[{}]]", id, result); // 阻塞等待,保证消费 new CountDownLatch(1).await(); } // @Test // public void testSyncSend2() throws InterruptedException { // int id = (int) (System.currentTimeMillis() / 1000); // String result = producer.syncSend(id); // logger.info("[testSyncSend][发送编号:[{}] 发送成功 消费结果:[{}]]", id, result); // // // 阻塞等待,保证消费 // new CountDownLatch(1).await(); // } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-transaction/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-04-rabbitmq-demo-transaction org.springframework.boot spring-boot-starter-amqp org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-transaction/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/Application.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.transaction.annotation.EnableTransactionManagement; @SpringBootApplication @EnableTransactionManagement // 开启事务 public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-transaction/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/config/RabbitConfig.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.config; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo11Message; import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.BindingBuilder; import org.springframework.amqp.core.DirectExchange; import org.springframework.amqp.core.Queue; import org.springframework.amqp.rabbit.connection.ConnectionFactory; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.rabbit.transaction.RabbitTransactionManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.annotation.EnableTransactionManagement; @Configuration @EnableTransactionManagement public class RabbitConfig { /** * Direct Exchange 示例的配置类 */ public static class DirectExchangeDemoConfiguration { // 创建 Queue @Bean public Queue demo11Queue() { return new Queue(Demo11Message.QUEUE, // Queue 名字 true, // durable: 是否持久化 false, // exclusive: 是否排它 false); // autoDelete: 是否自动删除 } // 创建 Direct Exchange @Bean public DirectExchange demo11Exchange() { return new DirectExchange(Demo11Message.EXCHANGE, true, // durable: 是否持久化 false); // exclusive: 是否排它 } // 创建 Binding // Exchange:Demo11Message.EXCHANGE // Routing key:Demo11Message.ROUTING_KEY // Queue:Demo11Message.QUEUE @Bean public Binding demo11Binding() { return BindingBuilder.bind(demo11Queue()).to(demo11Exchange()).with(Demo11Message.ROUTING_KEY); } } @Bean public RabbitTransactionManager rabbitTransactionManager(ConnectionFactory connectionFactory, RabbitTemplate rabbitTemplate) { // 设置 RabbitTemplate 支持事务 rabbitTemplate.setChannelTransacted(true); // 创建 RabbitTransactionManager 对象 return new RabbitTransactionManager(connectionFactory); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-transaction/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/Demo11Consumer.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo11Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; @Component @RabbitListener(queues = Demo11Message.QUEUE) public class Demo11Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @RabbitHandler public void onMessage(Demo11Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-transaction/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/message/Demo11Message.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.message; import java.io.Serializable; public class Demo11Message implements Serializable { public static final String QUEUE = "QUEUE_DEMO_11"; public static final String EXCHANGE = "EXCHANGE_DEMO_11"; public static final String ROUTING_KEY = "ROUTING_KEY_11"; /** * 编号 */ private Integer id; public Demo11Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo11Message{" + "id=" + id + '}'; } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-transaction/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo11Producer.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.producer; import cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo11Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; @Component public class Demo11Producer { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private RabbitTemplate rabbitTemplate; @Transactional public void syncSend(Integer id) throws InterruptedException { // 创建 Demo11Message 消息 Demo11Message message = new Demo11Message(); message.setId(id); // 同步发送消息 rabbitTemplate.convertAndSend(Demo11Message.EXCHANGE, Demo11Message.ROUTING_KEY, message); logger.info("[syncSend][发送编号:[{}] 发送成功]", id); // 等待 Thread.sleep(10 * 1000L); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-transaction/src/main/resources/application.yaml ================================================ spring: # RabbitMQ 配置项,对应 RabbitProperties 配置类 rabbitmq: host: 127.0.0.1 # RabbitMQ 服务的地址 port: 5672 # RabbitMQ 服务的端口 username: guest # RabbitMQ 服务的账号 password: guest # RabbitMQ 服务的密码 ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-transaction/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/package-info.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo; ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-demo-transaction/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo11ProducerTest.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo.producer; import cn.iocoder.springboot.lab04.rabbitmqdemo.Application; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.concurrent.CountDownLatch; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class Demo11ProducerTest { @Autowired private Demo11Producer producer; @Test public void testSyncSend() throws InterruptedException { int id = (int) (System.currentTimeMillis() / 1000); producer.syncSend(id); // 阻塞等待,保证消费 new CountDownLatch(1).await(); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-native/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-04-rabbitmq-native com.rabbitmq amqp-client 5.7.3 ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-native/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/RabbitMQConsumer.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo; import com.rabbitmq.client.*; import java.io.IOException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; public class RabbitMQConsumer { public static void main(String[] args) throws IOException, TimeoutException { // 创建连接 Connection connection = RabbitMQProducer.getConnection(); // 创建信道 final Channel channel = connection.createChannel(); channel.basicQos(64); // 设置客户端最多接收未被 ack 的消息数量为 64 。 // 创建消费者 Consumer consumer = new DefaultConsumer(channel) { @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { // 打印日志 System.out.println(String.format("[线程:%s][路由键:%s][消息内容:%s]", Thread.currentThread(), envelope.getRoutingKey(), new String(body))); // ack 消息已经消费 channel.basicAck(envelope.getDeliveryTag(), false); } }; // 订阅消费 QUEUE_NAME 队列 channel.basicConsume(RabbitMQProducer.QUEUE_NAME, consumer); // 关闭 try { TimeUnit.HOURS.sleep(1); } catch (InterruptedException ignore) { } channel.close(); connection.close(); } } ================================================ FILE: lab-04-rabbitmq/lab-04-rabbitmq-native/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/RabbitMQProducer.java ================================================ package cn.iocoder.springboot.lab04.rabbitmqdemo; import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; import com.rabbitmq.client.MessageProperties; import java.io.IOException; import java.util.concurrent.TimeoutException; public class RabbitMQProducer { private static final String IP_ADDRESS = "127.0.0.1"; private static final Integer PORT = 5672; private static final String USERNAME = "guest"; private static final String PASSWORD = "guest"; private static final String EXCHANGE_NAME = "exchange_demo"; private static final String ROUTING_KEY = "routingkey_demo"; public static final String QUEUE_NAME = "queue_demo"; // 只有 QUEUE_NAME 需要共享给 RabbitMQConsumer public static void main(String[] args) throws IOException, TimeoutException { // 创建连接 Connection connection = getConnection(); // 创建信道 Channel channel = connection.createChannel(); // 初始化测试用的 Exchange 和 Queue initExchangeAndQueue(channel); // 发送 3 条消息 for (int i = 0; i < 3; i++) { String message = "Hello World" + i; channel.basicPublish(EXCHANGE_NAME, ROUTING_KEY, MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes()); } // 关闭 channel.close(); connection.close(); } public static Connection getConnection() throws IOException, TimeoutException { ConnectionFactory factory = new ConnectionFactory(); factory.setHost(IP_ADDRESS); factory.setPort(PORT); factory.setUsername(USERNAME); factory.setPassword(PASSWORD); return factory.newConnection(); } // 创建 RabbitMQ Exchange 和 Queue ,然后使用 ROUTING_KEY 路由键将两者绑定。 // 该步骤,其实可以在 RabbitMQ Management 上操作,并不一定需要在代码中 private static void initExchangeAndQueue(Channel channel) throws IOException { // 创建交换器:direct、持久化、不自动删除 channel.exchangeDeclare(EXCHANGE_NAME, "direct", true, false, null); // 创建队列:持久化、非排他、非自动删除的队列 channel.queueDeclare(QUEUE_NAME, true, false, false, null); // 将交换器与队列通过路由键绑定 channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, ROUTING_KEY); } } ================================================ FILE: lab-04-rabbitmq/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-04-rabbitmq pom lab-04-rabbitmq-native lab-04-rabbitmq-demo lab-04-rabbitmq-demo-batch lab-04-rabbitmq-demo-batch-consume lab-04-rabbitmq-demo-batch-consume-02 lab-04-rabbitmq-consume-retry lab-04-rabbitmq-demo-delay lab-04-rabbitmq-demo-message-model lab-04-rabbitmq-demo-concurrency lab-04-rabbitmq-demo-orderly lab-04-rabbitmq-demo-transaction lab-04-rabbitmq-demo-ack lab-04-rabbitmq-demo-confirm lab-04-rabbitmq-demo-confirm-async lab-04-rabbitmq-demo-rpc lab-04-rabbitmq-demo-json lab-04-rabbitmq-demo-error-handler com.rabbitmq amqp-client 5.5.3 ================================================ FILE: lab-04-rabbitmq/《芋道 Spring Boot 消息队列 RabbitMQ 入门》.md ================================================ ================================================ FILE: lab-05-benchmark-tomcat-jetty-undertow/lab-05-jetty/pom.xml ================================================ lab-05-benchmark-tomcat-jetty-undertow cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-05-jetty org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-tomcat org.springframework.boot spring-boot-starter-jetty org.springframework.boot spring-boot-maven-plugin true ================================================ FILE: lab-05-benchmark-tomcat-jetty-undertow/lab-05-jetty/src/main/java/cn/iocoder/springboot/labs/lab05/tomcat/Controller.java ================================================ package cn.iocoder.springboot.labs.lab05.tomcat; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class Controller { @GetMapping("/hello") public String hello() { return "world"; } } ================================================ FILE: lab-05-benchmark-tomcat-jetty-undertow/lab-05-jetty/src/main/java/cn/iocoder/springboot/labs/lab05/tomcat/JettyApplication.java ================================================ package cn.iocoder.springboot.labs.lab05.tomcat; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class JettyApplication { public static void main(String[] args) { SpringApplication.run(JettyApplication.class); } } ================================================ FILE: lab-05-benchmark-tomcat-jetty-undertow/lab-05-tomcat/pom.xml ================================================ lab-05-benchmark-tomcat-jetty-undertow cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-05-tomcat org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-maven-plugin true ================================================ FILE: lab-05-benchmark-tomcat-jetty-undertow/lab-05-tomcat/src/main/java/cn/iocoder/springboot/labs/lab05/tomcat/Controller.java ================================================ package cn.iocoder.springboot.labs.lab05.tomcat; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class Controller { @GetMapping("/hello") public String hello() { return "world"; } @GetMapping("/sleep") public String sleep() throws InterruptedException { Thread.sleep(100L); return "sleep"; } } ================================================ FILE: lab-05-benchmark-tomcat-jetty-undertow/lab-05-tomcat/src/main/java/cn/iocoder/springboot/labs/lab05/tomcat/TomcatApplication.java ================================================ package cn.iocoder.springboot.labs.lab05.tomcat; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class TomcatApplication { public static void main(String[] args) { SpringApplication.run(TomcatApplication.class); } } ================================================ FILE: lab-05-benchmark-tomcat-jetty-undertow/lab-05-tomcat-apr/pom.xml ================================================ lab-05-benchmark-tomcat-jetty-undertow cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-05-tomcat-apr org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-maven-plugin true ================================================ FILE: lab-05-benchmark-tomcat-jetty-undertow/lab-05-tomcat-apr/src/main/java/cn/iocoder/springboot/labs/lab05/tomcat/Controller.java ================================================ package cn.iocoder.springboot.labs.lab05.tomcat; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class Controller { @GetMapping("/hello") public String hello() { return "world"; } } ================================================ FILE: lab-05-benchmark-tomcat-jetty-undertow/lab-05-tomcat-apr/src/main/java/cn/iocoder/springboot/labs/lab05/tomcat/TomcatAprApplication.java ================================================ package cn.iocoder.springboot.labs.lab05.tomcat; import org.apache.catalina.core.AprLifecycleListener; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; import org.springframework.boot.web.servlet.server.ServletWebServerFactory; import org.springframework.context.annotation.Bean; @SpringBootApplication public class TomcatAprApplication { @Bean public ServletWebServerFactory servletWebServerFactory() { TomcatServletWebServerFactory tomcatServletWebServerFactory = new TomcatServletWebServerFactory(); tomcatServletWebServerFactory.setProtocol("org.apache.coyote.http11.Http11AprProtocol"); tomcatServletWebServerFactory.addContextLifecycleListeners(new AprLifecycleListener()); return tomcatServletWebServerFactory; } // Mac 下,VM 启动参数,需要增加 -Djava.library.path=/usr/local/opt/tomcat-native/lib 。 // 当然是在安装了 apr 的情况。安装命令是 brew install apr tomcat-native public static void main(String[] args) { SpringApplication.run(TomcatAprApplication.class); } } ================================================ FILE: lab-05-benchmark-tomcat-jetty-undertow/lab-05-undertow/pom.xml ================================================ lab-05-benchmark-tomcat-jetty-undertow cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-05-undertow 1.0.0 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-tomcat org.springframework.boot spring-boot-starter-undertow org.springframework.boot spring-boot-maven-plugin true ================================================ FILE: lab-05-benchmark-tomcat-jetty-undertow/lab-05-undertow/src/main/java/cn/iocoder/springboot/labs/lab05/undertow/Controller.java ================================================ package cn.iocoder.springboot.labs.lab05.undertow; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class Controller { @GetMapping("/hello") public String hello() { return "world"; } } ================================================ FILE: lab-05-benchmark-tomcat-jetty-undertow/lab-05-undertow/src/main/java/cn/iocoder/springboot/labs/lab05/undertow/UndertowApplication.java ================================================ package cn.iocoder.springboot.labs.lab05.undertow; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class UndertowApplication { public static void main(String[] args) { SpringApplication.run(UndertowApplication.class); } } ================================================ FILE: lab-05-benchmark-tomcat-jetty-undertow/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE 4.0.0 cn.iocoder.springboot.labs lab-05-benchmark-tomcat-jetty-undertow 1.0-SNAPSHOT pom lab-05-tomcat lab-05-tomcat-apr lab-05-jetty lab-05-undertow ================================================ FILE: lab-05-benchmark-tomcat-jetty-undertow/《性能测试 —— Tomcat、Jetty、Undertow 基准测试》.md ================================================ ================================================ FILE: lab-06/lab-06-springmvc-tomcat/pom.xml ================================================ lab-06 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-06-springmvc-tomcat org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-maven-plugin true ================================================ FILE: lab-06/lab-06-springmvc-tomcat/src/main/java/cn/iocoder/springboot/labs/lab06/springmvc/Controller.java ================================================ package cn.iocoder.springboot.labs.lab06.springmvc; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class Controller { @GetMapping("/hello") public String hello() { // System.out.println(Thread.currentThread().getName()); return "world"; } @GetMapping("/sleep") public String sleep() throws InterruptedException { Thread.sleep(100L); // System.out.println(Thread.currentThread().getName()); return "world"; } } ================================================ FILE: lab-06/lab-06-springmvc-tomcat/src/main/java/cn/iocoder/springboot/labs/lab06/springmvc/SpringMVCApplication.java ================================================ package cn.iocoder.springboot.labs.lab06.springmvc; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SpringMVCApplication { public static void main(String[] args) { SpringApplication.run(SpringMVCApplication.class); } } ================================================ FILE: lab-06/lab-06-webflux-netty/pom.xml ================================================ lab-06 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-06-webflux-netty org.springframework.boot spring-boot-starter-webflux org.springframework.boot spring-boot-maven-plugin true cn.iocoder.springboot.labs.lab06.webflux.WebfluxNettyApplication ================================================ FILE: lab-06/lab-06-webflux-netty/src/main/java/cn/iocoder/springboot/labs/lab06/webflux/Controller.java ================================================ package cn.iocoder.springboot.labs.lab06.webflux; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Mono; import reactor.core.scheduler.Schedulers; @RestController public class Controller { @GetMapping("/hello") public Mono hello() { return Mono.just("world"); } @GetMapping("/sleep_direct") public Mono sleepDirect() throws InterruptedException { Thread.sleep(100L); return Mono.just("world"); } @GetMapping("/sleep") public Mono sleep() { // System.out.println(Thread.currentThread().getName()); // return Mono.just("world").delayElement(Duration.ofMillis(100)); return Mono.defer(() -> { // System.out.println(Thread.currentThread().getName()); try { Thread.sleep(100L); } catch (InterruptedException ignored) { } return Mono.just("world"); }).subscribeOn(Schedulers.parallel()); } } ================================================ FILE: lab-06/lab-06-webflux-netty/src/main/java/cn/iocoder/springboot/labs/lab06/webflux/WebfluxNettyApplication.java ================================================ package cn.iocoder.springboot.labs.lab06.webflux; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class WebfluxNettyApplication { public static void main(String[] args) { SpringApplication.run(WebfluxNettyApplication.class); } } ================================================ FILE: lab-06/lab-06-webflux-tomcat/pom.xml ================================================ lab-06 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-06-webflux-tomcat org.springframework.boot spring-boot-starter-webflux org.springframework.boot spring-boot-starter-netty org.springframework.boot spring-boot-starter-tomcat org.springframework.boot spring-boot-maven-plugin true cn.iocoder.springboot.labs.lab06.webflux.WebfluxTomcatApplication ================================================ FILE: lab-06/lab-06-webflux-tomcat/src/main/java/cn/iocoder/springboot/labs/lab06/webflux/Controller.java ================================================ package cn.iocoder.springboot.labs.lab06.webflux; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Mono; import reactor.core.scheduler.Schedulers; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @RestController public class Controller { @GetMapping("/hello") public Mono hello() { return Mono.just("world"); } @GetMapping("/sleep_direct") public Mono sleepDirect() throws InterruptedException { Thread.sleep(100L); return Mono.just("world"); } @GetMapping("/sleep") public Mono sleep() { // System.out.println(Thread.currentThread().getName()); // return Mono.just("world").delayElement(Duration.ofMillis(100)); return Mono.defer(() -> { // System.out.println(Thread.currentThread().getName()); try { Thread.sleep(100L); } catch (InterruptedException ignored) { } return Mono.just("world"); }).subscribeOn(Schedulers.parallel()); } private Map MAP = new ConcurrentHashMap<>(); @GetMapping("/sleep2") public Mono sleep2() { return Mono.defer(() -> { try { // System.out.println(Thread.currentThread().getName()); if (!MAP.containsKey(Thread.currentThread().getName())) { System.out.println(Thread.currentThread().getName()); MAP.put(Thread.currentThread().getName(), true); } Thread.sleep(100L); } catch (InterruptedException ignored) { } return Mono.just("world"); }).subscribeOn(Schedulers.elastic()); } } ================================================ FILE: lab-06/lab-06-webflux-tomcat/src/main/java/cn/iocoder/springboot/labs/lab06/webflux/WebfluxTomcatApplication.java ================================================ package cn.iocoder.springboot.labs.lab06.webflux; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class WebfluxTomcatApplication { public static void main(String[] args) { SpringApplication.run(WebfluxTomcatApplication.class); } } ================================================ FILE: lab-06/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE 4.0.0 cn.iocoder.springboot.labs lab-06 1.0-SNAPSHOT pom lab-06-springmvc-tomcat lab-06-webflux-netty lab-06-webflux-tomcat ================================================ FILE: lab-06/《性能测试 —— SpringMVC、Webflux 基准测试》.md ================================================ ================================================ FILE: lab-07/lab-07-spring-cloud-gateway/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE 4.0.0 lab-07-spring-cloud-gateway 1.0.0 UTF-8 UTF-8 1.8 Greenwich.RELEASE org.springframework.cloud spring-cloud-starter-gateway org.springframework.boot spring-boot-starter-webflux org.springframework.boot spring-boot-starter-test test io.projectreactor reactor-test test org.springframework.cloud spring-cloud-dependencies ${spring-cloud.version} pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: lab-07/lab-07-spring-cloud-gateway/src/main/java/cn/iocoder/springboot/labs/lab07/springcloudgateway/SpringCloudGatewayApplication.java ================================================ package cn.iocoder.springboot.labs.lab07.springcloudgateway; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.gateway.route.RouteLocator; import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; import org.springframework.context.annotation.Bean; @SpringBootApplication public class SpringCloudGatewayApplication { @Bean public RouteLocator myRouteLocator(RouteLocatorBuilder builder) { return builder.routes() .route(r -> r.path("/hello.txt") .uri("http://localhost:8000/hello.txt")) .route(r -> r.path("/**") .uri("http://localhost:8080")) .build(); } public static void main(String[] args) { SpringApplication.run(SpringCloudGatewayApplication.class, args); } } ================================================ FILE: lab-07/lab-07-spring-cloud-gateway/src/main/resources/application.yml ================================================ server: port: 8081 ================================================ FILE: lab-07/lab-07-zuul/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE 4.0.0 lab-07-zuul 1.0.0 UTF-8 UTF-8 1.8 Greenwich.RELEASE org.springframework.boot spring-boot-starter-actuator org.springframework.cloud spring-cloud-starter-netflix-zuul org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-dependencies ${spring-cloud.version} pom import org.springframework.boot spring-boot-maven-plugin ================================================ FILE: lab-07/lab-07-zuul/src/main/java/cn/iocoder/springboot/labs/lab07/zuul/ZuulApplication.java ================================================ package cn.iocoder.springboot.labs.lab07.zuul; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.zuul.EnableZuulProxy; @EnableZuulProxy @SpringBootApplication public class ZuulApplication { public static void main(String[] args) { SpringApplication.run(ZuulApplication.class, args); } } ================================================ FILE: lab-07/lab-07-zuul/src/main/resources/application.yml ================================================ zuul: routes: static: path: /hello.txt url: http://127.0.0.1:8000/ stripPrefix: false api: path: /** url: http://127.0.0.1:8080 include-debug-header: true server: port: 8081 ================================================ FILE: lab-07/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-07 pom lab-07-spring-cloud-gateway lab-07-zuul ================================================ FILE: lab-07/《性能测试 —— Spring Cloud Gateway、Zuul 基准测试》.md ================================================ ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-unit-test/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE 4.0.0 lab-07-spring-data-redis-unit-test org.redisson redisson-spring-boot-starter 3.11.3 org.springframework.boot spring-boot-starter-test test com.alibaba fastjson 1.2.61 com.fasterxml.jackson.core jackson-databind commons-io commons-io 2.6 it.ozimov embedded-redis 0.7.2 test ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-unit-test/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/Application.java ================================================ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication //@EnableTransactionManagement public class Application { } ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-unit-test/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/config/RedisConfiguration.java ================================================ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.RedisSerializer; @Configuration public class RedisConfiguration { @Bean public RedisTemplate redisTemplate(RedisConnectionFactory factory) { // 创建 RedisTemplate 对象 RedisTemplate template = new RedisTemplate<>(); // 设置开启事务支持 template.setEnableTransactionSupport(true); // 设置 RedisConnection 工厂。😈 它就是实现多种 Java Redis 客户端接入的秘密工厂。感兴趣的胖友,可以自己去撸下。 template.setConnectionFactory(factory); // 使用 String 序列化方式,序列化 KEY 。 template.setKeySerializer(RedisSerializer.string()); // 使用 JSON 序列化方式(库是 Jackson ),序列化 VALUE 。 template.setValueSerializer(RedisSerializer.json()); return template; } // Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); // ObjectMapper objectMapper = new ObjectMapper();// <1> //// objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); //// objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); // // jackson2JsonRedisSerializer.setObjectMapper(objectMapper); // template.setValueSerializer(jackson2JsonRedisSerializer); } ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-unit-test/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/util/JSONUtil.java ================================================ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.util; import com.alibaba.fastjson.JSON; /** * JSON 工具类 */ public class JSONUtil { public static T parseObject(String text, Class clazz) { return JSON.parseObject(text, clazz); } public static String toJSONString(Object javaObject) { return JSON.toJSONString(javaObject); } public static byte[] toJSONBytes(Object javaObject) { return JSON.toJSONBytes(javaObject); } } ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-unit-test/src/main/resources/application.yml ================================================ spring: # 对应 RedisProperties 类 redis: host: 127.0.0.1 port: 6379 # password: # Redis 服务器密码,默认为空。生产中,一定要设置 Redis 密码! database: 0 # Redis 数据库号,默认为 0 。 timeout: 0 # Redis 连接超时时间,单位:毫秒。 # 对应 RedissonProperties 类 # redisson: # config: classpath:redisson.yml # 具体的每个配置项,见 org.redisson.config.Config 类。 ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-unit-test/src/main/resources/redisson.yml ================================================ ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-unit-test/src/test/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/Test01.java ================================================ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis; import cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.config.TestRedisConfiguration; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.dao.DataAccessException; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.core.RedisCallback; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest(classes = TestRedisConfiguration.class) public class Test01 { @Autowired private StringRedisTemplate stringRedisTemplate; @Autowired private RedisTemplate redisTemplate; // @Autowired // private RedisServer server; @Test public void test01() { // 写入 stringRedisTemplate.opsForValue().set("yunai", "shuai"); // 读取 String value = stringRedisTemplate.opsForValue().get("yunai"); Assert.assertEquals("值不匹配", "shuai", value); // 测试重启后读取 redisTemplate.execute(new RedisCallback() { @Override public Object doInRedis(RedisConnection connection) throws DataAccessException { connection.flushDb(); return ""; } }); } } ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-unit-test/src/test/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/config/TestRedisConfiguration.java ================================================ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.config; import org.springframework.boot.autoconfigure.data.redis.RedisProperties; import org.springframework.boot.test.context.TestConfiguration; import redis.embedded.RedisServer; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; @TestConfiguration public class TestRedisConfiguration { private RedisServer redisServer; public TestRedisConfiguration(RedisProperties redisProperties) { this.redisServer = new RedisServer(redisProperties.getPort()); } // @Bean // public RedisServer redisServer() { // return redisServer; // } @PostConstruct public void postConstruct() { redisServer.start(); } @PreDestroy public void preDestroy() { redisServer.stop(); } } ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE 4.0.0 lab-07-spring-data-redis-with-jedis org.springframework.boot spring-boot-starter-data-redis io.lettuce lettuce-core redis.clients jedis org.springframework.boot spring-boot-starter-test test com.alibaba fastjson 1.2.61 com.fasterxml.jackson.core jackson-databind commons-io commons-io 2.6 ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/Application.java ================================================ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication //@EnableTransactionManagement public class Application { } ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/cacheobject/ProductCacheObject.java ================================================ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.cacheobject; /** * 商品缓存对象 */ public class ProductCacheObject { /** * 产品编号 */ private Integer id; /** * 产品名 */ private String name; /** * 产品分类编号 */ private Integer cid; public Integer getId() { return id; } public ProductCacheObject setId(Integer id) { this.id = id; return this; } public String getName() { return name; } public ProductCacheObject setName(String name) { this.name = name; return this; } public Integer getCid() { return cid; } public ProductCacheObject setCid(Integer cid) { this.cid = cid; return this; } @Override public String toString() { return "ProductCacheObject{" + "id=" + id + ", name='" + name + '\'' + ", cid=" + cid + '}'; } } ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/cacheobject/UserCacheObject.java ================================================ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.cacheobject; /** * 用户缓存对象 */ public class UserCacheObject { /** * 用户编号 */ private Integer id; /** * 昵称 */ private String name; /** * 性别 */ private Integer gender; public Integer getId() { return id; } public UserCacheObject setId(Integer id) { this.id = id; return this; } public String getName() { return name; } public UserCacheObject setName(String name) { this.name = name; return this; } public Integer getGender() { return gender; } public UserCacheObject setGender(Integer gender) { this.gender = gender; return this; } @Override public String toString() { return "UserCacheObject{" + "id=" + id + ", name='" + name + '\'' + ", gender=" + gender + '}'; } } ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/config/RedisConfiguration.java ================================================ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.config; import cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.listener.TestChannelTopicMessageListener; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.listener.ChannelTopic; import org.springframework.data.redis.listener.RedisMessageListenerContainer; import org.springframework.data.redis.serializer.RedisSerializer; @Configuration public class RedisConfiguration { @Bean public RedisTemplate redisTemplate(RedisConnectionFactory factory) { // 创建 RedisTemplate 对象 RedisTemplate template = new RedisTemplate<>(); // 设置开启事务支持 template.setEnableTransactionSupport(true); // 设置 RedisConnection 工厂。😈 它就是实现多种 Java Redis 客户端接入的秘密工厂。感兴趣的胖友,可以自己去撸下。 template.setConnectionFactory(factory); // 使用 String 序列化方式,序列化 KEY 。 template.setKeySerializer(RedisSerializer.string()); // 使用 JSON 序列化方式(库是 Jackson ),序列化 VALUE 。 template.setValueSerializer(RedisSerializer.json()); return template; } // Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); // ObjectMapper objectMapper = new ObjectMapper();// <1> //// objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); //// objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); // // jackson2JsonRedisSerializer.setObjectMapper(objectMapper); // template.setValueSerializer(jackson2JsonRedisSerializer); // @Bean // PUB/SUB 使用的 Bean ,需要时打开。 public RedisMessageListenerContainer listenerContainer(RedisConnectionFactory factory) { // 创建 RedisMessageListenerContainer 对象 RedisMessageListenerContainer container = new RedisMessageListenerContainer(); // 设置 RedisConnection 工厂。😈 它就是实现多种 Java Redis 客户端接入的秘密工厂。感兴趣的胖友,可以自己去撸下。 container.setConnectionFactory(factory); // 添加监听器 container.addMessageListener(new TestChannelTopicMessageListener(), new ChannelTopic("TEST")); // container.addMessageListener(new TestChannelTopicMessageListener(), new ChannelTopic("AOTEMAN")); // container.addMessageListener(new TestPatternTopicMessageListener(), new PatternTopic("TEST")); return container; } } ================================================ FILE: 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 ================================================ /** * 数据库访问层 */ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.dao; ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.dao.redis; import cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.cacheobject.UserCacheObject; import cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.util.JSONUtil; import org.springframework.data.redis.core.ValueOperations; import org.springframework.stereotype.Repository; import javax.annotation.Resource; @Repository public class UserCacheDao { private static final String KEY_PATTERN = "user:%d"; // user:用户编号 @Resource(name = "redisTemplate") @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") private ValueOperations operations; private static String buildKey(Integer id) { return String.format(KEY_PATTERN, id); } public UserCacheObject get(Integer id) { String key = buildKey(id); String value = operations.get(key); return JSONUtil.parseObject(value, UserCacheObject.class); } public void set(Integer id, UserCacheObject object) { String key = buildKey(id); String value = JSONUtil.toJSONString(object); operations.set(key, value); } } ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/listener/TestChannelTopicMessageListener.java ================================================ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.listener; import org.springframework.data.redis.connection.Message; import org.springframework.data.redis.connection.MessageListener; public class TestChannelTopicMessageListener implements MessageListener { @Override public void onMessage(Message message, byte[] pattern) { System.out.println("收到 ChannelTopic 消息:"); System.out.println("线程编号:" + Thread.currentThread().getName()); System.out.println("message:" + message); System.out.println("pattern:" + new String(pattern)); } } ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/listener/TestPatternTopicMessageListener.java ================================================ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.listener; import org.springframework.data.redis.connection.Message; import org.springframework.data.redis.connection.MessageListener; public class TestPatternTopicMessageListener implements MessageListener { @Override public void onMessage(Message message, byte[] pattern) { System.out.println("收到 PatternTopic 消息:"); System.out.println("线程编号:" + Thread.currentThread().getName()); System.out.println("message:" + message); System.out.println("pattern:" + new String(pattern)); } } ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/service/UserService01.java ================================================ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.service; import org.springframework.stereotype.Service; @Service public class UserService01 { } ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/service/UserService02.java ================================================ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.service; import cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.cacheobject.UserCacheObject; import cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.dao.redis.UserCacheDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserService02 { @Autowired private UserCacheDao userCacheDao; public UserCacheObject get(Integer id) { return userCacheDao.get(id); } public void set(Integer id, UserCacheObject object) { userCacheDao.set(id, object); } } ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/util/JSONUtil.java ================================================ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.util; import com.alibaba.fastjson.JSON; /** * JSON 工具类 */ public class JSONUtil { public static T parseObject(String text, Class clazz) { return JSON.parseObject(text, clazz); } public static String toJSONString(Object javaObject) { return JSON.toJSONString(javaObject); } public static byte[] toJSONBytes(Object javaObject) { return JSON.toJSONBytes(javaObject); } } ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/main/resources/application.yml ================================================ spring: # 对应 RedisProperties 类 redis: host: 127.0.0.1 port: 6379 password: # Redis 服务器密码,默认为空。生产中,一定要设置 Redis 密码! database: 0 # Redis 数据库号,默认为 0 。 timeout: 0 # Redis 连接超时时间,单位:毫秒。 # 对应 RedisProperties.Jedis 内部类 jedis: pool: max-active: 8 # 连接池最大连接数,默认为 8 。使用负数表示没有限制。 max-idle: 8 # 默认连接数最小空闲的连接数,默认为 8 。使用负数表示没有限制。 min-idle: 0 # 默认连接池最小空闲的连接数,默认为 0 。允许设置 0 和 正数。 max-wait: -1 # 连接池最大阻塞等待时间,单位:毫秒。默认为 -1 ,表示不限制。 ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/main/resources/lua/compareAndSet.lua ================================================ if redis.call('GET', KEYS[1]) ~= ARGV[1] then return 0 end redis.call('SET', KEYS[1], ARGV[2]) return 1 ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/main/resources/lua/compareAndSet2.lua ================================================ if redis.call('GET', KEYS[1]) != ARGV[1] then return {0} end redis.call('SET', KEYS[2], ARGV[2]) return {1} ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/main/resources/lua/test.lua ================================================ return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]} ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/test/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/PipelineTest.java ================================================ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.dao.DataAccessException; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.core.RedisCallback; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.test.context.junit4.SpringRunner; import java.util.List; @RunWith(SpringRunner.class) @SpringBootTest public class PipelineTest { @Autowired private StringRedisTemplate stringRedisTemplate; @Test public void test01() { List results = stringRedisTemplate.executePipelined(new RedisCallback() { @Override public Object doInRedis(RedisConnection connection) throws DataAccessException { // set 写入 for (int i = 0; i < 3; i++) { connection.set(String.format("yunai:%d", i).getBytes(), "shuai".getBytes()); } // get for (int i = 0; i < 3; i++) { connection.get(String.format("yunai:%d", i).getBytes()); } // 返回 null 即可 return null; } }); // 打印结果 System.out.println(results); } } ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/test/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/PubSubTest.java ================================================ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest public class PubSubTest { public static final String TOPIC = "TEST"; @Autowired private StringRedisTemplate stringRedisTemplate; @Test public void test01() throws InterruptedException { for (int i = 0; i < 3; i++) { stringRedisTemplate.convertAndSend(TOPIC, "yunai:" + i); Thread.sleep(1000L); } } } ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/test/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/ScriptTest.java ================================================ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis; import org.apache.commons.io.IOUtils; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.script.DefaultRedisScript; import org.springframework.data.redis.core.script.RedisScript; import org.springframework.test.context.junit4.SpringRunner; import java.io.IOException; import java.util.Arrays; import java.util.Collections; import java.util.List; @RunWith(SpringRunner.class) @SpringBootTest public class ScriptTest { @Autowired private StringRedisTemplate stringRedisTemplate; @Test public void test01() throws IOException { // 读取 /resources/lua/compareAndSet.lua 脚本 。注意,需要引入下 commons-io 依赖。 String scriptContents = IOUtils.toString(getClass().getResourceAsStream("/lua/compareAndSet.lua"), "UTF-8"); // 创建 RedisScript 对象 RedisScript script = new DefaultRedisScript<>(scriptContents, Long.class); // 执行 LUA 脚本 Long result = stringRedisTemplate.execute(script, Collections.singletonList("yunai:1"), "shuai02", "shuai"); System.out.println(result); } @Test public void test02() throws IOException { // 读取 /resources/lua/compareAndSet.lua 脚本 。注意,需要引入下 commons-io 依赖。 String scriptContents = IOUtils.toString(getClass().getResourceAsStream("/lua/test.lua"), "UTF-8"); // 创建 RedisScript 对象 RedisScript script = new DefaultRedisScript<>(scriptContents, List.class); List result = stringRedisTemplate.execute(script, Arrays.asList("key1", "key2"), "first", "second"); System.out.println(result); } } ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/test/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/SessionTest.java ================================================ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.dao.DataAccessException; import org.springframework.data.redis.core.RedisOperations; import org.springframework.data.redis.core.SessionCallback; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest public class SessionTest { @Autowired private StringRedisTemplate stringRedisTemplate; @Test public void test01() { String result = stringRedisTemplate.execute(new SessionCallback() { @Override public String execute(RedisOperations operations) throws DataAccessException { for (int i = 0; i < 100; i++) { operations.opsForValue().set(String.format("yunai:%d", i), "shuai02"); } return (String) operations.opsForValue().get(String.format("yunai:%d", 0)); } }); System.out.println("result:" + result); } } ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/test/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/Test01.java ================================================ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis; import cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.cacheobject.UserCacheObject; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest public class Test01 { @Autowired private StringRedisTemplate stringRedisTemplate; @Autowired private RedisTemplate redisTemplate; @Test public void testStringSetKey() { stringRedisTemplate.opsForValue().set("yunai", "shuai"); } @Test public void testStringSetKey02() { redisTemplate.opsForValue().set("yunai", "shuai"); } @Test public void testSetAdd() { stringRedisTemplate.opsForSet().add("yunai_descriptions", "shuai", "cai"); } @Test public void testStringSetKeyUserCache() { UserCacheObject object = new UserCacheObject() .setId(1) .setName("芋道源码") .setGender(1); // 男 String key = String.format("user:%d", object.getId()); redisTemplate.opsForValue().set(key, object); } @Test public void testStringGetKeyUserCache() { String key = String.format("user:%d", 1); Object value = redisTemplate.opsForValue().get(key); System.out.println(value); } } ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/test/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/TransactionTest.java ================================================ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; @RunWith(SpringRunner.class) @SpringBootTest //@EnableTransactionManagement public class TransactionTest { @Autowired private StringRedisTemplate stringRedisTemplate; @Test @Transactional public void test01() { // 这里是偷懒,没在 RedisConfiguration 配置类中,设置 stringRedisTemplate 开启事务。 stringRedisTemplate.setEnableTransactionSupport(true); // 执行想要的操作 stringRedisTemplate.opsForValue().set("yunai:1", "shuai"); stringRedisTemplate.opsForValue().set("yudaoyuanma:1", "dai"); // stringRedisTemplate.execute(new SessionCallback() { // // @Override // public String execute(RedisOperations operations) throws DataAccessException { // return null; // } // //// @Override //// public List execute(RedisOperations operations) throws DataAccessException { //// for (int i = 0; i < 100; i++) { //// operations.opsForValue(String.format("yunai:%d", i), "shuai"); //// } //// return null; //// } // // }); } @Test public void test02() { stringRedisTemplate.setEnableTransactionSupport(true); stringRedisTemplate.opsForValue().get("user:1"); stringRedisTemplate.opsForValue().get("user:2"); } } ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/test/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/UserService02Test.java ================================================ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis; import cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.cacheobject.UserCacheObject; import cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.service.UserService02; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class UserService02Test { @Autowired private UserService02 userService; @Test public void testSet() { UserCacheObject object = new UserCacheObject() .setId(1) .setName("芋道源码") .setGender(1); // 男 userService.set(object.getId(), object); } } ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/test/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/package-info.java ================================================ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis; ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE 4.0.0 lab-07-spring-data-redis-with-redisson org.redisson redisson-spring-boot-starter 3.11.3 org.springframework.boot spring-boot-starter-test test com.alibaba fastjson 1.2.61 com.fasterxml.jackson.core jackson-databind commons-io commons-io 2.6 ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/Application.java ================================================ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication //@EnableTransactionManagement public class Application { } ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/cacheobject/ProductCacheObject.java ================================================ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.cacheobject; /** * 商品缓存对象 */ public class ProductCacheObject { /** * 产品编号 */ private Integer id; /** * 产品名 */ private String name; /** * 产品分类编号 */ private Integer cid; public Integer getId() { return id; } public ProductCacheObject setId(Integer id) { this.id = id; return this; } public String getName() { return name; } public ProductCacheObject setName(String name) { this.name = name; return this; } public Integer getCid() { return cid; } public ProductCacheObject setCid(Integer cid) { this.cid = cid; return this; } @Override public String toString() { return "ProductCacheObject{" + "id=" + id + ", name='" + name + '\'' + ", cid=" + cid + '}'; } } ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/cacheobject/UserCacheObject.java ================================================ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.cacheobject; /** * 用户缓存对象 */ public class UserCacheObject { /** * 用户编号 */ private Integer id; /** * 昵称 */ private String name; /** * 性别 */ private Integer gender; public Integer getId() { return id; } public UserCacheObject setId(Integer id) { this.id = id; return this; } public String getName() { return name; } public UserCacheObject setName(String name) { this.name = name; return this; } public Integer getGender() { return gender; } public UserCacheObject setGender(Integer gender) { this.gender = gender; return this; } @Override public String toString() { return "UserCacheObject{" + "id=" + id + ", name='" + name + '\'' + ", gender=" + gender + '}'; } } ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/config/RedisConfiguration.java ================================================ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.config; import cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.listener.TestChannelTopicMessageListener; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.listener.ChannelTopic; import org.springframework.data.redis.listener.RedisMessageListenerContainer; import org.springframework.data.redis.serializer.RedisSerializer; @Configuration public class RedisConfiguration { @Bean public RedisTemplate redisTemplate(RedisConnectionFactory factory) { // 创建 RedisTemplate 对象 RedisTemplate template = new RedisTemplate<>(); // 设置开启事务支持 template.setEnableTransactionSupport(true); // 设置 RedisConnection 工厂。😈 它就是实现多种 Java Redis 客户端接入的秘密工厂。感兴趣的胖友,可以自己去撸下。 template.setConnectionFactory(factory); // 使用 String 序列化方式,序列化 KEY 。 template.setKeySerializer(RedisSerializer.string()); // 使用 JSON 序列化方式(库是 Jackson ),序列化 VALUE 。 template.setValueSerializer(RedisSerializer.json()); return template; } // Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); // ObjectMapper objectMapper = new ObjectMapper();// <1> //// objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); //// objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); // // jackson2JsonRedisSerializer.setObjectMapper(objectMapper); // template.setValueSerializer(jackson2JsonRedisSerializer); // @Bean // PUB/SUB 使用的 Bean ,需要时打开。 public RedisMessageListenerContainer listenerContainer(RedisConnectionFactory factory) { // 创建 RedisMessageListenerContainer 对象 RedisMessageListenerContainer container = new RedisMessageListenerContainer(); // 设置 RedisConnection 工厂。😈 它就是实现多种 Java Redis 客户端接入的秘密工厂。感兴趣的胖友,可以自己去撸下。 container.setConnectionFactory(factory); // 添加监听器 container.addMessageListener(new TestChannelTopicMessageListener(), new ChannelTopic("TEST")); // container.addMessageListener(new TestChannelTopicMessageListener(), new ChannelTopic("AOTEMAN")); // container.addMessageListener(new TestPatternTopicMessageListener(), new PatternTopic("TEST")); return container; } } ================================================ FILE: 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 ================================================ /** * 数据库访问层 */ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.dao; ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.dao.redis; import cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.cacheobject.UserCacheObject; import cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.util.JSONUtil; import org.springframework.data.redis.core.ValueOperations; import org.springframework.stereotype.Repository; import javax.annotation.Resource; @Repository public class UserCacheDao { private static final String KEY_PATTERN = "user:%d"; // user:用户编号 @Resource(name = "redisTemplate") @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") private ValueOperations operations; private static String buildKey(Integer id) { return String.format(KEY_PATTERN, id); } public UserCacheObject get(Integer id) { String key = buildKey(id); String value = operations.get(key); return JSONUtil.parseObject(value, UserCacheObject.class); } public void set(Integer id, UserCacheObject object) { String key = buildKey(id); String value = JSONUtil.toJSONString(object); operations.set(key, value); } } ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/listener/TestChannelTopicMessageListener.java ================================================ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.listener; import org.springframework.data.redis.connection.Message; import org.springframework.data.redis.connection.MessageListener; public class TestChannelTopicMessageListener implements MessageListener { @Override public void onMessage(Message message, byte[] pattern) { System.out.println("收到 ChannelTopic 消息:"); System.out.println("线程编号:" + Thread.currentThread().getName()); System.out.println("message:" + message); System.out.println("pattern:" + new String(pattern)); } } ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/listener/TestPatternTopicMessageListener.java ================================================ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.listener; import org.springframework.data.redis.connection.Message; import org.springframework.data.redis.connection.MessageListener; public class TestPatternTopicMessageListener implements MessageListener { @Override public void onMessage(Message message, byte[] pattern) { System.out.println("收到 PatternTopic 消息:"); System.out.println("线程编号:" + Thread.currentThread().getName()); System.out.println("message:" + message); System.out.println("pattern:" + new String(pattern)); } } ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/service/UserService01.java ================================================ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.service; import org.springframework.stereotype.Service; @Service public class UserService01 { } ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/service/UserService02.java ================================================ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.service; import cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.cacheobject.UserCacheObject; import cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.dao.redis.UserCacheDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserService02 { @Autowired private UserCacheDao userCacheDao; public UserCacheObject get(Integer id) { return userCacheDao.get(id); } public void set(Integer id, UserCacheObject object) { userCacheDao.set(id, object); } } ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/util/JSONUtil.java ================================================ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.util; import com.alibaba.fastjson.JSON; /** * JSON 工具类 */ public class JSONUtil { public static T parseObject(String text, Class clazz) { return JSON.parseObject(text, clazz); } public static String toJSONString(Object javaObject) { return JSON.toJSONString(javaObject); } public static byte[] toJSONBytes(Object javaObject) { return JSON.toJSONBytes(javaObject); } } ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/main/resources/application.yml ================================================ spring: # 对应 RedisProperties 类 redis: host: 127.0.0.1 port: 6379 # password: # Redis 服务器密码,默认为空。生产中,一定要设置 Redis 密码! database: 0 # Redis 数据库号,默认为 0 。 timeout: 0 # Redis 连接超时时间,单位:毫秒。 # 对应 RedissonProperties 类 # redisson: # config: classpath:redisson.yml # 具体的每个配置项,见 org.redisson.config.Config 类。 ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/main/resources/lua/compareAndSet.lua ================================================ if redis.call('GET', KEYS[1]) ~= ARGV[1] then return 0 end redis.call('SET', KEYS[1], ARGV[2]) return 1 ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/main/resources/lua/compareAndSet2.lua ================================================ if redis.call('GET', KEYS[1]) != ARGV[1] then return {0} end redis.call('SET', KEYS[2], ARGV[2]) return {1} ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/main/resources/lua/test.lua ================================================ return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]} ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/main/resources/redisson.yml ================================================ ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/test/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/LockTest.java ================================================ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis; import org.junit.Test; import org.junit.runner.RunWith; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.TimeUnit; @RunWith(SpringRunner.class) @SpringBootTest public class LockTest { private static final String LOCK_KEY = "anylock"; @Autowired private RedissonClient redissonClient; @Test public void test() throws InterruptedException { // 启动一个线程 A ,去占有锁 new Thread(new Runnable() { @Override public void run() { // 加锁以后 10 秒钟自动解锁 // 无需调用 unlock 方法手动解锁 final RLock lock = redissonClient.getLock(LOCK_KEY); lock.lock(10, TimeUnit.SECONDS); } }).start(); // 简单 sleep 1 秒,保证线程 A 成功持有锁 Thread.sleep(1000L); // 尝试加锁,最多等待 100 秒,上锁以后 10 秒自动解锁 System.out.println(String.format("准备开始获得锁时间:%s", new SimpleDateFormat("yyyy-MM-DD HH:mm:ss").format(new Date()))); final RLock lock = redissonClient.getLock(LOCK_KEY); boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS); if (res) { System.out.println(String.format("实际获得锁时间:%s", new SimpleDateFormat("yyyy-MM-DD HH:mm:ss").format(new Date()))); } else { System.out.println("加锁失败"); } } } ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/test/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/PipelineTest.java ================================================ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.dao.DataAccessException; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.core.RedisCallback; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.test.context.junit4.SpringRunner; import java.util.List; @RunWith(SpringRunner.class) @SpringBootTest public class PipelineTest { @Autowired private StringRedisTemplate stringRedisTemplate; @Test public void test01() { List results = stringRedisTemplate.executePipelined(new RedisCallback() { @Override public Object doInRedis(RedisConnection connection) throws DataAccessException { // set 写入 for (int i = 0; i < 3; i++) { connection.set(String.format("yunai:%d", i).getBytes(), "shuai".getBytes()); } // get for (int i = 0; i < 3; i++) { connection.get(String.format("yunai:%d", i).getBytes()); } // 返回 null 即可 return null; } }); // 打印结果 System.out.println(results); } } ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/test/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/PubSubTest.java ================================================ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest public class PubSubTest { public static final String TOPIC = "TEST"; @Autowired private StringRedisTemplate stringRedisTemplate; @Test public void test01() throws InterruptedException { for (int i = 0; i < 3; i++) { stringRedisTemplate.convertAndSend(TOPIC, "yunai:" + i); Thread.sleep(1000L); } } } ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/test/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/RateLimiterTest.java ================================================ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis; import org.junit.Test; import org.junit.runner.RunWith; import org.redisson.api.RRateLimiter; import org.redisson.api.RateIntervalUnit; import org.redisson.api.RateType; import org.redisson.api.RedissonClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.text.SimpleDateFormat; import java.util.Date; @RunWith(SpringRunner.class) @SpringBootTest public class RateLimiterTest { @Autowired private RedissonClient redissonClient; @Test public void test() throws InterruptedException { // 创建 RRateLimiter 对象 RRateLimiter rateLimiter = redissonClient.getRateLimiter("myRateLimiter"); // 初始化:最大流速 = 每 1 秒产生 2 个令牌 rateLimiter.trySetRate(RateType.OVERALL, 2, 1, RateIntervalUnit.SECONDS); // rateLimiter.trySetRate(RateType.PER_CLIENT, 50, 1, RateIntervalUnit.MINUTES); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); for (int i = 0; i < 5; i++) { System.out.println(String.format("%s:获得锁结果(%s)", simpleDateFormat.format(new Date()), rateLimiter.tryAcquire())); Thread.sleep(250L); } } } ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/test/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/ScriptTest.java ================================================ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis; import org.apache.commons.io.IOUtils; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.script.DefaultRedisScript; import org.springframework.data.redis.core.script.RedisScript; import org.springframework.test.context.junit4.SpringRunner; import java.io.IOException; import java.util.Arrays; import java.util.Collections; import java.util.List; @RunWith(SpringRunner.class) @SpringBootTest public class ScriptTest { @Autowired private StringRedisTemplate stringRedisTemplate; @Test public void test01() throws IOException { // 读取 /resources/lua/compareAndSet.lua 脚本 。注意,需要引入下 commons-io 依赖。 String scriptContents = IOUtils.toString(getClass().getResourceAsStream("/lua/compareAndSet.lua"), "UTF-8"); // 创建 RedisScript 对象 RedisScript script = new DefaultRedisScript<>(scriptContents, Long.class); // 执行 LUA 脚本 Long result = stringRedisTemplate.execute(script, Collections.singletonList("yunai:1"), "shuai02", "shuai"); System.out.println(result); } @Test public void test02() throws IOException { // 读取 /resources/lua/compareAndSet.lua 脚本 。注意,需要引入下 commons-io 依赖。 String scriptContents = IOUtils.toString(getClass().getResourceAsStream("/lua/test.lua"), "UTF-8"); // 创建 RedisScript 对象 RedisScript script = new DefaultRedisScript<>(scriptContents, List.class); List result = stringRedisTemplate.execute(script, Arrays.asList("key1", "key2"), "first", "second"); System.out.println(result); } } ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/test/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/SessionTest.java ================================================ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.dao.DataAccessException; import org.springframework.data.redis.core.RedisOperations; import org.springframework.data.redis.core.SessionCallback; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest public class SessionTest { @Autowired private StringRedisTemplate stringRedisTemplate; @Test public void test01() { String result = stringRedisTemplate.execute(new SessionCallback() { @Override public String execute(RedisOperations operations) throws DataAccessException { for (int i = 0; i < 100; i++) { operations.opsForValue().set(String.format("yunai:%d", i), "shuai02"); } return (String) operations.opsForValue().get(String.format("yunai:%d", 0)); } }); System.out.println("result:" + result); } } ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/test/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/Test01.java ================================================ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis; import cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.cacheobject.UserCacheObject; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest public class Test01 { @Autowired private StringRedisTemplate stringRedisTemplate; @Autowired private RedisTemplate redisTemplate; @Test public void testStringSetKey() { stringRedisTemplate.opsForValue().set("yunai", "shuai"); } @Test public void testStringSetKey02() { redisTemplate.opsForValue().set("yunai", "shuai"); } @Test public void testSetAdd() { stringRedisTemplate.opsForSet().add("yunai_descriptions", "shuai", "cai"); } @Test public void testStringSetKeyUserCache() { UserCacheObject object = new UserCacheObject() .setId(1) .setName("芋道源码") .setGender(1); // 男 String key = String.format("user:%d", object.getId()); redisTemplate.opsForValue().set(key, object); } @Test public void testStringGetKeyUserCache() { String key = String.format("user:%d", 1); Object value = redisTemplate.opsForValue().get(key); System.out.println(value); } } ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/test/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/TransactionTest.java ================================================ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; @RunWith(SpringRunner.class) @SpringBootTest //@EnableTransactionManagement public class TransactionTest { @Autowired private StringRedisTemplate stringRedisTemplate; @Test @Transactional public void test01() { // 这里是偷懒,没在 RedisConfiguration 配置类中,设置 stringRedisTemplate 开启事务。 stringRedisTemplate.setEnableTransactionSupport(true); // 执行想要的操作 stringRedisTemplate.opsForValue().set("yunai:1", "shuai"); stringRedisTemplate.opsForValue().set("yudaoyuanma:1", "dai"); // stringRedisTemplate.execute(new SessionCallback() { // // @Override // public String execute(RedisOperations operations) throws DataAccessException { // return null; // } // //// @Override //// public List execute(RedisOperations operations) throws DataAccessException { //// for (int i = 0; i < 100; i++) { //// operations.opsForValue(String.format("yunai:%d", i), "shuai"); //// } //// return null; //// } // // }); } @Test public void test02() { stringRedisTemplate.setEnableTransactionSupport(true); stringRedisTemplate.opsForValue().get("user:1"); stringRedisTemplate.opsForValue().get("user:2"); } } ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/test/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/UserService02Test.java ================================================ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis; import cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.cacheobject.UserCacheObject; import cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.service.UserService02; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class UserService02Test { @Autowired private UserService02 userService; @Test public void testSet() { UserCacheObject object = new UserCacheObject() .setId(1) .setName("芋道源码") .setGender(1); // 男 userService.set(object.getId(), object); } } ================================================ FILE: lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/test/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/package-info.java ================================================ package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis; ================================================ FILE: lab-11-spring-data-redis/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-11-spring-data-redis pom lab-07-spring-data-redis-with-jedis lab-07-spring-data-redis-with-redisson lab-07-spring-data-redis-unit-test ================================================ FILE: lab-11-spring-data-redis/《芋道 Spring Boot Redis 入门》.md ================================================ ================================================ FILE: lab-12-mybatis/lab-12-mybatis-annotation/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE 4.0.0 lab-12-mybatis-annotation org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 5.1.48 org.mybatis.spring.boot mybatis-spring-boot-starter 2.1.1 org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-12-mybatis/lab-12-mybatis-annotation/src/main/java/cn/iocoder/springboot/lab12/mybatis/Application.java ================================================ package cn.iocoder.springboot.lab12.mybatis; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @MapperScan(basePackages = "cn.iocoder.springboot.lab12.mybatis.mapper") public class Application { } ================================================ FILE: lab-12-mybatis/lab-12-mybatis-annotation/src/main/java/cn/iocoder/springboot/lab12/mybatis/dataobject/UserDO.java ================================================ package cn.iocoder.springboot.lab12.mybatis.dataobject; import java.util.Date; /** * 用户 DO */ public class UserDO { /** * 用户编号 */ private Integer id; /** * 账号 */ private String username; /** * 密码(明文) * * ps:生产环境下,千万不要明文噢 */ private String password; /** * 创建时间 */ private Date createTime; public Integer getId() { return id; } public UserDO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserDO setUsername(String username) { this.username = username; return this; } public String getPassword() { return password; } public UserDO setPassword(String password) { this.password = password; return this; } public Date getCreateTime() { return createTime; } public UserDO setCreateTime(Date createTime) { this.createTime = createTime; return this; } } ================================================ FILE: lab-12-mybatis/lab-12-mybatis-annotation/src/main/java/cn/iocoder/springboot/lab12/mybatis/mapper/UserMapper.java ================================================ package cn.iocoder.springboot.lab12.mybatis.mapper; import cn.iocoder.springboot.lab12.mybatis.dataobject.UserDO; import org.apache.ibatis.annotations.*; import org.springframework.stereotype.Repository; import java.util.Collection; import java.util.List; @Repository public interface UserMapper { @Insert("INSERT INTO users(username, password, create_time) VALUES(#{username}, #{password}, #{createTime})") @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id") int insert(UserDO user); @Update(value = { "" }) int updateById(UserDO user); @Insert("DELETE FROM users WHERE id = #{id}") int deleteById(@Param("id") Integer id); // 生产请使用标记删除,除非有点想不开,嘿嘿。 @Select("SELECT username, password, create_time FROM users WHERE id = #{id}") UserDO selectById(@Param("id") Integer id); @Select("SELECT username, password, create_time FROM users WHERE username = #{username}") UserDO selectByUsername(@Param("username") String username); @Select(value = { "" }) List selectByIds(@Param("ids") Collection ids); } ================================================ FILE: lab-12-mybatis/lab-12-mybatis-annotation/src/main/resources/application.yaml ================================================ spring: # datasource 数据源配置内容 datasource: url: jdbc:mysql://47.112.193.81:3306/testb5f4?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: testb5f4 password: F4df4db0ed86@11 # mybatis 配置内容 mybatis: config-location: classpath:mybatis-config.xml # 配置 MyBatis 配置文件路径 # mapper-locations: classpath:mapper/*.xml # 配置 Mapper XML 地址 type-aliases-package: cn.iocoder.springboot.lab12.mybatis.dataobject # 配置数据库实体包路径 ================================================ FILE: lab-12-mybatis/lab-12-mybatis-annotation/src/main/resources/mapper/UserMapper.xml ================================================ id, username, password, create_time INSERT INTO users ( username, password, create_time ) VALUES ( #{username}, #{password}, #{createTime} ) UPDATE users , username = #{username} , password = #{password} WHERE id = #{id} DELETE FROM users WHERE id = #{id} ================================================ FILE: lab-12-mybatis/lab-12-mybatis-annotation/src/main/resources/mybatis-config.xml ================================================ ================================================ FILE: lab-12-mybatis/lab-12-mybatis-annotation/src/main/resources/sql/users.sql ================================================ CREATE TABLE `users` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号', `username` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号', `password` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '密码', `create_time` datetime DEFAULT NULL COMMENT '创建时间', PRIMARY KEY (`id`), UNIQUE KEY `idx_username` (`username`) ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; ================================================ FILE: lab-12-mybatis/lab-12-mybatis-annotation/src/test/java/cn/iocoder/springboot/lab12/mybatis/mapper/UserMapperTest.java ================================================ package cn.iocoder.springboot.lab12.mybatis.mapper; import cn.iocoder.springboot.lab12.mybatis.Application; import cn.iocoder.springboot.lab12.mybatis.dataobject.UserDO; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.UUID; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class UserMapperTest { @Autowired private UserMapper userMapper; @Test public void testInsert() { UserDO user = new UserDO().setUsername(UUID.randomUUID().toString()) .setPassword("nicai").setCreateTime(new Date()); userMapper.insert(user); System.out.println(user.getId()); } @Test public void testUpdateById() { UserDO updateUser = new UserDO().setId(1) .setPassword("wobucai"); userMapper.updateById(updateUser); } @Test public void testDeleteById() { userMapper.deleteById(2); } @Test public void testSelectById() { userMapper.selectById(1); } @Test public void testSelectByUsername() { userMapper.selectByUsername("yunai"); } @Test public void testSelectByIds() { List users = userMapper.selectByIds(Arrays.asList(1, 3)); System.out.println("users:" + users.size()); } } ================================================ FILE: lab-12-mybatis/lab-12-mybatis-plus/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE 4.0.0 lab-12-mybatis-plus org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 5.1.48 com.baomidou mybatis-plus-boot-starter 3.2.0 org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-12-mybatis/lab-12-mybatis-plus/src/main/java/cn/iocoder/springboot/lab12/mybatis/Application.java ================================================ package cn.iocoder.springboot.lab12.mybatis; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @MapperScan(basePackages = "cn.iocoder.springboot.lab12.mybatis.mapper") public class Application { } ================================================ FILE: lab-12-mybatis/lab-12-mybatis-plus/src/main/java/cn/iocoder/springboot/lab12/mybatis/dataobject/UserDO.java ================================================ package cn.iocoder.springboot.lab12.mybatis.dataobject; import com.baomidou.mybatisplus.annotation.TableLogic; import com.baomidou.mybatisplus.annotation.TableName; import java.util.Date; /** * 用户 DO */ @TableName(value = "users") public class UserDO { /** * 用户编号 */ private Integer id; /** * 账号 */ private String username; /** * 密码(明文) * * ps:生产环境下,千万不要明文噢 */ private String password; /** * 创建时间 */ private Date createTime; /** * 是否删除 */ @TableLogic private Integer deleted; public Integer getId() { return id; } public UserDO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserDO setUsername(String username) { this.username = username; return this; } public String getPassword() { return password; } public UserDO setPassword(String password) { this.password = password; return this; } public Date getCreateTime() { return createTime; } public UserDO setCreateTime(Date createTime) { this.createTime = createTime; return this; } public Integer getDeleted() { return deleted; } public UserDO setDeleted(Integer deleted) { this.deleted = deleted; return this; } } ================================================ FILE: lab-12-mybatis/lab-12-mybatis-plus/src/main/java/cn/iocoder/springboot/lab12/mybatis/mapper/UserMapper.java ================================================ package cn.iocoder.springboot.lab12.mybatis.mapper; import cn.iocoder.springboot.lab12.mybatis.dataobject.UserDO; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.metadata.IPage; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; import java.util.Collection; import java.util.Date; import java.util.List; @Repository public interface UserMapper extends BaseMapper { default UserDO selectByUsername(@Param("username") String username) { return selectOne(new QueryWrapper().eq("username", username)); } List selectByIds(@Param("ids") Collection ids); default IPage selectPageByCreateTime(IPage page, @Param("createTime") Date createTime) { return selectPage(page, new QueryWrapper().gt("create_time", createTime) // new QueryWrapper().like("username", "46683d9d") ); } } ================================================ FILE: lab-12-mybatis/lab-12-mybatis-plus/src/main/resources/application.yaml ================================================ spring: # datasource 数据源配置内容 datasource: url: jdbc:mysql://47.112.193.81:3306/testb5f4?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: testb5f4 password: F4df4db0ed86@11 # mybatis-plus 配置内容 mybatis-plus: configuration: map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。 global-config: db-config: id-type: auto # ID 主键自增 logic-delete-value: 1 # 逻辑已删除值(默认为 1) logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) mapper-locations: classpath*:mapper/*.xml type-aliases-package: cn.iocoder.springboot.lab12.mybatis.dataobject # logging logging: level: # dao 开启 debug 模式 mybatis 输入 sql cn: iocoder: springboot: lab12: mybatis: mapper: debug ================================================ FILE: lab-12-mybatis/lab-12-mybatis-plus/src/main/resources/mapper/UserMapper.xml ================================================ id, username, password, create_time ================================================ FILE: lab-12-mybatis/lab-12-mybatis-plus/src/main/resources/sql/users.sql ================================================ CREATE TABLE `users` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号', `username` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号', `password` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '密码', `create_time` datetime DEFAULT NULL COMMENT '创建时间', `deleted` bit(1) DEFAULT NULL COMMENT '是否删除。0-未删除;1-删除', PRIMARY KEY (`id`), UNIQUE KEY `idx_username` (`username`) ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; ================================================ FILE: lab-12-mybatis/lab-12-mybatis-plus/src/test/java/cn/iocoder/springboot/lab12/mybatis/mapper/UserMapperTest.java ================================================ package cn.iocoder.springboot.lab12.mybatis.mapper; import cn.iocoder.springboot.lab12.mybatis.Application; import cn.iocoder.springboot.lab12.mybatis.dataobject.UserDO; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.*; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class UserMapperTest { @Autowired private UserMapper userMapper; @Test public void testInsert() { UserDO user = new UserDO().setUsername(UUID.randomUUID().toString()) .setPassword("nicai").setCreateTime(new Date()) .setDeleted(0); // 一般情况下,是否删除,可以全局枚举下。 userMapper.insert(user); } @Test public void testUpdateById() { UserDO updateUser = new UserDO().setId(1) .setPassword("wobucai"); userMapper.updateById(updateUser); } @Test public void testDeleteById() { userMapper.deleteById(2); } @Test public void testSelectById() { userMapper.selectById(1); } @Test public void testSelectByUsername() { userMapper.selectByUsername("yunai"); } @Test public void testSelectByIds() { List users = userMapper.selectByIds(Arrays.asList(1, 3)); System.out.println("users:" + users.size()); } @Test public void testSelectPageByCreateTime() { IPage page = new Page<>(1, 10); Date createTime = new Date(2018 - 1990, Calendar.FEBRUARY, 24); // 临时 Demo ,实际不建议这么写 page = userMapper.selectPageByCreateTime(page, createTime); System.out.println("users:" + page.getRecords().size()); } } ================================================ FILE: lab-12-mybatis/lab-12-mybatis-plus-tenant/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE 4.0.0 lab-12-mybatis-plus-tenant org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 5.1.48 com.baomidou mybatis-plus-boot-starter 3.4.1 org.springframework.boot spring-boot-starter-test test com.alibaba transmittable-thread-local 2.12.2 ================================================ FILE: lab-12-mybatis/lab-12-mybatis-plus-tenant/src/main/java/cn/iocoder/springboot/lab12/mybatis/Application.java ================================================ package cn.iocoder.springboot.lab12.mybatis; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @MapperScan(basePackages = "cn.iocoder.springboot.lab12.mybatis.mapper") public class Application { } ================================================ FILE: lab-12-mybatis/lab-12-mybatis-plus-tenant/src/main/java/cn/iocoder/springboot/lab12/mybatis/config/AsyncConfig.java ================================================ package cn.iocoder.springboot.lab12.mybatis.config; import com.alibaba.ttl.TtlRunnable; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; @Configuration @EnableAsync public class AsyncConfig { @Bean public BeanPostProcessor executorBeanPostProcessor() { return new BeanPostProcessor() { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (!(bean instanceof ThreadPoolTaskExecutor)) { return bean; } ThreadPoolTaskExecutor executor = (ThreadPoolTaskExecutor) bean; executor.setTaskDecorator(TtlRunnable::get); return executor; } }; } } ================================================ FILE: lab-12-mybatis/lab-12-mybatis-plus-tenant/src/main/java/cn/iocoder/springboot/lab12/mybatis/config/MybatisPlusConfig.java ================================================ package cn.iocoder.springboot.lab12.mybatis.config; import cn.iocoder.springboot.lab12.mybatis.context.TenantHolder; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler; import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.LongValue; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MybatisPlusConfig { /** * 新多租户插件配置,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存万一出现问题 */ @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new TenantLineHandler() { @Override public Expression getTenantId() { Integer tenantId = TenantHolder.getTenantId(); return new LongValue(tenantId); } // 这是 default 方法,默认返回 false 表示所有表都需要拼多租户条件 @Override public boolean ignoreTable(String tableName) { return false; } })); // 如果用了分页插件注意先 add TenantLineInnerInterceptor 再 add PaginationInnerInterceptor // 用了分页插件必须设置 MybatisConfiguration#useDeprecatedExecutor = false // interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); return interceptor; } } ================================================ FILE: lab-12-mybatis/lab-12-mybatis-plus-tenant/src/main/java/cn/iocoder/springboot/lab12/mybatis/context/TenantHolder.java ================================================ package cn.iocoder.springboot.lab12.mybatis.context; import com.alibaba.ttl.TransmittableThreadLocal; public class TenantHolder { private static final ThreadLocal TENANT_ID = new TransmittableThreadLocal<>(); // private static final ThreadLocal TENANT_ID = new ThreadLocal<>(); // private static final ThreadLocal TENANT_ID = new InheritableThreadLocal<>(); public static void setTenantId(Integer tenantId) { TENANT_ID.set(tenantId); } public static Integer getTenantId() { return TENANT_ID.get(); } } ================================================ FILE: lab-12-mybatis/lab-12-mybatis-plus-tenant/src/main/java/cn/iocoder/springboot/lab12/mybatis/core/TtlThreadPoolTaskExecutor.java ================================================ package cn.iocoder.springboot.lab12.mybatis.core; import com.alibaba.ttl.TtlCallable; import com.alibaba.ttl.TtlRunnable; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.util.concurrent.ListenableFuture; import java.util.Objects; import java.util.concurrent.Callable; import java.util.concurrent.Future; @Deprecated public class TtlThreadPoolTaskExecutor extends ThreadPoolTaskExecutor { @Override public void execute(Runnable task) { super.execute(Objects.requireNonNull(TtlRunnable.get(task))); } @Override public void execute(Runnable task, long startTimeout) { super.execute(Objects.requireNonNull(TtlRunnable.get(task)), startTimeout); } @Override public Future submit(Runnable task) { return super.submit(Objects.requireNonNull(TtlRunnable.get(task))); } @Override public Future submit(Callable task) { return super.submit(Objects.requireNonNull(TtlCallable.get(task))); } @Override public ListenableFuture submitListenable(Runnable task) { return super.submitListenable(Objects.requireNonNull(TtlRunnable.get(task))); } @Override public ListenableFuture submitListenable(Callable task) { return super.submitListenable(Objects.requireNonNull(TtlCallable.get(task))); } } ================================================ FILE: lab-12-mybatis/lab-12-mybatis-plus-tenant/src/main/java/cn/iocoder/springboot/lab12/mybatis/dataobject/UserDO.java ================================================ package cn.iocoder.springboot.lab12.mybatis.dataobject; import com.baomidou.mybatisplus.annotation.TableLogic; import com.baomidou.mybatisplus.annotation.TableName; import java.util.Date; /** * 用户 DO */ @TableName(value = "users") public class UserDO { /** * 用户编号 */ private Integer id; /** * 账号 */ private String username; /** * 密码(明文) * * ps:生产环境下,千万不要明文噢 */ private String password; /** * 创建时间 */ private Date createTime; /** * 是否删除 */ @TableLogic private Integer deleted; /** * 租户编号 */ private Integer tenantId; public Integer getId() { return id; } public UserDO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserDO setUsername(String username) { this.username = username; return this; } public String getPassword() { return password; } public UserDO setPassword(String password) { this.password = password; return this; } public Date getCreateTime() { return createTime; } public UserDO setCreateTime(Date createTime) { this.createTime = createTime; return this; } public Integer getDeleted() { return deleted; } public UserDO setDeleted(Integer deleted) { this.deleted = deleted; return this; } public Integer getTenantId() { return tenantId; } public UserDO setTenantId(Integer tenantId) { this.tenantId = tenantId; return this; } } ================================================ FILE: lab-12-mybatis/lab-12-mybatis-plus-tenant/src/main/java/cn/iocoder/springboot/lab12/mybatis/dataobject/UserProfileDO.java ================================================ package cn.iocoder.springboot.lab12.mybatis.dataobject; import com.baomidou.mybatisplus.annotation.TableLogic; import com.baomidou.mybatisplus.annotation.TableName; /** * 用户拓展 DO */ @TableName(value = "user_profile") public class UserProfileDO { /** * 编号 */ private Integer id; /** * 用户编号 */ private Integer userId; /** * 性别 */ private Integer gender; /** * 是否删除 */ @TableLogic private Integer deleted; /** * 租户编号 */ private Integer tenantId; public Integer getId() { return id; } public UserProfileDO setId(Integer id) { this.id = id; return this; } public Integer getGender() { return gender; } public UserProfileDO setGender(Integer gender) { this.gender = gender; return this; } public Integer getDeleted() { return deleted; } public UserProfileDO setDeleted(Integer deleted) { this.deleted = deleted; return this; } public Integer getTenantId() { return tenantId; } public UserProfileDO setTenantId(Integer tenantId) { this.tenantId = tenantId; return this; } public Integer getUserId() { return userId; } public UserProfileDO setUserId(Integer userId) { this.userId = userId; return this; } } ================================================ FILE: lab-12-mybatis/lab-12-mybatis-plus-tenant/src/main/java/cn/iocoder/springboot/lab12/mybatis/mapper/UserMapper.java ================================================ package cn.iocoder.springboot.lab12.mybatis.mapper; import cn.iocoder.springboot.lab12.mybatis.dataobject.UserDO; import cn.iocoder.springboot.lab12.mybatis.vo.UserDetailVO; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.metadata.IPage; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; import java.util.Collection; import java.util.Date; import java.util.List; @Repository public interface UserMapper extends BaseMapper { default UserDO selectByUsername(@Param("username") String username) { return selectOne(new QueryWrapper().eq("username", username)); } List selectByIds(@Param("ids") Collection ids); default IPage selectPageByCreateTime(IPage page, @Param("createTime") Date createTime) { return selectPage(page, new QueryWrapper().gt("create_time", createTime) // new QueryWrapper().like("username", "46683d9d") ); } List selectListA(); List selectListB(); } ================================================ FILE: lab-12-mybatis/lab-12-mybatis-plus-tenant/src/main/java/cn/iocoder/springboot/lab12/mybatis/mapper/UserProfileMapper.java ================================================ package cn.iocoder.springboot.lab12.mybatis.mapper; import cn.iocoder.springboot.lab12.mybatis.dataobject.UserProfileDO; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.springframework.stereotype.Repository; @Repository public interface UserProfileMapper extends BaseMapper { } ================================================ FILE: lab-12-mybatis/lab-12-mybatis-plus-tenant/src/main/java/cn/iocoder/springboot/lab12/mybatis/service/UserService.java ================================================ package cn.iocoder.springboot.lab12.mybatis.service; import cn.iocoder.springboot.lab12.mybatis.dataobject.UserDO; import cn.iocoder.springboot.lab12.mybatis.mapper.UserMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.AsyncResult; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.concurrent.Future; @Service public class UserService { private final Logger log = LoggerFactory.getLogger(UserService.class); @Resource private UserMapper userMapper; @Async public Future getUserAsync(Integer id) { UserDO userDO = userMapper.selectById(id); log.info("[getUserAsync][id({}) user({})]", id, userDO); return AsyncResult.forValue(userDO); } } ================================================ FILE: lab-12-mybatis/lab-12-mybatis-plus-tenant/src/main/java/cn/iocoder/springboot/lab12/mybatis/util/TtlExecutorsUtil.java ================================================ package cn.iocoder.springboot.lab12.mybatis.util; import com.alibaba.ttl.spi.TtlEnhanced; import com.alibaba.ttl.threadpool.agent.TtlAgent; import org.springframework.lang.Nullable; import java.util.concurrent.Executor; /** * {@link com.alibaba.ttl.threadpool.TtlExecutors} 工具类 */ @Deprecated public class TtlExecutorsUtil { public static Executor getTtlThreadPoolTaskExecutor(@Nullable Executor executor) { if (TtlAgent.isTtlAgentLoaded() || null == executor || executor instanceof TtlEnhanced) { return executor; } // return new ExecutorTtlWrapper(executor, true); return null; } } ================================================ FILE: lab-12-mybatis/lab-12-mybatis-plus-tenant/src/main/java/cn/iocoder/springboot/lab12/mybatis/vo/UserDetailVO.java ================================================ package cn.iocoder.springboot.lab12.mybatis.vo; import com.baomidou.mybatisplus.annotation.TableLogic; import java.util.Date; public class UserDetailVO { /** * 用户编号 */ private Integer id; /** * 账号 */ private String username; /** * 密码(明文) * * ps:生产环境下,千万不要明文噢 */ private String password; /** * 性别 */ private Integer gender; /** * 创建时间 */ private Date createTime; /** * 是否删除 */ @TableLogic private Integer deleted; /** * 租户编号 */ private Integer tenantId; public Integer getId() { return id; } public UserDetailVO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserDetailVO setUsername(String username) { this.username = username; return this; } public String getPassword() { return password; } public UserDetailVO setPassword(String password) { this.password = password; return this; } public Integer getGender() { return gender; } public UserDetailVO setGender(Integer gender) { this.gender = gender; return this; } public Date getCreateTime() { return createTime; } public UserDetailVO setCreateTime(Date createTime) { this.createTime = createTime; return this; } public Integer getDeleted() { return deleted; } public UserDetailVO setDeleted(Integer deleted) { this.deleted = deleted; return this; } public Integer getTenantId() { return tenantId; } public UserDetailVO setTenantId(Integer tenantId) { this.tenantId = tenantId; return this; } @Override public String toString() { return "UserDetailVO{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", gender=" + gender + ", createTime=" + createTime + ", deleted=" + deleted + ", tenantId=" + tenantId + '}'; } } ================================================ FILE: lab-12-mybatis/lab-12-mybatis-plus-tenant/src/main/resources/application.yaml ================================================ spring: # datasource 数据源配置内容 datasource: url: jdbc:mysql://127.0.0.1:3306/testb5f4?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: 123456 # mybatis-plus 配置内容 mybatis-plus: configuration: map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。 global-config: db-config: id-type: auto # ID 主键自增 logic-delete-value: 1 # 逻辑已删除值(默认为 1) logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) mapper-locations: classpath*:mapper/*.xml type-aliases-package: cn.iocoder.springboot.lab12.mybatis.dataobject # logging logging: level: # dao 开启 debug 模式 mybatis 输入 sql cn: iocoder: springboot: lab12: mybatis: mapper: debug ================================================ FILE: lab-12-mybatis/lab-12-mybatis-plus-tenant/src/main/resources/mapper/UserMapper.xml ================================================ id, username, password, create_time ================================================ FILE: lab-12-mybatis/lab-12-mybatis-plus-tenant/src/main/resources/sql/users.sql ================================================ CREATE TABLE `users` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号', `username` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号', `password` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '密码', `create_time` datetime DEFAULT NULL COMMENT '创建时间', `deleted` bit(1) DEFAULT NULL COMMENT '是否删除。0-未删除;1-删除', `tenant_id` int(11) NOT NULL COMMENT '租户编号', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; CREATE TABLE `user_profile` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '编号', `user_id` int(11) NOT NULL COMMENT '用户编号', `gender` int(11) NOT NULL COMMENT '性别', `deleted` bit(1) DEFAULT NULL COMMENT '是否删除。0-未删除;1-删除', `tenant_id` int(11) NOT NULL COMMENT '租户编号', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; ================================================ FILE: lab-12-mybatis/lab-12-mybatis-plus-tenant/src/test/java/cn/iocoder/springboot/lab12/mybatis/mapper/UserMapperTest.java ================================================ package cn.iocoder.springboot.lab12.mybatis.mapper; import cn.iocoder.springboot.lab12.mybatis.Application; import cn.iocoder.springboot.lab12.mybatis.dataobject.UserDO; import cn.iocoder.springboot.lab12.mybatis.dataobject.UserProfileDO; import cn.iocoder.springboot.lab12.mybatis.vo.UserDetailVO; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.*; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class UserMapperTest { @Autowired private UserMapper userMapper; @Autowired private UserProfileMapper userProfileMapper; @Test public void initTestData() { // 清理数据 userMapper.delete(new QueryWrapper<>()); userProfileMapper.delete(new QueryWrapper<>()); // 插入一个用户 UserDO userDO = new UserDO().setUsername(UUID.randomUUID().toString()) .setPassword("nicai").setCreateTime(new Date()) .setDeleted(0); // 一般情况下,是否删除,可以全局枚举下。 userMapper.insert(userDO); // 插入该用户的拓展信息 UserProfileDO userProfileDO = new UserProfileDO(); userProfileDO.setUserId(userDO.getId()); userProfileDO.setGender(1); userProfileDO.setTenantId(10); // TODO 全局写死 userProfileDO.setDeleted(0); // 一般情况下,是否删除,可以全局枚举下。 userProfileMapper.insert(userProfileDO); } @Test public void testInsert() { UserDO user = new UserDO().setUsername(UUID.randomUUID().toString()) .setPassword("nicai").setCreateTime(new Date()) .setDeleted(0); // 一般情况下,是否删除,可以全局枚举下。 userMapper.insert(user); } @Test public void testUpdateById() { UserDO updateUser = new UserDO().setId(1) .setPassword("wobucai"); userMapper.updateById(updateUser); } @Test public void testDeleteById() { userMapper.deleteById(2); } @Test public void testSelectById() { userMapper.selectById(1); } @Test public void testSelectByUsername() { UserDO userDO = userMapper.selectByUsername("yunai"); System.out.println(userDO); } @Test public void testSelectByIds() { List users = userMapper.selectByIds(Arrays.asList(1, 3)); System.out.println("users:" + users.size()); } @Test public void testSelectPageByCreateTime() { IPage page = new Page<>(1, 10); Date createTime = new Date(2018 - 1990, Calendar.FEBRUARY, 24); // 临时 Demo ,实际不建议这么写 page = userMapper.selectPageByCreateTime(page, createTime); System.out.println("users:" + page.getRecords().size()); } @Test public void testSelectListA() { List list = userMapper.selectListA(); System.out.println(list); } @Test public void testSelectListB() { List list = userMapper.selectListB(); System.out.println(list); } } ================================================ FILE: lab-12-mybatis/lab-12-mybatis-plus-tenant/src/test/java/cn/iocoder/springboot/lab12/mybatis/service/UserServiceTest.java ================================================ package cn.iocoder.springboot.lab12.mybatis.service; import cn.iocoder.springboot.lab12.mybatis.Application; import cn.iocoder.springboot.lab12.mybatis.context.TenantHolder; import cn.iocoder.springboot.lab12.mybatis.dataobject.UserDO; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import javax.annotation.Resource; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import static org.junit.Assert.*; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class UserServiceTest { @Resource private UserService userService; @Test public void testGetUserAsync() throws ExecutionException, InterruptedException { TenantHolder.setTenantId(10); // TODO 芋艿:写死 Future future = userService.getUserAsync(9); future.get(); } } ================================================ FILE: lab-12-mybatis/lab-12-mybatis-tk/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE 4.0.0 lab-12-mybatis-tk org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 5.1.48 org.mybatis.spring.boot mybatis-spring-boot-starter 2.1.1 tk.mybatis mapper-spring-boot-starter 2.1.5 com.github.pagehelper pagehelper-spring-boot-starter 1.2.5 org.mybatis.spring.boot mybatis-spring-boot-starter org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-12-mybatis/lab-12-mybatis-tk/src/main/java/cn/iocoder/springboot/lab12/mybatis/Application.java ================================================ package cn.iocoder.springboot.lab12.mybatis; import org.springframework.boot.autoconfigure.SpringBootApplication; import tk.mybatis.spring.annotation.MapperScan; @SpringBootApplication @MapperScan(basePackages = "cn.iocoder.springboot.lab12.mybatis.mapper") // 注意,要换成 tk 提供的 @MapperScan 注解 public class Application { } ================================================ FILE: lab-12-mybatis/lab-12-mybatis-tk/src/main/java/cn/iocoder/springboot/lab12/mybatis/dataobject/UserDO.java ================================================ package cn.iocoder.springboot.lab12.mybatis.dataobject; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; import java.util.Date; /** * 用户 DO */ @Table(name = "users") public class UserDO { /** * 用户编号 */ @Id // 表示该字段为主键 ID @GeneratedValue(strategy = GenerationType.IDENTITY, generator = "JDBC") // strategy 设置使用数据库主键自增策略;generator 设置插入完成后,查询最后生成的 ID 填充到该属性中。 private Integer id; /** * 账号 */ private String username; /** * 密码(明文) * * ps:生产环境下,千万不要明文噢 */ private String password; /** * 创建时间 */ private Date createTime; public Integer getId() { return id; } public UserDO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserDO setUsername(String username) { this.username = username; return this; } public String getPassword() { return password; } public UserDO setPassword(String password) { this.password = password; return this; } public Date getCreateTime() { return createTime; } public UserDO setCreateTime(Date createTime) { this.createTime = createTime; return this; } } ================================================ FILE: lab-12-mybatis/lab-12-mybatis-tk/src/main/java/cn/iocoder/springboot/lab12/mybatis/mapper/UserMapper.java ================================================ package cn.iocoder.springboot.lab12.mybatis.mapper; import cn.iocoder.springboot.lab12.mybatis.dataobject.UserDO; import cn.iocoder.springboot.lab12.mybatis.util.BaseMapper; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; import tk.mybatis.mapper.entity.Example; import java.util.Collection; import java.util.Date; import java.util.List; @Repository public interface UserMapper extends BaseMapper { // 继承的是,我们定义的 BaseMapper 接口 default UserDO selectByUsername(@Param("username") String username) { Example example = new Example(UserDO.class); // 创建 Criteria 对象,设置 username 查询条件 example.createCriteria().andEqualTo("username", username); // 执行查询 return selectOneByExample(example); } List selectByIds(@Param("ids") Collection ids); default List selectListByCreateTime(@Param("createTime") Date createTime) { Example example = new Example(UserDO.class); // 创建 Criteria 对象,设置 create_time 查询条件 example.createCriteria().andGreaterThan("createTime", createTime); return selectByExample(example); } } ================================================ FILE: lab-12-mybatis/lab-12-mybatis-tk/src/main/java/cn/iocoder/springboot/lab12/mybatis/util/BaseMapper.java ================================================ package cn.iocoder.springboot.lab12.mybatis.util; import tk.mybatis.mapper.common.Mapper; import tk.mybatis.mapper.common.MySqlMapper; public interface BaseMapper extends Mapper, MySqlMapper { } ================================================ FILE: lab-12-mybatis/lab-12-mybatis-tk/src/main/resources/application.yaml ================================================ spring: # datasource 数据源配置内容 datasource: url: jdbc:mysql://47.112.193.81:3306/testb5f4?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: testb5f4 password: F4df4db0ed86@11 # mybatis 配置内容 mybatis: config-location: classpath:mybatis-config.xml # 配置 MyBatis 配置文件路径 mapper-locations: classpath:mapper/*.xml # 配置 Mapper XML 地址 type-aliases-package: cn.iocoder.springboot.lab12.mybatis.dataobject # 配置数据库实体包路径 # mapper 配置内容 mapper: mappers: cn.iocoder.springboot.lab12.mybatis.util.BaseMapper # 设置基础 Mapper 接口 not-empty: true # 在 INSERT 和 UPDATE 操作时,是否会判断字段是否为空。即 identity: MYSQL # PageHelper 配置内容 # 具体的参数作用,看 https://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/zh/HowToUse.md pagehelper: helperDialect: mysql # 分页插件会自动检测当前的数据库链接,自动选择合适的分页方式。 你可以配置helperDialect属性来指定分页插件使用哪种方言。 reasonable: true # 分页合理化参数,默认值为false。当该参数设置为 true 时,pageNum<=0 时会查询第一页, pageNum>pages(超过总数时),会查询最后一页。默认false 时,直接根据参数进行查询。 supportMethodsArguments: true # 支持通过 Mapper 接口参数来传递分页参数,默认值false,分页插件会从查询方法的参数值中,自动根据上面 params 配置的字段中取值,查找到合适的值时就会自动分页。 使用方法可以参考测试代码中的 com.github.pagehelper.test.basic 包下的 ArgumentsMapTest 和 ArgumentsObjTest。 # logging logging: level: # dao 开启 debug 模式 mybatis 输入 sql cn: iocoder: springboot: lab12: mybatis: mapper: debug ================================================ FILE: lab-12-mybatis/lab-12-mybatis-tk/src/main/resources/mapper/UserMapper.xml ================================================ id, username, password, create_time ================================================ FILE: lab-12-mybatis/lab-12-mybatis-tk/src/main/resources/mybatis-config.xml ================================================ ================================================ FILE: lab-12-mybatis/lab-12-mybatis-tk/src/main/resources/sql/users.sql ================================================ CREATE TABLE `users` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号', `username` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号', `password` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '密码', `create_time` datetime DEFAULT NULL COMMENT '创建时间', PRIMARY KEY (`id`), UNIQUE KEY `idx_username` (`username`) ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; ================================================ FILE: lab-12-mybatis/lab-12-mybatis-tk/src/test/java/cn/iocoder/springboot/lab12/mybatis/mapper/UserMapperTest.java ================================================ package cn.iocoder.springboot.lab12.mybatis.mapper; import cn.iocoder.springboot.lab12.mybatis.Application; import cn.iocoder.springboot.lab12.mybatis.dataobject.UserDO; import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.*; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class UserMapperTest { @Autowired private UserMapper userMapper; @Test public void testInsert() { UserDO user = new UserDO().setUsername(UUID.randomUUID().toString()) .setPassword("nicai").setCreateTime(new Date()); userMapper.insert(user); System.out.println(user.getId()); } @Test public void testUpdateById() { UserDO updateUser = new UserDO().setId(1) .setPassword("wobucai"); userMapper.updateByPrimaryKey(updateUser); } @Test public void testDeleteById() { userMapper.deleteByPrimaryKey(2); } @Test public void testSelectById() { userMapper.selectByPrimaryKey(1); } @Test public void testSelectByUsername() { userMapper.selectByUsername("yunai"); } @Test public void testSelectByIds() { List users = userMapper.selectByIds(Arrays.asList(1, 3)); System.out.println("users:" + users.size()); } @Test // 更多使用,可以参考 https://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/zh/HowToUse.md public void testSelectPageByCreateTime() { // 设置分页 PageHelper.startPage(1, 10); Date createTime = new Date(2018 - 1990, Calendar.FEBRUARY, 24); // 临时 Demo ,实际不建议这么写 // 执行列表查询 // PageHelper 会自动发起分页的数量查询,设置到 PageHelper 中 List users = userMapper.selectListByCreateTime(createTime); // 实际返回的是 com.github.pagehelper.Page 代理对象 // 转换成 PageInfo 对象,并输出分页 PageInfo page = new PageInfo<>(users); System.out.println(page.getTotal()); } } ================================================ FILE: lab-12-mybatis/lab-12-mybatis-xml/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE 4.0.0 lab-12-mybatis-xml org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 5.1.48 org.mybatis.spring.boot mybatis-spring-boot-starter 2.1.1 org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-12-mybatis/lab-12-mybatis-xml/src/main/java/cn/iocoder/springboot/lab12/mybatis/Application.java ================================================ package cn.iocoder.springboot.lab12.mybatis; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @MapperScan(basePackages = "cn.iocoder.springboot.lab12.mybatis.mapper") public class Application { } ================================================ FILE: lab-12-mybatis/lab-12-mybatis-xml/src/main/java/cn/iocoder/springboot/lab12/mybatis/dataobject/UserDO.java ================================================ package cn.iocoder.springboot.lab12.mybatis.dataobject; import java.util.Date; /** * 用户 DO */ public class UserDO { /** * 用户编号 */ private Integer id; /** * 账号 */ private String username; /** * 密码(明文) * * ps:生产环境下,千万不要明文噢 */ private String password; /** * 创建时间 */ private Date createTime; public Integer getId() { return id; } public UserDO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserDO setUsername(String username) { this.username = username; return this; } public String getPassword() { return password; } public UserDO setPassword(String password) { this.password = password; return this; } public Date getCreateTime() { return createTime; } public UserDO setCreateTime(Date createTime) { this.createTime = createTime; return this; } } ================================================ FILE: lab-12-mybatis/lab-12-mybatis-xml/src/main/java/cn/iocoder/springboot/lab12/mybatis/mapper/UserMapper.java ================================================ package cn.iocoder.springboot.lab12.mybatis.mapper; import cn.iocoder.springboot.lab12.mybatis.dataobject.UserDO; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; import java.util.Collection; import java.util.List; @Repository public interface UserMapper { int insert(UserDO user); int updateById(UserDO user); int deleteById(@Param("id") Integer id); // 生产请使用标记删除,除非有点想不开,嘿嘿。 UserDO selectById(@Param("id") Integer id); UserDO selectByUsername(@Param("username") String username); List selectByIds(@Param("ids")Collection ids); } ================================================ FILE: lab-12-mybatis/lab-12-mybatis-xml/src/main/resources/application.yaml ================================================ spring: # datasource 数据源配置内容 datasource: url: jdbc:mysql://47.112.193.81:3306/testb5f4?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: testb5f4 password: F4df4db0ed86@11 # mybatis 配置内容 mybatis: config-location: classpath:mybatis-config.xml # 配置 MyBatis 配置文件路径 mapper-locations: classpath:mapper/*.xml # 配置 Mapper XML 地址 type-aliases-package: cn.iocoder.springboot.lab12.mybatis.dataobject # 配置数据库实体包路径 ================================================ FILE: lab-12-mybatis/lab-12-mybatis-xml/src/main/resources/mapper/UserMapper.xml ================================================ id, username, password, create_time INSERT INTO users ( username, password, create_time ) VALUES ( #{username}, #{password}, #{createTime} ) UPDATE users , username = #{username} , password = #{password} WHERE id = #{id} DELETE FROM users WHERE id = #{id} ================================================ FILE: lab-12-mybatis/lab-12-mybatis-xml/src/main/resources/mybatis-config.xml ================================================ ================================================ FILE: lab-12-mybatis/lab-12-mybatis-xml/src/main/resources/sql/users.sql ================================================ CREATE TABLE `users` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号', `username` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号', `password` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '密码', `create_time` datetime DEFAULT NULL COMMENT '创建时间', PRIMARY KEY (`id`), UNIQUE KEY `idx_username` (`username`) ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; ================================================ FILE: lab-12-mybatis/lab-12-mybatis-xml/src/test/java/cn/iocoder/springboot/lab12/mybatis/mapper/UserMapperTest.java ================================================ package cn.iocoder.springboot.lab12.mybatis.mapper; import cn.iocoder.springboot.lab12.mybatis.Application; import cn.iocoder.springboot.lab12.mybatis.dataobject.UserDO; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.UUID; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class UserMapperTest { @Autowired private UserMapper userMapper; @Test public void testInsert() { UserDO user = new UserDO().setUsername(UUID.randomUUID().toString()) .setPassword("nicai").setCreateTime(new Date()); userMapper.insert(user); } @Test public void testUpdateById() { UserDO updateUser = new UserDO().setId(1) .setPassword("wobucai"); userMapper.updateById(updateUser); } @Test public void testDeleteById() { userMapper.deleteById(2); } @Test public void testSelectById() { userMapper.selectById(1); } @Test public void testSelectByUsername() { userMapper.selectByUsername("yunai"); } @Test public void testSelectByIds() { List users = userMapper.selectByIds(Arrays.asList(1, 3)); System.out.println("users:" + users.size()); } } ================================================ FILE: lab-12-mybatis/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-12-mybatis pom lab-12-mybatis-xml lab-12-mybatis-annotation lab-12-mybatis-plus lab-12-mybatis-tk lab-12-mybatis-plus-tenant ================================================ FILE: lab-12-mybatis/《芋道 Spring Boot MyBatis 入门》.md ================================================ ================================================ FILE: lab-13-spring-data-jpa/lab-13-jpa/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE 4.0.0 lab-13-jpa org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 5.1.48 org.springframework.boot spring-boot-starter-data-jpa org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-13-spring-data-jpa/lab-13-jpa/src/main/java/cn/iocoder/springboot/lab13/jpa/Application.java ================================================ package cn.iocoder.springboot.lab13.jpa; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { } ================================================ FILE: lab-13-spring-data-jpa/lab-13-jpa/src/main/java/cn/iocoder/springboot/lab13/jpa/dataobject/UserDO.java ================================================ package cn.iocoder.springboot.lab13.jpa.dataobject; import javax.persistence.*; import java.util.Date; /** * 用户 DO */ @Entity @Table(name = "users") public class UserDO { /** * 用户编号 */ @Id @GeneratedValue(strategy = GenerationType.IDENTITY, // strategy 设置使用数据库主键自增策略; generator = "JDBC") // generator 设置插入完成后,查询最后生成的 ID 填充到该属性中。 private Integer id; /** * 账号 */ @Column(nullable = false) private String username; /** * 密码(明文) * * ps:生产环境下,千万不要明文噢 */ @Column(nullable = false) private String password; /** * 创建时间 */ @Column(name = "create_time", nullable = false) private Date createTime; public Integer getId() { return id; } public UserDO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserDO setUsername(String username) { this.username = username; return this; } public String getPassword() { return password; } public UserDO setPassword(String password) { this.password = password; return this; } public Date getCreateTime() { return createTime; } public UserDO setCreateTime(Date createTime) { this.createTime = createTime; return this; } } ================================================ FILE: lab-13-spring-data-jpa/lab-13-jpa/src/main/java/cn/iocoder/springboot/lab13/jpa/repository/UserRepository01.java ================================================ package cn.iocoder.springboot.lab13.jpa.repository; import cn.iocoder.springboot.lab13.jpa.dataobject.UserDO; import org.springframework.data.repository.CrudRepository; public interface UserRepository01 extends CrudRepository { } ================================================ FILE: lab-13-spring-data-jpa/lab-13-jpa/src/main/java/cn/iocoder/springboot/lab13/jpa/repository/UserRepository02.java ================================================ package cn.iocoder.springboot.lab13.jpa.repository; import cn.iocoder.springboot.lab13.jpa.dataobject.UserDO; import org.springframework.data.repository.PagingAndSortingRepository; public interface UserRepository02 extends PagingAndSortingRepository { } ================================================ FILE: lab-13-spring-data-jpa/lab-13-jpa/src/main/java/cn/iocoder/springboot/lab13/jpa/repository/UserRepository03.java ================================================ package cn.iocoder.springboot.lab13.jpa.repository; import cn.iocoder.springboot.lab13.jpa.dataobject.UserDO; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.repository.PagingAndSortingRepository; import java.util.Date; public interface UserRepository03 extends PagingAndSortingRepository { UserDO findByUsername(String username); Page findByCreateTimeAfter(Date createTime, Pageable pageable); } ================================================ FILE: lab-13-spring-data-jpa/lab-13-jpa/src/main/java/cn/iocoder/springboot/lab13/jpa/repository/UserRepository04.java ================================================ package cn.iocoder.springboot.lab13.jpa.repository; import cn.iocoder.springboot.lab13.jpa.dataobject.UserDO; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.query.Param; public interface UserRepository04 extends PagingAndSortingRepository { @Query("SELECT u FROM UserDO u WHERE u.username = ?1") UserDO findByUsername01(String username); @Query("SELECT u FROM UserDO u WHERE u.username = :username") UserDO findByUsername02(@Param("username") String username); @Query(value = "SELECT * FROM users u WHERE u.username = :username", nativeQuery = true) UserDO findByUsername03(@Param("username") String username); @Query("UPDATE UserDO u SET u.username = :username WHERE u.id = :id") @Modifying int updateUsernameById(Integer id, String username); } ================================================ FILE: lab-13-spring-data-jpa/lab-13-jpa/src/main/resources/application.yaml ================================================ spring: # datasource 数据源配置内容 datasource: url: jdbc:mysql://47.112.193.81:3306/testb5f4?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: testb5f4 password: F4df4db0ed86@11 # JPA 配置内容,对应 JpaProperties 类 jpa: show-sql: true # 打印 SQL 。生产环境,建议关闭 # Hibernate 配置内容,对应 HibernateProperties 类 hibernate: ddl-auto: none ================================================ FILE: lab-13-spring-data-jpa/lab-13-jpa/src/test/java/cn/iocoder/springboot/lab13/jpa/repository/UserRepository01Test.java ================================================ package cn.iocoder.springboot.lab13.jpa.repository; import cn.iocoder.springboot.lab13.jpa.dataobject.UserDO; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.util.Assert; import java.util.Arrays; import java.util.Date; import java.util.Optional; import java.util.UUID; @RunWith(SpringRunner.class) @SpringBootTest public class UserRepository01Test { @Autowired private UserRepository01 userRepository; @Test // 插入一条记录 public void testSave() { UserDO user = new UserDO().setUsername(UUID.randomUUID().toString()) .setPassword("nicai").setCreateTime(new Date()); userRepository.save(user); } @Test // 更新一条记录 public void testUpdate() { // 先查询一条记录 Optional userDO = userRepository.findById(1); Assert.isTrue(userDO.isPresent(), "记录不能为空"); // 更新一条记录 UserDO updateUser = userDO.get(); updateUser.setPassword("yudaoyuanma"); userRepository.save(updateUser); } @Test // 根据 ID 编号,删除一条记录 public void testDelete() { userRepository.deleteById(2); } @Test // 根据 ID 编号,查询一条记录 public void testSelectById() { Optional userDO = userRepository.findById(1); System.out.println(userDO.get()); } @Test // 根据 ID 编号数组,查询多条记录 public void testSelectByIds() { Iterable users = userRepository.findAllById(Arrays.asList(1, 4)); users.forEach(System.out::println); } } ================================================ FILE: lab-13-spring-data-jpa/lab-13-jpa/src/test/java/cn/iocoder/springboot/lab13/jpa/repository/UserRepository02Test.java ================================================ package cn.iocoder.springboot.lab13.jpa.repository; import cn.iocoder.springboot.lab13.jpa.dataobject.UserDO; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest public class UserRepository02Test { @Autowired private UserRepository02 userRepository; @Test // 排序 public void testFindAll() { // 创建排序条件 Sort sort = new Sort(Sort.Direction.DESC, "id"); // 执行排序操作 Iterable iterable = userRepository.findAll(sort); // 打印 iterable.forEach(System.out::println); } @Test // 分页 public void testFindPage() { // 创建排序条件 Sort sort = new Sort(Sort.Direction.DESC, "id"); // 创建分页条件 Pageable pageable = PageRequest.of(1, 10, sort); // 执行分页操作 Page page = userRepository.findAll(pageable); // 打印 System.out.println(page.getTotalElements()); System.out.println(page.getTotalPages()); } } ================================================ FILE: lab-13-spring-data-jpa/lab-13-jpa/src/test/java/cn/iocoder/springboot/lab13/jpa/repository/UserRepository03Test.java ================================================ package cn.iocoder.springboot.lab13.jpa.repository; import cn.iocoder.springboot.lab13.jpa.dataobject.UserDO; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.test.context.junit4.SpringRunner; import java.util.Calendar; import java.util.Date; @RunWith(SpringRunner.class) @SpringBootTest public class UserRepository03Test { @Autowired private UserRepository03 userRepository; @Test public void testFindByUsername() { UserDO user = userRepository.findByUsername("yunai"); System.out.println(user); } @Test public void testFindByCreateTimeAfter() { // 创建分页条件 Pageable pageable = PageRequest.of(1, 10); // 执行分页操作 Date createTime = new Date(2018 - 1990, Calendar.FEBRUARY, 24); // 临时 Demo ,实际不建议这么写 Page page = userRepository.findByCreateTimeAfter(createTime, pageable); // 打印 System.out.println(page.getTotalElements()); System.out.println(page.getTotalPages()); } } ================================================ FILE: lab-13-spring-data-jpa/lab-13-jpa/src/test/java/cn/iocoder/springboot/lab13/jpa/repository/UserRepository04Test.java ================================================ package cn.iocoder.springboot.lab13.jpa.repository; import cn.iocoder.springboot.lab13.jpa.dataobject.UserDO; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import javax.transaction.Transactional; @RunWith(SpringRunner.class) @SpringBootTest public class UserRepository04Test { @Autowired private UserRepository04 userRepository; @Test public void testFindIdByUsername01() { UserDO user = userRepository.findByUsername01("yunai"); System.out.println(user); } @Test public void testFindIdByUsername02() { UserDO user = userRepository.findByUsername02("yunai"); System.out.println(user); } @Test public void testFindIdByUsername03() { UserDO user = userRepository.findByUsername03("yunai"); System.out.println(user); } @Test // 更新操作,需要在事务中。 // 在单元测试中,事务默认回滚,所以胖友可能怎么测试,事务都不更新。 @Transactional public void testUpdateUsernameById() { userRepository.updateUsernameById(5, "yudaoyuanma"); } } ================================================ FILE: lab-13-spring-data-jpa/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-13-spring-data-jpa pom lab-13-jpa ================================================ FILE: lab-13-spring-data-jpa/《芋道 Spring Boot JPA 入门》.md ================================================ ================================================ FILE: lab-14-spring-jdbc-template/lab-14-jdbctemplate/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE 4.0.0 lab-14-jdbctemplate org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 5.1.48 org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-14-spring-jdbc-template/lab-14-jdbctemplate/src/main/java/cn/iocoder/springboot/lab14/jdbctemplate/Application.java ================================================ package cn.iocoder.springboot.lab14.jdbctemplate; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { } ================================================ FILE: lab-14-spring-jdbc-template/lab-14-jdbctemplate/src/main/java/cn/iocoder/springboot/lab14/jdbctemplate/dao/UserDao.java ================================================ package cn.iocoder.springboot.lab14.jdbctemplate.dao; import cn.iocoder.springboot.lab14.jdbctemplate.dataobject.UserDO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.PreparedStatementCreatorFactory; import org.springframework.jdbc.core.SqlParameter; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.jdbc.core.simple.SimpleJdbcInsert; import org.springframework.jdbc.support.GeneratedKeyHolder; import org.springframework.jdbc.support.KeyHolder; import org.springframework.stereotype.Repository; import java.sql.Types; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @Repository public class UserDao { /** * 声明 INSERT 操作的 PreparedStatementCreatorFactory 对象 */ private static final PreparedStatementCreatorFactory INSERT_PREPARED_STATEMENT_CREATOR_FACTORY = new PreparedStatementCreatorFactory("INSERT INTO users(username, password, create_time) VALUES(?, ?, ?)"); static { // 设置返回主键 INSERT_PREPARED_STATEMENT_CREATOR_FACTORY.setReturnGeneratedKeys(true); INSERT_PREPARED_STATEMENT_CREATOR_FACTORY.setGeneratedKeysColumnNames("id"); // 设置每个占位符的类型 INSERT_PREPARED_STATEMENT_CREATOR_FACTORY.addParameter(new SqlParameter(Types.VARCHAR)); INSERT_PREPARED_STATEMENT_CREATOR_FACTORY.addParameter(new SqlParameter(Types.VARCHAR)); INSERT_PREPARED_STATEMENT_CREATOR_FACTORY.addParameter(new SqlParameter(Types.TIMESTAMP)); } @Autowired private JdbcTemplate template; /** * 使用 PreparedStatementCreator 实现插入数据 * * @param entity 实体 * @return 影响行数 */ public int insert(UserDO entity) { // 创建 KeyHolder 对象,设置返回的主键 ID KeyHolder keyHolder = new GeneratedKeyHolder(); // 执行插入操作 int updateCounts = template.update(INSERT_PREPARED_STATEMENT_CREATOR_FACTORY.newPreparedStatementCreator( Arrays.asList(entity.getUsername(), entity.getPassword(), entity.getCreateTime()) ), keyHolder); // 设置 ID 主键到 entity 实体中 if (keyHolder.getKey() != null) { entity.setId(keyHolder.getKey().intValue()); } // 返回影响行数 return updateCounts; } /** * 使用 SimpleJdbcInsert 实现插入数据 * * @param entity 实体 * @return 影响行数 */ public int insert0(UserDO entity) { // 创建 SimpleJdbcInsert 对象 SimpleJdbcInsert insertOp = new SimpleJdbcInsert(template); insertOp.setTableName("users"); insertOp.setColumnNames(Arrays.asList("username", "password", "create_time")); insertOp.setGeneratedKeyName("id"); // 拼接参数 Map params = new HashMap<>(); params.put("username", entity.getUsername()); params.put("password", entity.getPassword()); params.put("create_time", entity.getCreateTime()); // 执行插入操作 Number id = insertOp.executeAndReturnKey(params); // 设置 ID 主键到 entity 实体中 entity.setId(id.intValue()); // 返回影响行数 return 1; } public int updateById(UserDO entity) { // JdbcTemplate 生成更新的动态 SQL 不是很方便,需要自己二次封装。类似 SimpleJdbcInsert 对象 return template.update("UPDATE users SET password = ? WHERE id = ?", entity.getPassword(), entity.getId()); } public int deleteById(Integer id) { return template.update("DELETE FROM users WHERE id = ?", id); } public UserDO selectById(Integer id) { return template.queryForObject("SELECT id, username, password, create_time FROM users WHERE id = ?", new BeanPropertyRowMapper<>(UserDO.class), // 结果转换成对应的对象 id); } public UserDO selectByUsername(String username) { return template.queryForObject("SELECT id, username, password, create_time FROM users WHERE username = ? LIMIT 1", new BeanPropertyRowMapper<>(UserDO.class), // 结果转换成对应的对象 username); } public List selectByIds(List ids) { // 创建 NamedParameterJdbcTemplate 对象 NamedParameterJdbcTemplate namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(template); // 拼接参数 Map params = new HashMap<>(); params.put("ids", ids); // 执行查询 return namedParameterJdbcTemplate.query( "SELECT id, username, password, create_time FROM users WHERE id IN (:ids)", // 使用 :ids 作为占位服务 params, new BeanPropertyRowMapper<>(UserDO.class) // 结果转换成对应的对象 ); } } ================================================ FILE: lab-14-spring-jdbc-template/lab-14-jdbctemplate/src/main/java/cn/iocoder/springboot/lab14/jdbctemplate/dataobject/UserDO.java ================================================ package cn.iocoder.springboot.lab14.jdbctemplate.dataobject; import java.util.Date; /** * 用户 DO */ public class UserDO { /** * 用户编号 */ private Integer id; /** * 账号 */ private String username; /** * 密码(明文) * * ps:生产环境下,千万不要明文噢 */ private String password; /** * 创建时间 */ private Date createTime; public Integer getId() { return id; } public UserDO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserDO setUsername(String username) { this.username = username; return this; } public String getPassword() { return password; } public UserDO setPassword(String password) { this.password = password; return this; } public Date getCreateTime() { return createTime; } public UserDO setCreateTime(Date createTime) { this.createTime = createTime; return this; } @Override public String toString() { return "UserDO{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", createTime=" + createTime + '}'; } } ================================================ FILE: lab-14-spring-jdbc-template/lab-14-jdbctemplate/src/main/resources/application.yaml ================================================ spring: # datasource 数据源配置内容 datasource: url: jdbc:mysql://47.112.193.81:3306/testb5f4?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: testb5f4 password: F4df4db0ed86@11 ================================================ FILE: lab-14-spring-jdbc-template/lab-14-jdbctemplate/src/test/java/cn/iocoder/springboot/lab14/jdbctemplate/dao/UserDaoTest.java ================================================ package cn.iocoder.springboot.lab14.jdbctemplate.dao; import cn.iocoder.springboot.lab14.jdbctemplate.Application; import cn.iocoder.springboot.lab14.jdbctemplate.dataobject.UserDO; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.UUID; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class UserDaoTest { @Autowired private UserDao userDao; @Test public void testInsert() { UserDO user = new UserDO().setUsername(UUID.randomUUID().toString()) .setPassword("nicai").setCreateTime(new Date()); userDao.insert(user); System.out.println(user); } @Test public void testInsert0() { UserDO user = new UserDO().setUsername(UUID.randomUUID().toString()) .setPassword("nicai").setCreateTime(new Date()); userDao.insert0(user); System.out.println(user); } @Test public void testUpdateById() { UserDO updateUser = new UserDO().setId(1) .setPassword("wobucai"); userDao.updateById(updateUser); } @Test public void testDeleteById() { userDao.deleteById(2); } @Test public void testSelectById() { UserDO user = userDao.selectById(1); System.out.println(user); } @Test public void testSelectByUsername() { UserDO user = userDao.selectByUsername("yunai"); System.out.println(user); } @Test public void testSelectByIds() { List users = userDao.selectByIds(Arrays.asList(1, 5)); System.out.println("users:" + users.size()); } } ================================================ FILE: lab-14-spring-jdbc-template/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-14-spring-jdbc-template pom lab-14-jdbctemplate ================================================ FILE: lab-14-spring-jdbc-template/《芋道 Spring Boot JdbcTemplate 入门》.md ================================================ ================================================ FILE: lab-15-spring-data-es/lab-15-spring-data-elasticsearch/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE 4.0.0 lab-15-spring-data-elasticsearch org.springframework.boot spring-boot-starter-data-elasticsearch org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-15-spring-data-es/lab-15-spring-data-elasticsearch/src/main/java/cn/iocoder/springboot/lab15/springdataelasticsearch/Application.java ================================================ package cn.iocoder.springboot.lab15.springdataelasticsearch; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { } ================================================ FILE: lab-15-spring-data-es/lab-15-spring-data-elasticsearch/src/main/java/cn/iocoder/springboot/lab15/springdataelasticsearch/bo/ProductConditionBO.java ================================================ package cn.iocoder.springboot.lab15.springdataelasticsearch.bo; import java.util.List; /** * 商品搜索条件返回 BO */ public class ProductConditionBO { /** * 商品分类数组 */ private List categories; public static class Category { /** * 分类编号 */ private Integer id; /** * 分类名称 */ private String name; public Integer getId() { return id; } public Category setId(Integer id) { this.id = id; return this; } public String getName() { return name; } public Category setName(String name) { this.name = name; return this; } } public List getCategories() { return categories; } public ProductConditionBO setCategories(List categories) { this.categories = categories; return this; } } ================================================ FILE: lab-15-spring-data-es/lab-15-spring-data-elasticsearch/src/main/java/cn/iocoder/springboot/lab15/springdataelasticsearch/constant/FieldAnalyzer.java ================================================ package cn.iocoder.springboot.lab15.springdataelasticsearch.constant; /** * ES 字段分析器的枚举类 * * 关于 IK 分词,文章 https://blog.csdn.net/xsdxs/article/details/72853288 不错。 * 目前项目使用的 ES 版本是 6.7.1 ,可以在 https://www.elastic.co/cn/downloads/past-releases/elasticsearch-6-7-1 下载。 * 如果不知道怎么安装 ES ,可以看 https://blog.csdn.net/chengyuqiang/article/details/78837712 简单。 */ public class FieldAnalyzer { /** * IK 最大化分词 * * 会将文本做最细粒度的拆分 */ public static final String IK_MAX_WORD = "ik_max_word"; /** * IK 智能分词 * * 会做最粗粒度的拆分 */ public static final String IK_SMART = "ik_smart"; } ================================================ FILE: lab-15-spring-data-es/lab-15-spring-data-elasticsearch/src/main/java/cn/iocoder/springboot/lab15/springdataelasticsearch/dataobject/ESProductDO.java ================================================ package cn.iocoder.springboot.lab15.springdataelasticsearch.dataobject; import cn.iocoder.springboot.lab15.springdataelasticsearch.constant.FieldAnalyzer; import org.springframework.data.annotation.Id; import org.springframework.data.elasticsearch.annotations.Document; import org.springframework.data.elasticsearch.annotations.Field; import org.springframework.data.elasticsearch.annotations.FieldType; @Document(indexName = "product", // 索引名 type = "product", // 类型。未来的版本即将废弃 shards = 1, // 默认索引分区数 replicas = 0, // 每个分区的备份数 refreshInterval = "-1" // 刷新间隔 ) public class ESProductDO { /** * ID 主键 */ @Id private Integer id; /** * SPU 名字 */ @Field(analyzer = FieldAnalyzer.IK_MAX_WORD, type = FieldType.Text) private String name; /** * 卖点 */ @Field(analyzer = FieldAnalyzer.IK_MAX_WORD, type = FieldType.Text) private String sellPoint; /** * 描述 */ @Field(analyzer = FieldAnalyzer.IK_MAX_WORD, type = FieldType.Text) private String description; /** * 分类编号 */ private Integer cid; /** * 分类名 */ @Field(analyzer = FieldAnalyzer.IK_MAX_WORD, type = FieldType.Text) private String categoryName; public Integer getId() { return id; } public ESProductDO setId(Integer id) { this.id = id; return this; } public String getName() { return name; } public ESProductDO setName(String name) { this.name = name; return this; } public String getSellPoint() { return sellPoint; } public ESProductDO setSellPoint(String sellPoint) { this.sellPoint = sellPoint; return this; } public String getDescription() { return description; } public ESProductDO setDescription(String description) { this.description = description; return this; } public Integer getCid() { return cid; } public ESProductDO setCid(Integer cid) { this.cid = cid; return this; } public String getCategoryName() { return categoryName; } public ESProductDO setCategoryName(String categoryName) { this.categoryName = categoryName; return this; } @Override public String toString() { return "ProductDO{" + "id=" + id + ", name='" + name + '\'' + ", sellPoint='" + sellPoint + '\'' + ", description='" + description + '\'' + ", cid=" + cid + ", categoryName='" + categoryName + '\'' + '}'; } } ================================================ FILE: lab-15-spring-data-es/lab-15-spring-data-elasticsearch/src/main/java/cn/iocoder/springboot/lab15/springdataelasticsearch/repository/ProductRepository.java ================================================ package cn.iocoder.springboot.lab15.springdataelasticsearch.repository; import cn.iocoder.springboot.lab15.springdataelasticsearch.dataobject.ESProductDO; import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; public interface ProductRepository extends ElasticsearchRepository { } ================================================ FILE: lab-15-spring-data-es/lab-15-spring-data-elasticsearch/src/main/resources/application.yaml ================================================ spring: data: # Elasticsearch 配置项 elasticsearch: cluster-name: elasticsearch # 集群名 cluster-nodes: 127.0.0.1:9300 # 集群节点 ================================================ FILE: lab-15-spring-data-es/lab-15-spring-data-elasticsearch/src/test/java/cn/iocoder/springboot/lab15/springdataelasticsearch/repository/ProductRepository04Test.java ================================================ package cn.iocoder.springboot.lab15.springdataelasticsearch.repository; import cn.iocoder.springboot.lab15.springdataelasticsearch.Application; import cn.iocoder.springboot.lab15.springdataelasticsearch.bo.ProductConditionBO; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.aggregations.Aggregation; import org.elasticsearch.search.aggregations.AggregationBuilders; import org.elasticsearch.search.aggregations.bucket.terms.LongTerms; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.elasticsearch.core.ElasticsearchTemplate; import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder; import org.springframework.test.context.junit4.SpringRunner; import java.util.ArrayList; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class ProductRepository04Test { @Autowired private ElasticsearchTemplate elasticsearchTemplate; @Test public void test() { // 创建 ES 搜索条件 NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder() .withIndices("product"); // 筛选 nativeSearchQueryBuilder.withQuery(QueryBuilders.multiMatchQuery("芋道", "name", "sellPoint", "categoryName")); // 聚合 nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("cids").field("cid")); // 商品分类 // 执行查询 ProductConditionBO condition = elasticsearchTemplate.query(nativeSearchQueryBuilder.build(), response -> { ProductConditionBO result = new ProductConditionBO(); // categoryIds 聚合 Aggregation categoryIdsAggregation = response.getAggregations().get("cids"); if (categoryIdsAggregation != null) { result.setCategories(new ArrayList<>()); for (LongTerms.Bucket bucket : (((LongTerms) categoryIdsAggregation).getBuckets())) { result.getCategories().add(new ProductConditionBO.Category().setId(bucket.getKeyAsNumber().intValue())); } } // 返回结果 return result; }); // 后续遍历 condition.categories 数组,查询商品分类,设置商品分类名。 System.out.println(); } } ================================================ FILE: lab-15-spring-data-es/lab-15-spring-data-elasticsearch/src/test/java/cn/iocoder/springboot/lab15/springdataelasticsearch/repository/ProductRepositoryTest.java ================================================ package cn.iocoder.springboot.lab15.springdataelasticsearch.repository; import cn.iocoder.springboot.lab15.springdataelasticsearch.Application; import cn.iocoder.springboot.lab15.springdataelasticsearch.dataobject.ESProductDO; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.Arrays; import java.util.Optional; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class ProductRepositoryTest { @Autowired private ProductRepository productRepository; @Test // 插入一条记录 public void testInsert() { ESProductDO product = new ESProductDO(); product.setId(1); // 一般 ES 的 ID 编号,使用 DB 数据对应的编号。这里,先写死 product.setName("芋道源码"); product.setSellPoint("愿半生编码,如一生老友"); product.setDescription("我只是一个描述"); product.setCid(2); product.setCategoryName("技术"); productRepository.save(product); } // 这里要注意,如果使用 save 方法来更新的话,必须是全量字段,否则其它字段会被覆盖。 // 所以,这里仅仅是作为一个示例。 @Test // 更新一条记录 public void testUpdate() { ESProductDO product = new ESProductDO(); product.setId(1); product.setCid(2); product.setCategoryName("技术-Java"); productRepository.save(product); } @Test // 根据 ID 编号,删除一条记录 public void testDelete() { productRepository.deleteById(1); } @Test // 根据 ID 编号,查询一条记录 public void testSelectById() { Optional userDO = productRepository.findById(1); System.out.println(userDO.isPresent()); } @Test // 根据 ID 编号数组,查询多条记录 public void testSelectByIds() { Iterable users = productRepository.findAllById(Arrays.asList(1, 4)); users.forEach(System.out::println); } } ================================================ FILE: lab-15-spring-data-es/lab-15-spring-data-jest/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE 4.0.0 lab-15-spring-data-jest com.github.vanroy spring-boot-starter-data-jest 3.2.5.RELEASE org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-15-spring-data-es/lab-15-spring-data-jest/src/main/java/cn/iocoder/springboot/lab15/springdatajest/Application.java ================================================ package cn.iocoder.springboot.lab15.springdatajest; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration; import org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration; @SpringBootApplication(exclude = {ElasticsearchAutoConfiguration.class, ElasticsearchDataAutoConfiguration.class}) public class Application { } ================================================ FILE: lab-15-spring-data-es/lab-15-spring-data-jest/src/main/java/cn/iocoder/springboot/lab15/springdatajest/constant/FieldAnalyzer.java ================================================ package cn.iocoder.springboot.lab15.springdatajest.constant; /** * ES 字段分析器的枚举类 * * 关于 IK 分词,文章 https://blog.csdn.net/xsdxs/article/details/72853288 不错。 * 目前项目使用的 ES 版本是 6.7.1 ,可以在 https://www.elastic.co/cn/downloads/past-releases/elasticsearch-6-7-1 下载。 * 如果不知道怎么安装 ES ,可以看 https://blog.csdn.net/chengyuqiang/article/details/78837712 简单。 */ public class FieldAnalyzer { /** * IK 最大化分词 * * 会将文本做最细粒度的拆分 */ public static final String IK_MAX_WORD = "ik_max_word"; /** * IK 智能分词 * * 会做最粗粒度的拆分 */ public static final String IK_SMART = "ik_smart"; } ================================================ FILE: lab-15-spring-data-es/lab-15-spring-data-jest/src/main/java/cn/iocoder/springboot/lab15/springdatajest/dataobject/ESProductDO.java ================================================ package cn.iocoder.springboot.lab15.springdatajest.dataobject; import cn.iocoder.springboot.lab15.springdatajest.constant.FieldAnalyzer; import org.springframework.data.annotation.Id; import org.springframework.data.elasticsearch.annotations.Document; import org.springframework.data.elasticsearch.annotations.Field; import org.springframework.data.elasticsearch.annotations.FieldType; @Document(indexName = "product", // 索引名 type = "product", // 类型。未来的版本即将废弃 shards = 1, // 默认索引分区数 replicas = 0, // 每个分区的备份数 refreshInterval = "-1" // 刷新间隔 ) public class ESProductDO { /** * ID 主键 */ @Id private Integer id; /** * SPU 名字 */ @Field(analyzer = FieldAnalyzer.IK_MAX_WORD, type = FieldType.Text) private String name; /** * 卖点 */ @Field(analyzer = FieldAnalyzer.IK_MAX_WORD, type = FieldType.Text) private String sellPoint; /** * 描述 */ @Field(analyzer = FieldAnalyzer.IK_MAX_WORD, type = FieldType.Text) private String description; /** * 分类编号 */ private Integer cid; /** * 分类名 */ @Field(analyzer = FieldAnalyzer.IK_MAX_WORD, type = FieldType.Text) private String categoryName; public Integer getId() { return id; } public ESProductDO setId(Integer id) { this.id = id; return this; } public String getName() { return name; } public ESProductDO setName(String name) { this.name = name; return this; } public String getSellPoint() { return sellPoint; } public ESProductDO setSellPoint(String sellPoint) { this.sellPoint = sellPoint; return this; } public String getDescription() { return description; } public ESProductDO setDescription(String description) { this.description = description; return this; } public Integer getCid() { return cid; } public ESProductDO setCid(Integer cid) { this.cid = cid; return this; } public String getCategoryName() { return categoryName; } public ESProductDO setCategoryName(String categoryName) { this.categoryName = categoryName; return this; } @Override public String toString() { return "ProductDO{" + "id=" + id + ", name='" + name + '\'' + ", sellPoint='" + sellPoint + '\'' + ", description='" + description + '\'' + ", cid=" + cid + ", categoryName='" + categoryName + '\'' + '}'; } } ================================================ FILE: lab-15-spring-data-es/lab-15-spring-data-jest/src/main/java/cn/iocoder/springboot/lab15/springdatajest/repository/ProductRepository.java ================================================ package cn.iocoder.springboot.lab15.springdatajest.repository; import cn.iocoder.springboot.lab15.springdatajest.dataobject.ESProductDO; import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; public interface ProductRepository extends ElasticsearchRepository { } ================================================ FILE: lab-15-spring-data-es/lab-15-spring-data-jest/src/main/java/cn/iocoder/springboot/lab15/springdatajest/repository/ProductRepository02.java ================================================ package cn.iocoder.springboot.lab15.springdatajest.repository; import cn.iocoder.springboot.lab15.springdatajest.dataobject.ESProductDO; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; public interface ProductRepository02 extends ElasticsearchRepository { ESProductDO findByName(String name); Page findByNameLike(String name, Pageable pageable); } ================================================ FILE: lab-15-spring-data-es/lab-15-spring-data-jest/src/main/java/cn/iocoder/springboot/lab15/springdatajest/repository/ProductRepository03.java ================================================ package cn.iocoder.springboot.lab15.springdatajest.repository; import cn.iocoder.springboot.lab15.springdatajest.dataobject.ESProductDO; import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder; import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders; import org.elasticsearch.search.sort.SortBuilders; import org.elasticsearch.search.sort.SortOrder; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder; import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; import org.springframework.util.StringUtils; import static org.elasticsearch.index.query.QueryBuilders.matchQuery; public interface ProductRepository03 extends ElasticsearchRepository { default Page search(Integer cid, String keyword, Pageable pageable) { NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder(); // 筛选条件 cid if (cid != null) { nativeSearchQueryBuilder.withFilter(QueryBuilders.termQuery("cid", cid)); } // 筛选 if (StringUtils.hasText(keyword)) { FunctionScoreQueryBuilder.FilterFunctionBuilder[] functions = { // TODO 芋艿,分值随便打的 new FunctionScoreQueryBuilder.FilterFunctionBuilder(matchQuery("name", keyword), ScoreFunctionBuilders.weightFactorFunction(10)), new FunctionScoreQueryBuilder.FilterFunctionBuilder(matchQuery("sellPoint", keyword), ScoreFunctionBuilders.weightFactorFunction(2)), new FunctionScoreQueryBuilder.FilterFunctionBuilder(matchQuery("categoryName", keyword), ScoreFunctionBuilders.weightFactorFunction(3)), // new FunctionScoreQueryBuilder.FilterFunctionBuilder(matchQuery("description", keyword), // ScoreFunctionBuilders.weightFactorFunction(2)), // TODO 芋艿,目前这么做,如果商品描述很长,在按照价格降序,会命中超级多的关键字。 }; FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(functions) .scoreMode(FunctionScoreQuery.ScoreMode.SUM) .setMinScore(2F); // TODO 芋艿,需要考虑下 score nativeSearchQueryBuilder.withQuery(functionScoreQueryBuilder); } // 排序 if (StringUtils.hasText(keyword)) { // 关键字,使用打分 nativeSearchQueryBuilder.withSort(SortBuilders.scoreSort().order(SortOrder.DESC)); } else if (pageable.getSort().isSorted()) { // 有排序,则进行拼接 pageable.getSort().get().forEach(sortField -> nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort(sortField.getProperty()) .order(sortField.getDirection().isAscending() ? SortOrder.ASC : SortOrder.DESC))); } else { // 无排序,则按照 ID 倒序 nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort("id").order(SortOrder.DESC)); } // 分页 nativeSearchQueryBuilder.withPageable(PageRequest.of(pageable.getPageNumber(), pageable.getPageSize())); // 避免 // 执行查询 return search(nativeSearchQueryBuilder.build()); } } ================================================ FILE: lab-15-spring-data-es/lab-15-spring-data-jest/src/main/resources/application.yaml ================================================ spring: data: # Jest 配置项 jest: uri: http://127.0.0.1:9200 ================================================ FILE: lab-15-spring-data-es/lab-15-spring-data-jest/src/test/java/cn/iocoder/springboot/lab15/springdatajest/repository/ProductRepository02Test.java ================================================ package cn.iocoder.springboot.lab15.springdatajest.repository; import cn.iocoder.springboot.lab15.springdatajest.Application; import cn.iocoder.springboot.lab15.springdatajest.dataobject.ESProductDO; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class ProductRepository02Test { @Autowired private ProductRepository02 productRepository; @Test // 根据名字获得一条记录 public void testFindByName() { ESProductDO product = productRepository.findByName("芋道源码"); System.out.println(product); } @Test // 使用 name 模糊查询,分页返回结果 public void testFindByNameLike() { // 根据情况,是否要制造测试数据 if (true) { testInsert(); } // 创建排序条件 Sort sort = new Sort(Sort.Direction.DESC, "id"); // ID 倒序 // 创建分页条件。 Pageable pageable = PageRequest.of(0, 10, sort); // 执行分页操作 Page page = productRepository.findByNameLike("芋道", pageable); // 打印 System.out.println(page.getTotalElements()); System.out.println(page.getTotalPages()); } /** * 为了给分页制造一点数据 */ private void testInsert() { for (int i = 1; i <= 100; i++) { ESProductDO product = new ESProductDO(); product.setId(i); // 一般 ES 的 ID 编号,使用 DB 数据对应的编号。这里,先写死 product.setName("芋道源码:" + i); product.setSellPoint("愿半生编码,如一生老友"); product.setDescription("我只是一个描述"); product.setCid(1); product.setCategoryName("技术"); productRepository.save(product); } } } ================================================ FILE: lab-15-spring-data-es/lab-15-spring-data-jest/src/test/java/cn/iocoder/springboot/lab15/springdatajest/repository/ProductRepository03Test.java ================================================ package cn.iocoder.springboot.lab15.springdatajest.repository; import cn.iocoder.springboot.lab15.springdatajest.Application; import cn.iocoder.springboot.lab15.springdatajest.dataobject.ESProductDO; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class ProductRepository03Test { @Autowired private ProductRepository03 productRepository; @Test public void testSearch() { // 查找分类为 1 + 指定关键字,并且按照 id 升序 Page page = productRepository.search(1, "技术", PageRequest.of(0, 5, Sort.Direction.ASC, "id")); System.out.println(page.getTotalPages()); // 查找分类为 1 ,并且按照 id 升序 page = productRepository.search(1, null, PageRequest.of(0, 5, Sort.Direction.ASC, "id")); System.out.println(page.getTotalPages()); } } ================================================ FILE: lab-15-spring-data-es/lab-15-spring-data-jest/src/test/java/cn/iocoder/springboot/lab15/springdatajest/repository/ProductRepositoryTest.java ================================================ package cn.iocoder.springboot.lab15.springdatajest.repository; import cn.iocoder.springboot.lab15.springdatajest.Application; import cn.iocoder.springboot.lab15.springdatajest.dataobject.ESProductDO; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.Arrays; import java.util.Optional; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class ProductRepositoryTest { @Autowired private ProductRepository productRepository; @Test // 插入一条记录 public void testInsert() { ESProductDO product = new ESProductDO(); product.setId(1); // 一般 ES 的 ID 编号,使用 DB 数据对应的编号。这里,先写死 product.setName("芋道源码"); product.setSellPoint("愿半生编码,如一生老友"); product.setDescription("我只是一个描述"); product.setCid(1); product.setCategoryName("技术"); productRepository.save(product); } // 这里要注意,如果使用 save 方法来更新的话,必须是全量字段,否则其它字段会被覆盖。 // 所以,这里仅仅是作为一个示例。 @Test // 更新一条记录 public void testUpdate() { ESProductDO product = new ESProductDO(); product.setId(1); product.setCid(2); product.setCategoryName("技术-Java"); productRepository.save(product); } @Test // 根据 ID 编号,删除一条记录 public void testDelete() { productRepository.deleteById(1); } @Test // 根据 ID 编号,查询一条记录 public void testSelectById() { Optional userDO = productRepository.findById(1); System.out.println(userDO.isPresent()); } @Test // 根据 ID 编号数组,查询多条记录 public void testSelectByIds() { Iterable users = productRepository.findAllById(Arrays.asList(1, 4)); users.forEach(System.out::println); } } ================================================ FILE: lab-15-spring-data-es/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-15-spring-data-es pom lab-15-spring-data-jest lab-15-spring-data-elasticsearch ================================================ FILE: lab-15-spring-data-es/《芋道 Spring Boot Elasticsearch 入门》.md ================================================ ================================================ FILE: lab-16-spring-data-mongo/lab-16-spring-data-mongodb/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE 4.0.0 lab-16-spring-data-mongodb org.springframework.boot spring-boot-starter-data-mongodb org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-16-spring-data-mongo/lab-16-spring-data-mongodb/src/main/java/cn/iocoder/springboot/lab16/springdatamongodb/Application.java ================================================ package cn.iocoder.springboot.lab16.springdatamongodb; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { } ================================================ FILE: lab-16-spring-data-mongo/lab-16-spring-data-mongodb/src/main/java/cn/iocoder/springboot/lab16/springdatamongodb/config/MongoDBConfig.java ================================================ package cn.iocoder.springboot.lab16.springdatamongodb.config; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.convert.CustomConversions; import org.springframework.data.mongodb.MongoDbFactory; import org.springframework.data.mongodb.core.convert.DbRefResolver; import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver; import org.springframework.data.mongodb.core.convert.DefaultMongoTypeMapper; import org.springframework.data.mongodb.core.convert.MappingMongoConverter; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; @Configuration public class MongoDBConfig { @Bean // 目的,就是为了移除 _class field 。参考博客 https://blog.csdn.net/bigtree_3721/article/details/82787411 public MappingMongoConverter mappingMongoConverter(MongoDbFactory factory, MongoMappingContext context, BeanFactory beanFactory) { // 创建 DbRefResolver 对象 DbRefResolver dbRefResolver = new DefaultDbRefResolver(factory); // 创建 MappingMongoConverter 对象 MappingMongoConverter mappingConverter = new MappingMongoConverter(dbRefResolver, context); // 设置 conversions 属性 try { mappingConverter.setCustomConversions(beanFactory.getBean(CustomConversions.class)); } catch (NoSuchBeanDefinitionException ignore) { } // 设置 typeMapper 属性,从而移除 _class field 。 mappingConverter.setTypeMapper(new DefaultMongoTypeMapper(null)); return mappingConverter; } } ================================================ FILE: lab-16-spring-data-mongo/lab-16-spring-data-mongodb/src/main/java/cn/iocoder/springboot/lab16/springdatamongodb/dao/UserDao.java ================================================ package cn.iocoder.springboot.lab16.springdatamongodb.dao; import cn.iocoder.springboot.lab16.springdatamongodb.dataobject.UserDO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.annotation.Transient; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.Update; import org.springframework.stereotype.Repository; import org.springframework.util.ReflectionUtils; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.List; @Repository public class UserDao { @Autowired private MongoTemplate mongoTemplate; public void insert(UserDO entity) { mongoTemplate.insert(entity); } public void updateById(UserDO entity) { // 生成 Update 条件 final Update update = new Update(); // 反射遍历 entity 对象,将非空字段设置到 Update 中 ReflectionUtils.doWithFields(entity.getClass(), new ReflectionUtils.FieldCallback() { @Override public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException { // 排除指定条件 if ("id".equals(field.getName()) // 排除 id 字段,因为作为查询主键 || field.getAnnotation(Transient.class) != null // 排除 @Transient 注解的字段,因为非存储字段 || Modifier.isStatic(field.getModifiers())) { // 排除静态字段 return; } // 设置字段可反射 if (!field.isAccessible()) { field.setAccessible(true); } // 排除字段为空的情况 if (field.get(entity) == null) { return; } // 设置更新条件 update.set(field.getName(), field.get(entity)); } }); // 防御,避免有业务传递空的 Update 对象 if (update.getUpdateObject().isEmpty()) { return; } // 执行更新 mongoTemplate.updateFirst(new Query(Criteria.where("_id").is(entity.getId())), update, UserDO.class); } public void deleteById(Integer id) { mongoTemplate.remove(new Query(Criteria.where("_id").is(id)), UserDO.class); } public UserDO findById(Integer id) { return mongoTemplate.findOne(new Query(Criteria.where("_id").is(id)), UserDO.class); } public UserDO findByUsername(String username) { return mongoTemplate.findOne(new Query(Criteria.where("username").is(username)), UserDO.class); } public List findAllById(List ids) { return mongoTemplate.find(new Query(Criteria.where("_id").in(ids)), UserDO.class); } } ================================================ FILE: lab-16-spring-data-mongo/lab-16-spring-data-mongodb/src/main/java/cn/iocoder/springboot/lab16/springdatamongodb/dataobject/ProductDO.java ================================================ package cn.iocoder.springboot.lab16.springdatamongodb.dataobject; import cn.iocoder.springboot.lab16.springdatamongodb.mongo.IncIdEntity; import org.springframework.data.mongodb.core.mapping.Document; /** * 商品 DO */ @Document(collection = "Product") public class ProductDO extends IncIdEntity { /** * 商品名 */ private String name; public String getName() { return name; } public ProductDO setName(String name) { this.name = name; return this; } } ================================================ FILE: lab-16-spring-data-mongo/lab-16-spring-data-mongodb/src/main/java/cn/iocoder/springboot/lab16/springdatamongodb/dataobject/UserDO.java ================================================ package cn.iocoder.springboot.lab16.springdatamongodb.dataobject; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.mapping.Document; import java.util.Date; /** * 用户 DO */ @Document(collection = "User") public class UserDO { /** * 用户信息 */ public static class Profile { /** * 昵称 */ private String nickname; /** * 性别 */ private Integer gender; public String getNickname() { return nickname; } public Profile setNickname(String nickname) { this.nickname = nickname; return this; } public Integer getGender() { return gender; } public Profile setGender(Integer gender) { this.gender = gender; return this; } @Override public String toString() { return "Profile{" + "nickname='" + nickname + '\'' + ", gender=" + gender + '}'; } } @Id private Integer id; /** * 账号 */ private String username; /** * 密码 */ private String password; /** * 创建时间 */ private Date createTime; /** * 用户信息 */ private Profile profile; public Integer getId() { return id; } public UserDO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserDO setUsername(String username) { this.username = username; return this; } public String getPassword() { return password; } public UserDO setPassword(String password) { this.password = password; return this; } public Date getCreateTime() { return createTime; } public UserDO setCreateTime(Date createTime) { this.createTime = createTime; return this; } public Profile getProfile() { return profile; } public UserDO setProfile(Profile profile) { this.profile = profile; return this; } @Override public String toString() { return "UserDO{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", createTime=" + createTime + ", profile=" + profile + '}'; } } ================================================ FILE: lab-16-spring-data-mongo/lab-16-spring-data-mongodb/src/main/java/cn/iocoder/springboot/lab16/springdatamongodb/mongo/IncIdEntity.java ================================================ package cn.iocoder.springboot.lab16.springdatamongodb.mongo; import org.springframework.data.annotation.Id; /** * 自增主键实体 * * @param 主键泛型 */ public abstract class IncIdEntity { @Id private T id; public T getId() { return id; } public void setId(T id) { this.id = id; } } ================================================ FILE: lab-16-spring-data-mongo/lab-16-spring-data-mongodb/src/main/java/cn/iocoder/springboot/lab16/springdatamongodb/mongo/MongoInsertEventListener.java ================================================ package cn.iocoder.springboot.lab16.springdatamongodb.mongo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.mongodb.core.FindAndModifyOptions; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener; import org.springframework.data.mongodb.core.mapping.event.BeforeConvertEvent; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.Update; import org.springframework.stereotype.Component; import java.util.HashMap; @Component public class MongoInsertEventListener extends AbstractMongoEventListener { /** * sequence - 集合名 */ private static final String SEQUENCE_COLLECTION_NAME = "sequence"; /** * sequence - 自增值的字段名 */ private static final String SEQUENCE_FIELD_VALUE = "value"; @Autowired private MongoTemplate mongoTemplate; @Override public void onBeforeConvert(BeforeConvertEvent event) { IncIdEntity entity = event.getSource(); // 判断 id 为空 if (entity.getId() == null) { // 获得下一个编号 Number id = this.getNextId(entity); // 设置到实体中 // noinspection unchecked entity.setId(id); } } /** * 获得实体的下一个主键 ID 编号 * * @param entity 实体对象 * @return ID 编号 */ private Number getNextId(IncIdEntity entity) { // 使用实体名的简单类名,作为 ID 编号 String id = entity.getClass().getSimpleName(); // 创建 Query 对象 Query query = new Query(Criteria.where("_id").is(id)); // 创建 Update 对象 Update update = new Update(); update.inc(SEQUENCE_FIELD_VALUE, 1); // 自增值 // 创建 FindAndModifyOptions 对象 FindAndModifyOptions options = new FindAndModifyOptions(); options.upsert(true); // 如果不存在时,则进行插入 options.returnNew(true); // 返回新值 // 执行操作 @SuppressWarnings("unchecked") HashMap result = mongoTemplate.findAndModify(query, update, options, HashMap.class, SEQUENCE_COLLECTION_NAME); // 返回主键 return (Number) result.get(SEQUENCE_FIELD_VALUE); } } ================================================ FILE: lab-16-spring-data-mongo/lab-16-spring-data-mongodb/src/main/java/cn/iocoder/springboot/lab16/springdatamongodb/repository/ProductRepository.java ================================================ package cn.iocoder.springboot.lab16.springdatamongodb.repository; import cn.iocoder.springboot.lab16.springdatamongodb.dataobject.ProductDO; import org.springframework.data.mongodb.repository.MongoRepository; public interface ProductRepository extends MongoRepository { } ================================================ FILE: lab-16-spring-data-mongo/lab-16-spring-data-mongodb/src/main/java/cn/iocoder/springboot/lab16/springdatamongodb/repository/UserRepository.java ================================================ package cn.iocoder.springboot.lab16.springdatamongodb.repository; import cn.iocoder.springboot.lab16.springdatamongodb.dataobject.UserDO; import org.springframework.data.mongodb.repository.MongoRepository; public interface UserRepository extends MongoRepository { } ================================================ FILE: lab-16-spring-data-mongo/lab-16-spring-data-mongodb/src/main/java/cn/iocoder/springboot/lab16/springdatamongodb/repository/UserRepository02.java ================================================ package cn.iocoder.springboot.lab16.springdatamongodb.repository; import cn.iocoder.springboot.lab16.springdatamongodb.dataobject.UserDO; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.mongodb.repository.MongoRepository; public interface UserRepository02 extends MongoRepository { UserDO findByUsername(String username); Page findByUsernameLike(String username, Pageable pageable); } ================================================ FILE: lab-16-spring-data-mongo/lab-16-spring-data-mongodb/src/main/java/cn/iocoder/springboot/lab16/springdatamongodb/repository/UserRepository03.java ================================================ package cn.iocoder.springboot.lab16.springdatamongodb.repository; import cn.iocoder.springboot.lab16.springdatamongodb.dataobject.UserDO; import org.springframework.data.domain.Example; import org.springframework.data.domain.ExampleMatcher; import org.springframework.data.mongodb.repository.MongoRepository; public interface UserRepository03 extends MongoRepository { // 使用 username 精准匹配 default UserDO findByUsername01(String username) { // 创建 Example 对象,使用 username 查询 UserDO probe = new UserDO(); probe.setUsername(username); // 精准匹配 username 查询 Example example = Example.of(probe); // 执行查询 return findOne(example) .orElse(null); // 如果为空,则返回 null } // 使用 username 模糊匹配 default UserDO findByUsernameLike01(String username) { // 创建 Example 对象,使用 username 查询 UserDO probe = new UserDO(); probe.setUsername(username); // 这里还需要设置 ExampleMatcher matcher = ExampleMatcher.matching() .withMatcher("username", ExampleMatcher.GenericPropertyMatchers.contains()); // 模糊匹配 username 查询 Example example = Example.of(probe, matcher); // 执行查询 return findOne(example) .orElse(null); // 如果为空,则返回 null } } ================================================ FILE: lab-16-spring-data-mongo/lab-16-spring-data-mongodb/src/main/resources/application.yaml ================================================ spring: data: # MongoDB 配置项,对应 MongoProperties 类 mongodb: host: 127.0.0.1 port: 27017 database: yourdatabase username: test01 password: password01 # 上述属性,也可以只配置 uri logging: level: org: springframework: data: mongodb: core: DEBUG # 打印 mongodb 操作的具体语句。生产环境下,不建议开启。 ================================================ FILE: lab-16-spring-data-mongo/lab-16-spring-data-mongodb/src/test/java/cn/iocoder/springboot/lab16/springdatamongodb/dao/UserDaoTest.java ================================================ package cn.iocoder.springboot.lab16.springdatamongodb.dao; import cn.iocoder.springboot.lab16.springdatamongodb.Application; import cn.iocoder.springboot.lab16.springdatamongodb.dataobject.UserDO; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.Arrays; import java.util.Date; import java.util.List; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class UserDaoTest { @Autowired private UserDao userDao; @Test // 插入一条记录 public void testInsert() { // 创建 UserDO 对象 UserDO user = new UserDO(); user.setId(1); // 这里先临时写死一个 ID 编号,后面演示自增 ID 的时候,在修改这块 user.setUsername("yudaoyuanma"); user.setPassword("buzhidao"); user.setCreateTime(new Date()); // 创建 Profile 对象 UserDO.Profile profile = new UserDO.Profile(); profile.setNickname("芋道源码"); profile.setGender(1); user.setProfile(profile); // 存储到 DB userDao.insert(user); } // 这里要注意,如果使用 save 方法来更新的话,必须是全量字段,否则其它字段会被覆盖。 // 所以,这里仅仅是作为一个示例。 @Test // 更新一条记录 public void testUpdate() { // 创建 UserDO 对象 UserDO updateUser = new UserDO(); updateUser.setId(1); updateUser.setUsername("nicai"); // 执行更新 userDao.updateById(updateUser); } @Test // 根据 ID 编号,删除一条记录 public void testDelete() { userDao.deleteById(1); } @Test // 根据 ID 编号,查询一条记录 public void testSelectById() { UserDO userDO = userDao.findById(1); System.out.println(userDO); } @Test // 根据 ID 编号数组,查询多条记录 public void testSelectByIds() { List users = userDao.findAllById(Arrays.asList(1, 4)); users.forEach(System.out::println); } } ================================================ FILE: lab-16-spring-data-mongo/lab-16-spring-data-mongodb/src/test/java/cn/iocoder/springboot/lab16/springdatamongodb/repository/ProductRepositoryTest.java ================================================ package cn.iocoder.springboot.lab16.springdatamongodb.repository; import cn.iocoder.springboot.lab16.springdatamongodb.Application; import cn.iocoder.springboot.lab16.springdatamongodb.dataobject.ProductDO; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class ProductRepositoryTest { @Autowired private ProductRepository productRepository; @Test public void testInsert() { // 创建 ProductDO 对象 ProductDO product = new ProductDO(); product.setName("芋头"); // 插入 productRepository.insert(product); // 打印 ID System.out.println(product.getId()); } } ================================================ FILE: lab-16-spring-data-mongo/lab-16-spring-data-mongodb/src/test/java/cn/iocoder/springboot/lab16/springdatamongodb/repository/UserRepository02Test.java ================================================ package cn.iocoder.springboot.lab16.springdatamongodb.repository; import cn.iocoder.springboot.lab16.springdatamongodb.Application; import cn.iocoder.springboot.lab16.springdatamongodb.dataobject.UserDO; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class UserRepository02Test { @Autowired private UserRepository02 userRepository; @Test // 根据名字获得一条记录 public void testFindByName() { UserDO user = userRepository.findByUsername("yutou"); System.out.println(user); } @Test // 使用 username 模糊查询,分页返回结果 public void testFindByNameLike() { // 创建排序条件 Sort sort = new Sort(Sort.Direction.DESC, "id"); // ID 倒序 // 创建分页条件。 Pageable pageable = PageRequest.of(0, 10, sort); // 执行分页操作 Page page = userRepository.findByUsernameLike("yu", pageable); // 打印 System.out.println(page.getTotalElements()); System.out.println(page.getTotalPages()); } } ================================================ FILE: lab-16-spring-data-mongo/lab-16-spring-data-mongodb/src/test/java/cn/iocoder/springboot/lab16/springdatamongodb/repository/UserRepository03Test.java ================================================ package cn.iocoder.springboot.lab16.springdatamongodb.repository; import cn.iocoder.springboot.lab16.springdatamongodb.Application; import cn.iocoder.springboot.lab16.springdatamongodb.dataobject.UserDO; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class UserRepository03Test { @Autowired private UserRepository03 userRepository; @Test public void testFindByUsername01() { UserDO user = userRepository.findByUsername01("yutou"); System.out.println(user); } @Test public void testFindByUsernameLike01() { UserDO user = userRepository.findByUsernameLike01("yu"); System.out.println(user); } } ================================================ FILE: lab-16-spring-data-mongo/lab-16-spring-data-mongodb/src/test/java/cn/iocoder/springboot/lab16/springdatamongodb/repository/UserRepositoryTest.java ================================================ package cn.iocoder.springboot.lab16.springdatamongodb.repository; import cn.iocoder.springboot.lab16.springdatamongodb.Application; import cn.iocoder.springboot.lab16.springdatamongodb.dataobject.UserDO; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.util.Assert; import java.util.Arrays; import java.util.Date; import java.util.Optional; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class UserRepositoryTest { @Autowired private UserRepository userRepository; @Test // 插入一条记录 public void testInsert() { // 创建 UserDO 对象 UserDO user = new UserDO(); user.setId(1); // 这里先临时写死一个 ID 编号,后面演示自增 ID 的时候,在修改这块 user.setUsername("yudaoyuanma"); user.setPassword("buzhidao"); user.setCreateTime(new Date()); // 创建 Profile 对象 UserDO.Profile profile = new UserDO.Profile(); profile.setNickname("芋道源码"); profile.setGender(1); user.setProfile(profile); // 存储到 DB userRepository.insert(user); } // 这里要注意,如果使用 save 方法来更新的话,必须是全量字段,否则其它字段会被覆盖。 // 所以,这里仅仅是作为一个示例。 @Test // 更新一条记录 public void testUpdate() { // 查询用户 Optional userResult = userRepository.findById(1); Assert.isTrue(userResult.isPresent(), "用户一定要存在"); // 更新 UserDO updateUser = userResult.get(); updateUser.setUsername("yutou"); userRepository.save(updateUser); } @Test // 根据 ID 编号,删除一条记录 public void testDelete() { userRepository.deleteById(1); } @Test // 根据 ID 编号,查询一条记录 public void testSelectById() { Optional userDO = userRepository.findById(1); System.out.println(userDO.isPresent()); } @Test // 根据 ID 编号数组,查询多条记录 public void testSelectByIds() { Iterable users = userRepository.findAllById(Arrays.asList(1, 4)); users.forEach(System.out::println); } } ================================================ FILE: lab-16-spring-data-mongo/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-16-spring-data-mongo pom lab-16-spring-data-mongodb ================================================ FILE: lab-16-spring-data-mongo/《芋道 Spring Boot MongoDB 入门》.md ================================================ ================================================ FILE: lab-17/lab-17-dynamic-datasource-baomidou-01/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE 4.0.0 lab-17-dynamic-datasource-baomidou-01 org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 5.1.48 org.mybatis.spring.boot mybatis-spring-boot-starter 2.1.1 com.baomidou dynamic-datasource-spring-boot-starter 2.5.7 org.springframework.boot spring-boot-actuator org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-17/lab-17-dynamic-datasource-baomidou-01/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/Application.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.EnableAspectJAutoProxy; @SpringBootApplication @MapperScan(basePackages = "cn.iocoder.springboot.lab17.dynamicdatasource.mapper") @EnableAspectJAutoProxy(exposeProxy = true) // http://www.voidcn.com/article/p-zddcuyii-bpt.html public class Application { } ================================================ FILE: lab-17/lab-17-dynamic-datasource-baomidou-01/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/constant/DBConstants.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.constant; /** * 数据库枚举类 */ public class DBConstants { /** * 数据源分组 - 订单库 */ public static final String DATASOURCE_ORDERS = "orders"; /** * 数据源分组 - 用户库 */ public static final String DATASOURCE_USERS = "users"; } ================================================ FILE: lab-17/lab-17-dynamic-datasource-baomidou-01/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/dataobject/OrderDO.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.dataobject; /** * 订单 DO */ public class OrderDO { /** * 订单编号 */ private Integer id; /** * 用户编号 */ private Integer userId; public Integer getId() { return id; } public OrderDO setId(Integer id) { this.id = id; return this; } public Integer getUserId() { return userId; } public OrderDO setUserId(Integer userId) { this.userId = userId; return this; } @Override public String toString() { return "OrderDO{" + "id=" + id + ", userId=" + userId + '}'; } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-baomidou-01/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/dataobject/UserDO.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.dataobject; /** * 用户 DO */ public class UserDO { /** * 用户编号 */ private Integer id; /** * 账号 */ private String username; public Integer getId() { return id; } public UserDO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserDO setUsername(String username) { this.username = username; return this; } @Override public String toString() { return "UserDO{" + "id=" + id + ", username='" + username + '\'' + '}'; } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-baomidou-01/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/mapper/OrderMapper.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.mapper; import cn.iocoder.springboot.lab17.dynamicdatasource.constant.DBConstants; import cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO; import com.baomidou.dynamic.datasource.annotation.DS; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; @Repository @DS(DBConstants.DATASOURCE_ORDERS) public interface OrderMapper { OrderDO selectById(@Param("id") Integer id); } ================================================ FILE: lab-17/lab-17-dynamic-datasource-baomidou-01/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/mapper/UserMapper.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.mapper; import cn.iocoder.springboot.lab17.dynamicdatasource.constant.DBConstants; import cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.UserDO; import com.baomidou.dynamic.datasource.annotation.DS; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; @Repository @DS(DBConstants.DATASOURCE_USERS) public interface UserMapper { UserDO selectById(@Param("id") Integer id); } ================================================ FILE: lab-17/lab-17-dynamic-datasource-baomidou-01/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/service/OrderService.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.service; import cn.iocoder.springboot.lab17.dynamicdatasource.constant.DBConstants; import cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO; import cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.UserDO; import cn.iocoder.springboot.lab17.dynamicdatasource.mapper.OrderMapper; import cn.iocoder.springboot.lab17.dynamicdatasource.mapper.UserMapper; import com.baomidou.dynamic.datasource.annotation.DS; import org.springframework.aop.framework.AopContext; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @Service public class OrderService { @Autowired private OrderMapper orderMapper; @Autowired private UserMapper userMapper; private OrderService self() { return (OrderService) AopContext.currentProxy(); } public void method01() { // 查询订单 OrderDO order = orderMapper.selectById(1); System.out.println(order); // 查询用户 UserDO user = userMapper.selectById(1); System.out.println(user); } @Transactional public void method02() { // 查询订单 OrderDO order = orderMapper.selectById(1); System.out.println(order); // 查询用户 UserDO user = userMapper.selectById(1); System.out.println(user); } public void method03() { // 查询订单 self().method031(); // 查询用户 self().method032(); } @Transactional // 报错,因为此时获取的是 primary 对应的 DataSource ,即 users 。 public void method031() { OrderDO order = orderMapper.selectById(1); System.out.println(order); } @Transactional public void method032() { UserDO user = userMapper.selectById(1); System.out.println(user); } public void method04() { // 查询订单 self().method041(); // 查询用户 self().method042(); } @Transactional @DS(DBConstants.DATASOURCE_ORDERS) public void method041() { OrderDO order = orderMapper.selectById(1); System.out.println(order); } @Transactional @DS(DBConstants.DATASOURCE_USERS) public void method042() { UserDO user = userMapper.selectById(1); System.out.println(user); } @Transactional @DS(DBConstants.DATASOURCE_ORDERS) public void method05() { // 查询订单 OrderDO order = orderMapper.selectById(1); System.out.println(order); // 查询用户 self().method052(); } @Transactional(propagation = Propagation.REQUIRES_NEW) @DS(DBConstants.DATASOURCE_USERS) public void method052() { UserDO user = userMapper.selectById(1); System.out.println(user); } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-baomidou-01/src/main/resources/application.yaml ================================================ spring: datasource: # dynamic-datasource-spring-boot-starter 动态数据源的配置内容 dynamic: primary: users # 设置默认的数据源或者数据源组,默认值即为 master datasource: # 订单 orders 数据源配置 orders: url: jdbc:mysql://127.0.0.1:3306/test_orders?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: # 用户 users 数据源配置 users: url: jdbc:mysql://127.0.0.1:3306/test_users?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: # mybatis 配置内容 mybatis: config-location: classpath:mybatis-config.xml # 配置 MyBatis 配置文件路径 mapper-locations: classpath:mapper/*.xml # 配置 Mapper XML 地址 type-aliases-package: cn.iocoder.springboot.lab17.dynamicdatasource.dataobject # 配置数据库实体包路径 ================================================ FILE: lab-17/lab-17-dynamic-datasource-baomidou-01/src/main/resources/mapper/OrderMapper.xml ================================================ id, user_id ================================================ FILE: lab-17/lab-17-dynamic-datasource-baomidou-01/src/main/resources/mapper/UserMapper.xml ================================================ id, username ================================================ FILE: lab-17/lab-17-dynamic-datasource-baomidou-01/src/main/resources/mybatis-config.xml ================================================ ================================================ FILE: lab-17/lab-17-dynamic-datasource-baomidou-01/src/main/resources/sql/db.sql ================================================ CREATE TABLE `users` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号', `username` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号', PRIMARY KEY (`id`), UNIQUE KEY `idx_username` (`username`) ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; CREATE TABLE `orders` ( `id` int(11) DEFAULT NULL COMMENT '订单编号', `user_id` int(16) DEFAULT NULL COMMENT '用户编号' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='订单表'; ================================================ FILE: lab-17/lab-17-dynamic-datasource-baomidou-01/src/test/java/cn/iocoder/springboot/lab17/dynamicdatasource/mapper/OrderMapperTest.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.mapper; import cn.iocoder.springboot.lab17.dynamicdatasource.Application; import cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class OrderMapperTest { @Autowired private OrderMapper orderMapper; @Test public void testSelectById() { OrderDO order = orderMapper.selectById(1); System.out.println(order); } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-baomidou-01/src/test/java/cn/iocoder/springboot/lab17/dynamicdatasource/mapper/UserMapperTest.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.mapper; import cn.iocoder.springboot.lab17.dynamicdatasource.Application; import cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.UserDO; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class UserMapperTest { @Autowired private UserMapper userMapper; @Test public void testSelectById() { UserDO user = userMapper.selectById(1); System.out.println(user); } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-baomidou-01/src/test/java/cn/iocoder/springboot/lab17/dynamicdatasource/service/OrderServiceTest.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.service; import cn.iocoder.springboot.lab17.dynamicdatasource.Application; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class OrderServiceTest { @Autowired private OrderService orderService; @Test public void testMethod01() { orderService.method01(); } @Test public void testMethod02() { orderService.method02(); } @Test public void testMethod03() { orderService.method03(); } @Test public void testMethod04() { orderService.method04(); } @Test public void testMethod05() { orderService.method05(); } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-baomidou-02/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE 4.0.0 lab-17-dynamic-datasource-baomidou-02 org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 5.1.48 org.mybatis.spring.boot mybatis-spring-boot-starter 2.1.1 com.baomidou dynamic-datasource-spring-boot-starter 2.5.7 org.springframework.boot spring-boot-actuator org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-17/lab-17-dynamic-datasource-baomidou-02/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/Application.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.EnableAspectJAutoProxy; @SpringBootApplication @MapperScan(basePackages = "cn.iocoder.springboot.lab17.dynamicdatasource.mapper") @EnableAspectJAutoProxy(exposeProxy = true) // http://www.voidcn.com/article/p-zddcuyii-bpt.html public class Application { } ================================================ FILE: lab-17/lab-17-dynamic-datasource-baomidou-02/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/constant/DBConstants.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.constant; /** * 数据库枚举类 */ public class DBConstants { /** * 数据源分组 - 订单库 - 主库 */ public static final String DATASOURCE_MASTER = "master"; /** * 数据源分组 - 订单库 - 从库 */ public static final String DATASOURCE_SLAVE = "slave"; } ================================================ FILE: lab-17/lab-17-dynamic-datasource-baomidou-02/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/dataobject/OrderDO.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.dataobject; /** * 订单 DO */ public class OrderDO { /** * 订单编号 */ private Integer id; /** * 用户编号 */ private Integer userId; public Integer getId() { return id; } public OrderDO setId(Integer id) { this.id = id; return this; } public Integer getUserId() { return userId; } public OrderDO setUserId(Integer userId) { this.userId = userId; return this; } @Override public String toString() { return "OrderDO{" + "id=" + id + ", userId=" + userId + '}'; } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-baomidou-02/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/mapper/OrderMapper.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.mapper; import cn.iocoder.springboot.lab17.dynamicdatasource.constant.DBConstants; import cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO; import com.baomidou.dynamic.datasource.annotation.DS; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; @Repository public interface OrderMapper { @DS(DBConstants.DATASOURCE_SLAVE) OrderDO selectById(@Param("id") Integer id); @DS(DBConstants.DATASOURCE_MASTER) int insert(OrderDO entity); } ================================================ FILE: lab-17/lab-17-dynamic-datasource-baomidou-02/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/service/OrderService.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.service; import cn.iocoder.springboot.lab17.dynamicdatasource.constant.DBConstants; import cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO; import cn.iocoder.springboot.lab17.dynamicdatasource.mapper.OrderMapper; import com.baomidou.dynamic.datasource.annotation.DS; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class OrderService { @Autowired private OrderMapper orderMapper; @Transactional @DS(DBConstants.DATASOURCE_MASTER) public void add(OrderDO order) { // 这里先假模假样的读取一下, OrderDO exists = orderMapper.selectById(order.getId()); System.out.println(exists); // 插入订单 orderMapper.insert(order); } public OrderDO findById(Integer id) { return orderMapper.selectById(id); } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-baomidou-02/src/main/resources/application.yaml ================================================ spring: datasource: # dynamic-datasource-spring-boot-starter 动态数据源的配置内容 dynamic: primary: master # 设置默认的数据源或者数据源组,默认值即为 master datasource: # 订单 orders 主库的数据源配置 master: url: jdbc:mysql://127.0.0.1:3306/test_orders?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: # 订单 orders 从库数据源配置 slave_1: url: jdbc:mysql://127.0.0.1:3306/test_orders_01?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: # 订单 orders 从库数据源配置 slave_2: url: jdbc:mysql://127.0.0.1:3306/test_orders_02?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: # mybatis 配置内容 mybatis: config-location: classpath:mybatis-config.xml # 配置 MyBatis 配置文件路径 mapper-locations: classpath:mapper/*.xml # 配置 Mapper XML 地址 type-aliases-package: cn.iocoder.springboot.lab17.dynamicdatasource.dataobject # 配置数据库实体包路径 ================================================ FILE: lab-17/lab-17-dynamic-datasource-baomidou-02/src/main/resources/mapper/OrderMapper.xml ================================================ id, user_id INSERT INTO orders ( user_id ) VALUES ( #{userId} ) ================================================ FILE: lab-17/lab-17-dynamic-datasource-baomidou-02/src/main/resources/mybatis-config.xml ================================================ ================================================ FILE: lab-17/lab-17-dynamic-datasource-baomidou-02/src/main/resources/sql/db.sql ================================================ CREATE TABLE `users` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号', `username` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号', PRIMARY KEY (`id`), UNIQUE KEY `idx_username` (`username`) ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; CREATE TABLE `orders` ( `id` int(11) DEFAULT NULL COMMENT '订单编号', `user_id` int(16) DEFAULT NULL COMMENT '用户编号' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='订单表'; ================================================ FILE: lab-17/lab-17-dynamic-datasource-baomidou-02/src/test/java/dynamicdatasource/mapper/OrderMapperTest.java ================================================ package dynamicdatasource.mapper; import cn.iocoder.springboot.lab17.dynamicdatasource.Application; import cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO; import cn.iocoder.springboot.lab17.dynamicdatasource.mapper.OrderMapper; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class OrderMapperTest { @Autowired private OrderMapper orderMapper; @Test public void testSelectById() { for (int i = 0; i < 10; i++) { OrderDO order = orderMapper.selectById(1); System.out.println(order); } } @Test public void testInsert() { OrderDO order = new OrderDO(); order.setUserId(10); orderMapper.insert(order); } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-baomidou-02/src/test/java/dynamicdatasource/service/OrderServiceTest.java ================================================ package dynamicdatasource.service; import cn.iocoder.springboot.lab17.dynamicdatasource.Application; import cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO; import cn.iocoder.springboot.lab17.dynamicdatasource.service.OrderService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class OrderServiceTest { @Autowired private OrderService orderService; @Test public void testAdd() { OrderDO order = new OrderDO(); order.setUserId(20); orderService.add(order); } @Test public void testFindById() { OrderDO order = orderService.findById(1); System.out.println(order); } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-jdbctemplate/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE 4.0.0 lab-17-dynamic-datasource-jdbctemplate org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 5.1.48 org.springframework spring-aspects org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-17/lab-17-dynamic-datasource-jdbctemplate/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/Application.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.EnableAspectJAutoProxy; @SpringBootApplication @EnableAspectJAutoProxy(exposeProxy = true) // http://www.voidcn.com/article/p-zddcuyii-bpt.html public class Application { } ================================================ FILE: lab-17/lab-17-dynamic-datasource-jdbctemplate/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/config/JdbcTemplateOrdersConfig.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.config; import cn.iocoder.springboot.lab17.dynamicdatasource.constant.DBConstants; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; import javax.sql.DataSource; @Configuration public class JdbcTemplateOrdersConfig { /** * 创建 orders 数据源 */ @Bean(name = "ordersDataSource") @ConfigurationProperties(prefix = "spring.datasource.orders") public DataSource dataSource() { return DataSourceBuilder.create().build(); } /** * 创建 orders JdbcTemplate */ @Bean(name = DBConstants.JDBC_TEMPLATE_ORDERS) public JdbcTemplate jdbcTemplate() { return new JdbcTemplate(this.dataSource()); } /** * 创建 orders 数据源的 TransactionManager 事务管理器 */ @Bean(name = DBConstants.TX_MANAGER_ORDERS) public PlatformTransactionManager transactionManager() { return new DataSourceTransactionManager(this.dataSource()); } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-jdbctemplate/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/config/JdbcTemplateUsersConfig.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.config; import cn.iocoder.springboot.lab17.dynamicdatasource.constant.DBConstants; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; import javax.sql.DataSource; @Configuration public class JdbcTemplateUsersConfig { /** * 创建 users 数据源 */ @Bean(name = "usersDataSource") @ConfigurationProperties(prefix = "spring.datasource.users") public DataSource dataSource() { return DataSourceBuilder.create().build(); } /** * 创建 users JdbcTemplate */ @Bean(name = DBConstants.JDBC_TEMPLATE_USERS) public JdbcTemplate jdbcTemplate() { return new JdbcTemplate(this.dataSource()); } /** * 创建 users 数据源的 TransactionManager 事务管理器 */ @Bean(name = DBConstants.TX_MANAGER_USERS) public PlatformTransactionManager transactionManager() { return new DataSourceTransactionManager(this.dataSource()); } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-jdbctemplate/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/constant/DBConstants.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.constant; /** * 数据库枚举类 */ public class DBConstants { /** * 事务管理器 - 订单库 */ public static final String TX_MANAGER_ORDERS = "ordersTransactionManager"; /** * 事务管理器 - 用户库 */ public static final String TX_MANAGER_USERS = "usersTransactionManager"; /** * JdbcTemplate - 订单库 */ public static final String JDBC_TEMPLATE_ORDERS = "ordersJdbcTemplate"; /** * JdbcTemplate - 用户库 */ public static final String JDBC_TEMPLATE_USERS = "usersJdbcTemplate"; } ================================================ FILE: lab-17/lab-17-dynamic-datasource-jdbctemplate/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/dao/OrderDao.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.dao; import cn.iocoder.springboot.lab17.dynamicdatasource.constant.DBConstants; import cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; import javax.annotation.Resource; @Repository public class OrderDao { @Resource(name = DBConstants.JDBC_TEMPLATE_ORDERS) private JdbcTemplate template; public OrderDO selectById(Integer id) { return template.queryForObject("SELECT id, user_id FROM orders WHERE id = ?", new BeanPropertyRowMapper<>(OrderDO.class), // 结果转换成对应的对象 id); } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-jdbctemplate/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/dao/UserDao.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.dao; import cn.iocoder.springboot.lab17.dynamicdatasource.constant.DBConstants; import cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.UserDO; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; import javax.annotation.Resource; @Repository public class UserDao { @Resource(name = DBConstants.JDBC_TEMPLATE_USERS) private JdbcTemplate template; public UserDO selectById(Integer id) { return template.queryForObject("SELECT id, username FROM users WHERE id = ?", new BeanPropertyRowMapper<>(UserDO.class), // 结果转换成对应的对象 id); } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-jdbctemplate/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/dataobject/OrderDO.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.dataobject; /** * 订单 DO */ public class OrderDO { /** * 订单编号 */ private Integer id; /** * 用户编号 */ private Integer userId; public Integer getId() { return id; } public OrderDO setId(Integer id) { this.id = id; return this; } public Integer getUserId() { return userId; } public OrderDO setUserId(Integer userId) { this.userId = userId; return this; } @Override public String toString() { return "OrderDO{" + "id=" + id + ", userId=" + userId + '}'; } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-jdbctemplate/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/dataobject/UserDO.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.dataobject; /** * 用户 DO */ public class UserDO { /** * 用户编号 */ private Integer id; /** * 账号 */ private String username; public Integer getId() { return id; } public UserDO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserDO setUsername(String username) { this.username = username; return this; } @Override public String toString() { return "UserDO{" + "id=" + id + ", username='" + username + '\'' + '}'; } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-jdbctemplate/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/service/OrderService.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.service; import cn.iocoder.springboot.lab17.dynamicdatasource.constant.DBConstants; import cn.iocoder.springboot.lab17.dynamicdatasource.dao.OrderDao; import cn.iocoder.springboot.lab17.dynamicdatasource.dao.UserDao; import cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO; import cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.UserDO; import org.springframework.aop.framework.AopContext; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @Service public class OrderService { @Autowired private OrderDao orderDao; @Autowired private UserDao userDao; private OrderService self() { return (OrderService) AopContext.currentProxy(); } public void method01() { // 查询订单 OrderDO order = orderDao.selectById(1); System.out.println(order); // 查询用户 UserDO user = userDao.selectById(1); System.out.println(user); } @Transactional // 报错,找不到事务管理器 public void method02() { // 查询订单 OrderDO order = orderDao.selectById(1); System.out.println(order); // 查询用户 UserDO user = userDao.selectById(1); System.out.println(user); } public void method03() { // 查询订单 self().method031(); // 查询用户 self().method032(); } @Transactional(transactionManager = DBConstants.TX_MANAGER_ORDERS) public void method031() { OrderDO order = orderDao.selectById(1); System.out.println(order); } @Transactional(transactionManager = DBConstants.TX_MANAGER_USERS) public void method032() { UserDO user = userDao.selectById(1); System.out.println(user); } @Transactional(transactionManager = DBConstants.TX_MANAGER_ORDERS) public void method05() { // 查询订单 OrderDO order = orderDao.selectById(1); System.out.println(order); // 查询用户 self().method052(); } @Transactional(transactionManager = DBConstants.TX_MANAGER_USERS, propagation = Propagation.REQUIRES_NEW) public void method052() { UserDO user = userDao.selectById(1); System.out.println(user); } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-jdbctemplate/src/main/resources/application.yaml ================================================ spring: # datasource 数据源配置内容 datasource: # 订单数据源配置 orders: jdbc-url: jdbc:mysql://127.0.0.1:3306/test_orders?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: # 用户数据源配置 users: jdbc-url: jdbc:mysql://127.0.0.1:3306/test_users?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: ================================================ FILE: lab-17/lab-17-dynamic-datasource-jdbctemplate/src/test/java/cn/iocoder/springboot/lab17/dynamicdatasource/dao/OrderDaoTest.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.dao; import cn.iocoder.springboot.lab17.dynamicdatasource.Application; import cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class OrderDaoTest { @Autowired private OrderDao orderDao; @Test public void testSelectById() { OrderDO order = orderDao.selectById(1); System.out.println(order); } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-jdbctemplate/src/test/java/cn/iocoder/springboot/lab17/dynamicdatasource/dao/UserDaoTest.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.dao; import cn.iocoder.springboot.lab17.dynamicdatasource.Application; import cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.UserDO; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class UserDaoTest { @Autowired private UserDao userDao; @Test public void testSelectById() { UserDO user = userDao.selectById(1); System.out.println(user); } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-jdbctemplate/src/test/java/cn/iocoder/springboot/lab17/dynamicdatasource/package-info.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource; ================================================ FILE: lab-17/lab-17-dynamic-datasource-jdbctemplate/src/test/java/cn/iocoder/springboot/lab17/dynamicdatasource/service/OrderServiceTest.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.service; import cn.iocoder.springboot.lab17.dynamicdatasource.Application; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class OrderServiceTest { @Autowired private OrderService orderService; @Test public void testMethod01() { orderService.method01(); } @Test public void testMethod02() { orderService.method02(); } @Test public void testMethod03() { orderService.method03(); } @Test public void testMethod05() { orderService.method05(); } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-mybatis/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE 4.0.0 lab-17-dynamic-datasource-mybatis org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 5.1.48 org.mybatis.spring.boot mybatis-spring-boot-starter 2.1.1 org.springframework spring-aspects org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-17/lab-17-dynamic-datasource-mybatis/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/Application.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.EnableAspectJAutoProxy; @SpringBootApplication @EnableAspectJAutoProxy(exposeProxy = true) // http://www.voidcn.com/article/p-zddcuyii-bpt.html public class Application { } ================================================ FILE: lab-17/lab-17-dynamic-datasource-mybatis/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/config/MyBatisOrdersConfig.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.config; import cn.iocoder.springboot.lab17.dynamicdatasource.constant.DBConstants; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.SqlSessionTemplate; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; import javax.sql.DataSource; @Configuration @MapperScan(basePackages = "cn.iocoder.springboot.lab17.dynamicdatasource.mapper.orders", sqlSessionTemplateRef = "ordersSqlSessionTemplate") public class MyBatisOrdersConfig { /** * 创建 orders 数据源 */ @Bean(name = "ordersDataSource") @ConfigurationProperties(prefix = "spring.datasource.orders") public DataSource dataSource() { return DataSourceBuilder.create().build(); } /** * 创建 MyBatis SqlSessionFactory */ @Bean(name = "ordersSqlSessionFactory") public SqlSessionFactory sqlSessionFactory() throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); // 设置 orders 数据源 bean.setDataSource(this.dataSource()); // 设置 entity 所在包 bean.setTypeAliasesPackage("cn.iocoder.springboot.lab17.dynamicdatasource.dataobject"); // 设置 config 路径 bean.setConfigLocation(new PathMatchingResourcePatternResolver().getResource("classpath:mybatis-config.xml")); // 设置 mapper 路径 bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/orders/*.xml")); return bean.getObject(); } /** * 创建 MyBatis SqlSessionTemplate */ @Bean(name = "ordersSqlSessionTemplate") public SqlSessionTemplate sqlSessionTemplate() throws Exception { return new SqlSessionTemplate(this.sqlSessionFactory()); } /** * 创建 orders 数据源的 TransactionManager 事务管理器 */ @Bean(name = DBConstants.TX_MANAGER_ORDERS) public PlatformTransactionManager transactionManager() { return new DataSourceTransactionManager(this.dataSource()); } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-mybatis/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/config/MyBatisUsersConfig.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.config; import cn.iocoder.springboot.lab17.dynamicdatasource.constant.DBConstants; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.SqlSessionTemplate; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; import javax.sql.DataSource; @Configuration @MapperScan(basePackages = "cn.iocoder.springboot.lab17.dynamicdatasource.mapper.users", sqlSessionTemplateRef = "usersSqlSessionTemplate") public class MyBatisUsersConfig { /** * 创建 users 数据源 */ @Bean(name = "usersDataSource") @ConfigurationProperties(prefix = "spring.datasource.users") public DataSource dataSource() { return DataSourceBuilder.create().build(); } /** * 创建 MyBatis SqlSessionFactory */ @Bean(name = "sqlSessionFactory") public SqlSessionFactory sqlSessionFactory() throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); // 设置 users 数据源 bean.setDataSource(this.dataSource()); // 设置 entity 所在包 bean.setTypeAliasesPackage("cn.iocoder.springboot.lab17.dynamicdatasource.dataobject"); // 设置 config 路径 bean.setConfigLocation(new PathMatchingResourcePatternResolver().getResource("classpath:mybatis-config.xml")); // 设置 mapper 路径 bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/users/*.xml")); return bean.getObject(); } /** * 创建 MyBatis SqlSessionTemplate */ @Bean(name = "usersSqlSessionTemplate") public SqlSessionTemplate sqlSessionTemplate() throws Exception { return new SqlSessionTemplate(this.sqlSessionFactory()); } /** * 创建 users 数据源的 TransactionManager 事务管理器 */ @Bean(name = DBConstants.TX_MANAGER_USERS) public PlatformTransactionManager transactionManager() { return new DataSourceTransactionManager(this.dataSource()); } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-mybatis/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/constant/DBConstants.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.constant; /** * 数据库枚举类 */ public class DBConstants { /** * 事务管理器 - 订单库 */ public static final String TX_MANAGER_ORDERS = "ordersTransactionManager"; /** * 事务管理器 - 用户库 */ public static final String TX_MANAGER_USERS = "usersTransactionManager"; } ================================================ FILE: lab-17/lab-17-dynamic-datasource-mybatis/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/dataobject/OrderDO.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.dataobject; /** * 订单 DO */ public class OrderDO { /** * 订单编号 */ private Integer id; /** * 用户编号 */ private Integer userId; public Integer getId() { return id; } public OrderDO setId(Integer id) { this.id = id; return this; } public Integer getUserId() { return userId; } public OrderDO setUserId(Integer userId) { this.userId = userId; return this; } @Override public String toString() { return "OrderDO{" + "id=" + id + ", userId=" + userId + '}'; } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-mybatis/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/dataobject/UserDO.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.dataobject; /** * 用户 DO */ public class UserDO { /** * 用户编号 */ private Integer id; /** * 账号 */ private String username; public Integer getId() { return id; } public UserDO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserDO setUsername(String username) { this.username = username; return this; } @Override public String toString() { return "UserDO{" + "id=" + id + ", username='" + username + '\'' + '}'; } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-mybatis/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/mapper/orders/OrderMapper.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.mapper.orders; import cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; @Repository public interface OrderMapper { OrderDO selectById(@Param("id") Integer id); } ================================================ FILE: lab-17/lab-17-dynamic-datasource-mybatis/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/mapper/users/UserMapper.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.mapper.users; import cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.UserDO; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; @Repository public interface UserMapper { UserDO selectById(@Param("id") Integer id); } ================================================ FILE: lab-17/lab-17-dynamic-datasource-mybatis/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/service/OrderService.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.service; import cn.iocoder.springboot.lab17.dynamicdatasource.constant.DBConstants; import cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO; import cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.UserDO; import cn.iocoder.springboot.lab17.dynamicdatasource.mapper.orders.OrderMapper; import cn.iocoder.springboot.lab17.dynamicdatasource.mapper.users.UserMapper; import org.springframework.aop.framework.AopContext; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @Service public class OrderService { @Autowired private OrderMapper orderMapper; @Autowired private UserMapper userMapper; private OrderService self() { return (OrderService) AopContext.currentProxy(); } public void method01() { // 查询订单 OrderDO order = orderMapper.selectById(1); System.out.println(order); // 查询用户 UserDO user = userMapper.selectById(1); System.out.println(user); } @Transactional // 报错,找不到事务管理器 public void method02() { // 查询订单 OrderDO order = orderMapper.selectById(1); System.out.println(order); // 查询用户 UserDO user = userMapper.selectById(1); System.out.println(user); } public void method03() { // 查询订单 self().method031(); // 查询用户 self().method032(); } @Transactional(transactionManager = DBConstants.TX_MANAGER_ORDERS) public void method031() { OrderDO order = orderMapper.selectById(1); System.out.println(order); } @Transactional(transactionManager = DBConstants.TX_MANAGER_USERS) public void method032() { UserDO user = userMapper.selectById(1); System.out.println(user); } @Transactional(transactionManager = DBConstants.TX_MANAGER_ORDERS) public void method05() { // 查询订单 OrderDO order = orderMapper.selectById(1); System.out.println(order); // 查询用户 self().method052(); } @Transactional(transactionManager = DBConstants.TX_MANAGER_USERS, propagation = Propagation.REQUIRES_NEW) public void method052() { UserDO user = userMapper.selectById(1); System.out.println(user); } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-mybatis/src/main/resources/application.yaml ================================================ spring: # datasource 数据源配置内容 datasource: # 订单数据源配置 orders: jdbc-url: jdbc:mysql://127.0.0.1:3306/test_orders?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: # 用户数据源配置 users: jdbc-url: jdbc:mysql://127.0.0.1:3306/test_users?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: # mybatis 配置内容 #mybatis: # config-location: classpath:mybatis-config.xml # 配置 MyBatis 配置文件路径 # type-aliases-package: cn.iocoder.springboot.lab17.dynamicdatasource.dataobject # 配置数据库实体包路径 ================================================ FILE: lab-17/lab-17-dynamic-datasource-mybatis/src/main/resources/mapper/orders/OrderMapper.xml ================================================ id, user_id ================================================ FILE: lab-17/lab-17-dynamic-datasource-mybatis/src/main/resources/mapper/users/UserMapper.xml ================================================ id, username ================================================ FILE: lab-17/lab-17-dynamic-datasource-mybatis/src/main/resources/mybatis-config.xml ================================================ ================================================ FILE: lab-17/lab-17-dynamic-datasource-mybatis/src/main/resources/sql/db.sql ================================================ CREATE TABLE `users` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号', `username` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号', PRIMARY KEY (`id`), UNIQUE KEY `idx_username` (`username`) ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; CREATE TABLE `orders` ( `id` int(11) DEFAULT NULL COMMENT '订单编号', `user_id` int(16) DEFAULT NULL COMMENT '用户编号' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='订单表'; ================================================ FILE: lab-17/lab-17-dynamic-datasource-mybatis/src/test/java/cn/iocoder/springboot/lab17/dynamicdatasource/mapper/orders/OrderMapperTest.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.mapper.orders; import cn.iocoder.springboot.lab17.dynamicdatasource.Application; import cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class OrderMapperTest { @Autowired private OrderMapper orderMapper; @Test public void testSelectById() { OrderDO order = orderMapper.selectById(1); System.out.println(order); } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-mybatis/src/test/java/cn/iocoder/springboot/lab17/dynamicdatasource/mapper/users/UserMapperTest.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.mapper.users; import cn.iocoder.springboot.lab17.dynamicdatasource.Application; import cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.UserDO; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class UserMapperTest { @Autowired private UserMapper userMapper; @Test public void testSelectById() { UserDO user = userMapper.selectById(1); System.out.println(user); } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-mybatis/src/test/java/cn/iocoder/springboot/lab17/dynamicdatasource/package-info.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource; ================================================ FILE: lab-17/lab-17-dynamic-datasource-mybatis/src/test/java/cn/iocoder/springboot/lab17/dynamicdatasource/service/OrderServiceTest.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.service; import cn.iocoder.springboot.lab17.dynamicdatasource.Application; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class OrderServiceTest { @Autowired private OrderService orderService; @Test public void testMethod01() { orderService.method01(); } @Test public void testMethod02() { orderService.method02(); } @Test public void testMethod03() { orderService.method03(); } @Test public void testMethod05() { orderService.method05(); } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-sharding-jdbc-01/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE 4.0.0 lab-17-dynamic-datasource-sharding-jdbc-01 org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 5.1.48 org.mybatis.spring.boot mybatis-spring-boot-starter 2.1.1 org.apache.shardingsphere sharding-jdbc-spring-boot-starter 4.0.0-RC2 org.springframework spring-aspects org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-17/lab-17-dynamic-datasource-sharding-jdbc-01/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/Application.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.EnableAspectJAutoProxy; @SpringBootApplication @MapperScan(basePackages = "cn.iocoder.springboot.lab17.dynamicdatasource.mapper") @EnableAspectJAutoProxy(exposeProxy = true) // http://www.voidcn.com/article/p-zddcuyii-bpt.html public class Application { } ================================================ FILE: lab-17/lab-17-dynamic-datasource-sharding-jdbc-01/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/dataobject/OrderDO.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.dataobject; /** * 订单 DO */ public class OrderDO { /** * 订单编号 */ private Integer id; /** * 用户编号 */ private Integer userId; public Integer getId() { return id; } public OrderDO setId(Integer id) { this.id = id; return this; } public Integer getUserId() { return userId; } public OrderDO setUserId(Integer userId) { this.userId = userId; return this; } @Override public String toString() { return "OrderDO{" + "id=" + id + ", userId=" + userId + '}'; } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-sharding-jdbc-01/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/dataobject/UserDO.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.dataobject; /** * 用户 DO */ public class UserDO { /** * 用户编号 */ private Integer id; /** * 账号 */ private String username; public Integer getId() { return id; } public UserDO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserDO setUsername(String username) { this.username = username; return this; } @Override public String toString() { return "UserDO{" + "id=" + id + ", username='" + username + '\'' + '}'; } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-sharding-jdbc-01/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/mapper/OrderMapper.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.mapper; import cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; @Repository public interface OrderMapper { OrderDO selectById(@Param("id") Integer id); } ================================================ FILE: lab-17/lab-17-dynamic-datasource-sharding-jdbc-01/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/mapper/UserMapper.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.mapper; import cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.UserDO; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; @Repository public interface UserMapper { UserDO selectById(@Param("id") Integer id); } ================================================ FILE: lab-17/lab-17-dynamic-datasource-sharding-jdbc-01/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/service/OrderService.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.service; import cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO; import cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.UserDO; import cn.iocoder.springboot.lab17.dynamicdatasource.mapper.OrderMapper; import cn.iocoder.springboot.lab17.dynamicdatasource.mapper.UserMapper; import org.springframework.aop.framework.AopContext; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @Service public class OrderService { @Autowired private OrderMapper orderMapper; @Autowired private UserMapper userMapper; private OrderService self() { return (OrderService) AopContext.currentProxy(); } public void method01() { // 查询订单 OrderDO order = orderMapper.selectById(1); System.out.println(order); // 查询用户 UserDO user = userMapper.selectById(1); System.out.println(user); } @Transactional public void method02() { // 查询订单 OrderDO order = orderMapper.selectById(1); System.out.println(order); // 查询用户 UserDO user = userMapper.selectById(1); System.out.println(user); } public void method03() { // 查询订单 self().method031(); // 查询用户 self().method032(); } @Transactional // 报错,因为此时获取的是 primary 对应的 DataSource ,即 users 。 public void method031() { OrderDO order = orderMapper.selectById(1); System.out.println(order); } @Transactional public void method032() { UserDO user = userMapper.selectById(1); System.out.println(user); } public void method04() { // 查询订单 self().method041(); // 查询用户 self().method042(); } @Transactional public void method041() { OrderDO order = orderMapper.selectById(1); System.out.println(order); } @Transactional public void method042() { UserDO user = userMapper.selectById(1); System.out.println(user); } @Transactional public void method05() { // 查询订单 OrderDO order = orderMapper.selectById(1); System.out.println(order); // 查询用户 self().method052(); } @Transactional(propagation = Propagation.REQUIRES_NEW) public void method052() { UserDO user = userMapper.selectById(1); System.out.println(user); } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-sharding-jdbc-01/src/main/resources/application.yaml ================================================ spring: # ShardingSphere 配置项 shardingsphere: datasource: # 所有数据源的名字 names: ds-orders, ds-users # 订单 orders 数据源配置 ds-orders: type: com.zaxxer.hikari.HikariDataSource # 使用 Hikari 数据库连接池 driver-class-name: com.mysql.jdbc.Driver jdbc-url: jdbc:mysql://127.0.0.1:3306/test_orders?useSSL=false&useUnicode=true&characterEncoding=UTF-8 username: root password: # 订单 users 数据源配置 ds-users: type: com.zaxxer.hikari.HikariDataSource # 使用 Hikari 数据库连接池 driver-class-name: com.mysql.jdbc.Driver jdbc-url: jdbc:mysql://127.0.0.1:3306/test_users?useSSL=false&useUnicode=true&characterEncoding=UTF-8 username: root password: # 分片规则 sharding: tables: # orders 表配置 orders: actualDataNodes: ds-orders.orders # 映射到 ds-orders 数据源的 orders 表 # users 表配置 users: actualDataNodes: ds-users.users # 映射到 ds-users 数据源的 users 表 # mybatis 配置内容 mybatis: config-location: classpath:mybatis-config.xml # 配置 MyBatis 配置文件路径 mapper-locations: classpath:mapper/*.xml # 配置 Mapper XML 地址 type-aliases-package: cn.iocoder.springboot.lab17.dynamicdatasource.dataobject # 配置数据库实体包路径 ================================================ FILE: lab-17/lab-17-dynamic-datasource-sharding-jdbc-01/src/main/resources/mapper/OrderMapper.xml ================================================ id, user_id ================================================ FILE: lab-17/lab-17-dynamic-datasource-sharding-jdbc-01/src/main/resources/mapper/UserMapper.xml ================================================ id, username ================================================ FILE: lab-17/lab-17-dynamic-datasource-sharding-jdbc-01/src/main/resources/mybatis-config.xml ================================================ ================================================ FILE: lab-17/lab-17-dynamic-datasource-sharding-jdbc-01/src/main/resources/sql/db.sql ================================================ CREATE TABLE `users` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号', `username` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号', PRIMARY KEY (`id`), UNIQUE KEY `idx_username` (`username`) ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; CREATE TABLE `orders` ( `id` int(11) DEFAULT NULL COMMENT '订单编号', `user_id` int(16) DEFAULT NULL COMMENT '用户编号' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='订单表'; ================================================ FILE: lab-17/lab-17-dynamic-datasource-sharding-jdbc-01/src/test/java/dynamicdatasource/mapper/OrderMapperTest.java ================================================ package dynamicdatasource.mapper; import cn.iocoder.springboot.lab17.dynamicdatasource.Application; import cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO; import cn.iocoder.springboot.lab17.dynamicdatasource.mapper.OrderMapper; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class OrderMapperTest { @Autowired private OrderMapper orderMapper; @Test public void testSelectById() { OrderDO order = orderMapper.selectById(1); System.out.println(order); } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-sharding-jdbc-01/src/test/java/dynamicdatasource/mapper/UserMapperTest.java ================================================ package dynamicdatasource.mapper; import cn.iocoder.springboot.lab17.dynamicdatasource.Application; import cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.UserDO; import cn.iocoder.springboot.lab17.dynamicdatasource.mapper.UserMapper; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class UserMapperTest { @Autowired private UserMapper userMapper; @Test public void testSelectById() { UserDO user = userMapper.selectById(1); System.out.println(user); } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-sharding-jdbc-01/src/test/java/dynamicdatasource/service/OrderServiceTest.java ================================================ package dynamicdatasource.service; import cn.iocoder.springboot.lab17.dynamicdatasource.Application; import cn.iocoder.springboot.lab17.dynamicdatasource.service.OrderService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class OrderServiceTest { @Autowired private OrderService orderService; @Test public void testMethod01() { orderService.method01(); } @Test public void testMethod02() { orderService.method02(); } @Test public void testMethod03() { orderService.method03(); } @Test public void testMethod04() { orderService.method04(); } @Test public void testMethod05() { orderService.method05(); } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-sharding-jdbc-02/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE 4.0.0 lab-17-dynamic-datasource-sharding-jdbc-02 org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 5.1.48 org.mybatis.spring.boot mybatis-spring-boot-starter 2.1.1 org.apache.shardingsphere sharding-jdbc-spring-boot-starter 4.0.0-RC2 org.springframework spring-aspects org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-17/lab-17-dynamic-datasource-sharding-jdbc-02/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/Application.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.EnableAspectJAutoProxy; @SpringBootApplication @MapperScan(basePackages = "cn.iocoder.springboot.lab17.dynamicdatasource.mapper") @EnableAspectJAutoProxy(exposeProxy = true) // http://www.voidcn.com/article/p-zddcuyii-bpt.html public class Application { } ================================================ FILE: lab-17/lab-17-dynamic-datasource-sharding-jdbc-02/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/dataobject/OrderDO.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.dataobject; /** * 订单 DO */ public class OrderDO { /** * 订单编号 */ private Integer id; /** * 用户编号 */ private Integer userId; public Integer getId() { return id; } public OrderDO setId(Integer id) { this.id = id; return this; } public Integer getUserId() { return userId; } public OrderDO setUserId(Integer userId) { this.userId = userId; return this; } @Override public String toString() { return "OrderDO{" + "id=" + id + ", userId=" + userId + '}'; } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-sharding-jdbc-02/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/mapper/OrderMapper.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.mapper; import cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; @Repository public interface OrderMapper { OrderDO selectById(@Param("id") Integer id); int insert(OrderDO entity); } ================================================ FILE: lab-17/lab-17-dynamic-datasource-sharding-jdbc-02/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/service/OrderService.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.service; import cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO; import cn.iocoder.springboot.lab17.dynamicdatasource.mapper.OrderMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class OrderService { @Autowired private OrderMapper orderMapper; @Transactional public void add(OrderDO order) { // 这里先假模假样的读取一下。读取从库 OrderDO exists = orderMapper.selectById(1); System.out.println(exists); // 插入订单 orderMapper.insert(order); // 这里先假模假样的读取一下。读取主库 exists = orderMapper.selectById(1); System.out.println(exists); } public OrderDO findById(Integer id) { return orderMapper.selectById(id); } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-sharding-jdbc-02/src/main/resources/application.yaml ================================================ spring: # ShardingSphere 配置项 shardingsphere: # 数据源配置 datasource: # 所有数据源的名字 names: ds-master, ds-slave-1, ds-slave-2 # 订单 orders 主库的数据源配置 ds-master: type: com.zaxxer.hikari.HikariDataSource # 使用 Hikari 数据库连接池 driver-class-name: com.mysql.jdbc.Driver jdbc-url: jdbc:mysql://127.0.0.1:3306/test_orders?useSSL=false&useUnicode=true&characterEncoding=UTF-8 username: root password: # 订单 orders 从库数据源配置 ds-slave-1: type: com.zaxxer.hikari.HikariDataSource # 使用 Hikari 数据库连接池 driver-class-name: com.mysql.jdbc.Driver jdbc-url: jdbc:mysql://127.0.0.1:3306/test_orders_01?useSSL=false&useUnicode=true&characterEncoding=UTF-8 username: root password: # 订单 orders 从库数据源配置 ds-slave-2: type: com.zaxxer.hikari.HikariDataSource # 使用 Hikari 数据库连接池 driver-class-name: com.mysql.jdbc.Driver jdbc-url: jdbc:mysql://127.0.0.1:3306/test_orders_02?useSSL=false&useUnicode=true&characterEncoding=UTF-8 username: root password: # 读写分离配置,对应 YamlMasterSlaveRuleConfiguration 配置类 masterslave: name: ms # 名字,任意,需要保证唯一 master-data-source-name: ds-master # 主库数据源 slave-data-source-names: ds-slave-1, ds-slave-2 # 从库数据源 # mybatis 配置内容 mybatis: config-location: classpath:mybatis-config.xml # 配置 MyBatis 配置文件路径 mapper-locations: classpath:mapper/*.xml # 配置 Mapper XML 地址 type-aliases-package: cn.iocoder.springboot.lab17.dynamicdatasource.dataobject # 配置数据库实体包路径 ================================================ FILE: lab-17/lab-17-dynamic-datasource-sharding-jdbc-02/src/main/resources/mapper/OrderMapper.xml ================================================ id, user_id INSERT INTO orders ( user_id ) VALUES ( #{userId} ) ================================================ FILE: lab-17/lab-17-dynamic-datasource-sharding-jdbc-02/src/main/resources/mybatis-config.xml ================================================ ================================================ FILE: lab-17/lab-17-dynamic-datasource-sharding-jdbc-02/src/main/resources/sql/db.sql ================================================ CREATE TABLE `users` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号', `username` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号', PRIMARY KEY (`id`), UNIQUE KEY `idx_username` (`username`) ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; CREATE TABLE `orders` ( `id` int(11) DEFAULT NULL COMMENT '订单编号', `user_id` int(16) DEFAULT NULL COMMENT '用户编号' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='订单表'; ================================================ FILE: lab-17/lab-17-dynamic-datasource-sharding-jdbc-02/src/test/java/cn/iocoder/springboot/lab17/dynamicdatasource/mapper/OrderMapperTest.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.mapper; import cn.iocoder.springboot.lab17.dynamicdatasource.Application; import cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO; import org.apache.shardingsphere.api.hint.HintManager; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class OrderMapperTest { @Autowired private OrderMapper orderMapper; @Test public void testSelectById() { // 测试从库的负载均衡 for (int i = 0; i < 10; i++) { OrderDO order = orderMapper.selectById(1); System.out.println(order); } } @Test public void testSelectById02() { // 测试强制访问主库 try (HintManager hintManager = HintManager.getInstance()) { // 设置强制访问主库 hintManager.setMasterRouteOnly(); // 执行查询 OrderDO order = orderMapper.selectById(1); System.out.println(order); } } @Test public void testInsert() { // 插入 OrderDO order = new OrderDO(); order.setUserId(10); orderMapper.insert(order); } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-sharding-jdbc-02/src/test/java/cn/iocoder/springboot/lab17/dynamicdatasource/service/OrderServiceTest.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.service; import cn.iocoder.springboot.lab17.dynamicdatasource.Application; import cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class OrderServiceTest { @Autowired private OrderService orderService; @Test public void testAdd() { OrderDO order = new OrderDO(); order.setUserId(20); orderService.add(order); } @Test public void testFindById() { OrderDO order = orderService.findById(1); System.out.println(order); } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-springdatajpa/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE 4.0.0 lab-17-dynamic-datasource-springdatajpa org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 5.1.48 org.springframework.boot spring-boot-starter-data-jpa org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-17/lab-17-dynamic-datasource-springdatajpa/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/Application.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.EnableAspectJAutoProxy; @SpringBootApplication @EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true) // http://www.voidcn.com/article/p-zddcuyii-bpt.html public class Application { } ================================================ FILE: lab-17/lab-17-dynamic-datasource-springdatajpa/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/config/HibernateConfig.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.orm.jpa.HibernateProperties; import org.springframework.boot.autoconfigure.orm.jpa.HibernateSettings; import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.Map; @Configuration public class HibernateConfig { @Autowired private JpaProperties jpaProperties; @Autowired private HibernateProperties hibernateProperties; /** * 获取 Hibernate Vendor 相关配置 */ @Bean(name = "hibernateVendorProperties") public Map hibernateVendorProperties() { return hibernateProperties.determineHibernateProperties( jpaProperties.getProperties(), new HibernateSettings()); } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-springdatajpa/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/config/JpaOrdersConfig.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.config; import cn.iocoder.springboot.lab17.dynamicdatasource.constant.DBConstants; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.transaction.PlatformTransactionManager; import javax.annotation.Resource; import javax.sql.DataSource; import java.util.Map; @Configuration @EnableJpaRepositories( entityManagerFactoryRef = DBConstants.ENTITY_MANAGER_FACTORY_ORDERS, transactionManagerRef = DBConstants.TX_MANAGER_ORDERS, basePackages = {"cn.iocoder.springboot.lab17.dynamicdatasource.repository.orders"}) // 设置 Repository 接口所在包 public class JpaOrdersConfig { @Resource(name = "hibernateVendorProperties") private Map hibernateVendorProperties; /** * 创建 orders 数据源 */ @Bean(name = "ordersDataSource") @ConfigurationProperties(prefix = "spring.datasource.orders") @Primary // 需要特殊添加,否则初始化会有问题 public DataSource dataSource() { return DataSourceBuilder.create().build(); } /** * 创建 LocalContainerEntityManagerFactoryBean */ @Bean(name = DBConstants.ENTITY_MANAGER_FACTORY_ORDERS) public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder) { return builder .dataSource(this.dataSource()) // 数据源 .properties(hibernateVendorProperties) // 获取并注入 Hibernate Vendor 相关配置 .packages("cn.iocoder.springboot.lab17.dynamicdatasource.dataobject") // 数据库实体 entity 所在包 .persistenceUnit("ordersPersistenceUnit") // 设置持久单元的名字,需要唯一 .build(); } /** * 创建 PlatformTransactionManager */ @Bean(name = DBConstants.TX_MANAGER_ORDERS) public PlatformTransactionManager transactionManager(EntityManagerFactoryBuilder builder) { return new JpaTransactionManager(entityManagerFactory(builder).getObject()); } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-springdatajpa/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/config/JpaUsersConfig.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.config; import cn.iocoder.springboot.lab17.dynamicdatasource.constant.DBConstants; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.transaction.PlatformTransactionManager; import javax.annotation.Resource; import javax.sql.DataSource; import java.util.Map; @Configuration @EnableJpaRepositories( entityManagerFactoryRef = DBConstants.ENTITY_MANAGER_FACTORY_USERS, transactionManagerRef = DBConstants.TX_MANAGER_USERS, basePackages = {"cn.iocoder.springboot.lab17.dynamicdatasource.repository.users"}) // 设置 Repository 接口所在包 public class JpaUsersConfig { @Resource(name = "hibernateVendorProperties") private Map hibernateVendorProperties; /** * 创建 users 数据源 */ @Bean(name = "usersDataSource") @ConfigurationProperties(prefix = "spring.datasource.users") public DataSource dataSource() { return DataSourceBuilder.create().build(); } /** * 创建 LocalContainerEntityManagerFactoryBean */ @Bean(name = DBConstants.ENTITY_MANAGER_FACTORY_USERS) public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder) { return builder .dataSource(this.dataSource()) // 数据源 .properties(hibernateVendorProperties) // 获取并注入 Hibernate Vendor 相关配置 .packages("cn.iocoder.springboot.lab17.dynamicdatasource.dataobject") // 数据库实体 entity 所在包 .persistenceUnit("usersPersistenceUnit") // 设置持久单元的名字,需要唯一 .build(); } /** * 创建 PlatformTransactionManager */ @Bean(name = DBConstants.TX_MANAGER_USERS) public PlatformTransactionManager transactionManager(EntityManagerFactoryBuilder builder) { return new JpaTransactionManager(entityManagerFactory(builder).getObject()); } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-springdatajpa/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/constant/DBConstants.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.constant; /** * 数据库枚举类 */ public class DBConstants { /** * 事务管理器 - 订单库 */ public static final String TX_MANAGER_ORDERS = "ordersTransactionManager"; /** * 事务管理器 - 用户库 */ public static final String TX_MANAGER_USERS = "usersTransactionManager"; /** * 实体管理器工厂 - 订单库 */ public static final String ENTITY_MANAGER_FACTORY_ORDERS = "ordersEntityManagerFactory"; /** * 实体管理器工厂 - 用户库 */ public static final String ENTITY_MANAGER_FACTORY_USERS = "usersEntityManagerFactory"; } ================================================ FILE: lab-17/lab-17-dynamic-datasource-springdatajpa/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/dataobject/OrderDO.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.dataobject; import javax.persistence.*; /** * 订单 DO */ @Entity @Table(name = "orders") public class OrderDO { /** * 订单编号 */ @Id @GeneratedValue(strategy = GenerationType.IDENTITY, // strategy 设置使用数据库主键自增策略; generator = "JDBC") // generator 设置插入完成后,查询最后生成的 ID 填充到该属性中。 private Integer id; /** * 用户编号 */ @Column(name = "user_id") private Integer userId; public Integer getId() { return id; } public OrderDO setId(Integer id) { this.id = id; return this; } public Integer getUserId() { return userId; } public OrderDO setUserId(Integer userId) { this.userId = userId; return this; } @Override public String toString() { return "OrderDO{" + "id=" + id + ", userId=" + userId + '}'; } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-springdatajpa/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/dataobject/UserDO.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.dataobject; import javax.persistence.*; /** * 用户 DO */ @Entity @Table(name = "users") public class UserDO { /** * 用户编号 */ @Id @GeneratedValue(strategy = GenerationType.IDENTITY, // strategy 设置使用数据库主键自增策略; generator = "JDBC") // generator 设置插入完成后,查询最后生成的 ID 填充到该属性中。 private Integer id; /** * 账号 */ private String username; public Integer getId() { return id; } public UserDO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserDO setUsername(String username) { this.username = username; return this; } @Override public String toString() { return "UserDO{" + "id=" + id + ", username='" + username + '\'' + '}'; } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-springdatajpa/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/repository/orders/OrderRepository.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.repository.orders; import cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO; import org.springframework.data.repository.CrudRepository; public interface OrderRepository extends CrudRepository { } ================================================ FILE: lab-17/lab-17-dynamic-datasource-springdatajpa/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/repository/users/UserRepository.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.repository.users; import cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.UserDO; import org.springframework.data.repository.CrudRepository; public interface UserRepository extends CrudRepository { } ================================================ FILE: lab-17/lab-17-dynamic-datasource-springdatajpa/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/service/OrderService.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.service; import cn.iocoder.springboot.lab17.dynamicdatasource.constant.DBConstants; import cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO; import cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.UserDO; import cn.iocoder.springboot.lab17.dynamicdatasource.repository.orders.OrderRepository; import cn.iocoder.springboot.lab17.dynamicdatasource.repository.users.UserRepository; import org.springframework.aop.framework.AopContext; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @Service public class OrderService { @Autowired private OrderRepository orderRepository; @Autowired private UserRepository userRepository; private OrderService self() { return (OrderService) AopContext.currentProxy(); } public void method01() { // 查询订单 OrderDO order = orderRepository.findById(1).orElse(null); System.out.println(order); // 查询用户 UserDO user = userRepository.findById(1).orElse(null); System.out.println(user); } @Transactional // 报错,找不到事务管理器 public void method02() { // 查询订单 OrderDO order = orderRepository.findById(1).orElse(null); System.out.println(order); // 查询用户 UserDO user = userRepository.findById(1).orElse(null); System.out.println(user); } public void method03() { // 查询订单 self().method031(); // 查询用户 self().method032(); } @Transactional(transactionManager = DBConstants.TX_MANAGER_ORDERS) public void method031() { OrderDO order = orderRepository.findById(1).orElse(null); System.out.println(order); } @Transactional(transactionManager = DBConstants.TX_MANAGER_USERS) public void method032() { UserDO user = userRepository.findById(1).orElse(null); System.out.println(user); } @Transactional(transactionManager = DBConstants.TX_MANAGER_ORDERS) public void method05() { // 查询订单 OrderDO order = orderRepository.findById(1).orElse(null); System.out.println(order); // 查询用户 self().method052(); } @Transactional(transactionManager = DBConstants.TX_MANAGER_USERS, propagation = Propagation.REQUIRES_NEW) public void method052() { UserDO user = userRepository.findById(1).orElse(null); System.out.println(user); } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-springdatajpa/src/main/resources/application.yaml ================================================ spring: # datasource 数据源配置内容 datasource: # 订单数据源配置 orders: jdbc-url: jdbc:mysql://127.0.0.1:3306/test_orders?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: # 用户数据源配置 users: jdbc-url: jdbc:mysql://127.0.0.1:3306/test_users?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: jpa: show-sql: true # 打印 SQL 。生产环境,建议关闭 # Hibernate 配置内容,对应 HibernateProperties 类 hibernate: ddl-auto: none ================================================ FILE: lab-17/lab-17-dynamic-datasource-springdatajpa/src/main/resources/sql/db.sql ================================================ CREATE TABLE `users` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号', `username` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号', PRIMARY KEY (`id`), UNIQUE KEY `idx_username` (`username`) ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; CREATE TABLE `orders` ( `id` int(11) DEFAULT NULL COMMENT '订单编号', `user_id` int(16) DEFAULT NULL COMMENT '用户编号' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='订单表'; ================================================ FILE: lab-17/lab-17-dynamic-datasource-springdatajpa/src/test/java/cn/iocoder/springboot/lab17/dynamicdatasource/package-info.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource; ================================================ FILE: lab-17/lab-17-dynamic-datasource-springdatajpa/src/test/java/cn/iocoder/springboot/lab17/dynamicdatasource/repository/orders/OrderRepositoryTest.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.repository.orders; import cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest public class OrderRepositoryTest { @Autowired private OrderRepository orderRepository; @Test public void testSelectById() { OrderDO order = orderRepository.findById(1) .orElse(null); // 为空,则返回 null System.out.println(order); } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-springdatajpa/src/test/java/cn/iocoder/springboot/lab17/dynamicdatasource/repository/users/UserRepositoryTest.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.repository.users; import cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.UserDO; import cn.iocoder.springboot.lab17.dynamicdatasource.repository.users.UserRepository; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest public class UserRepositoryTest { @Autowired private UserRepository userRepository; @Test public void testSelectById() { UserDO user = userRepository.findById(1) .orElse(null); // 为空,则返回 null System.out.println(user); } } ================================================ FILE: lab-17/lab-17-dynamic-datasource-springdatajpa/src/test/java/cn/iocoder/springboot/lab17/dynamicdatasource/service/OrderServiceTest.java ================================================ package cn.iocoder.springboot.lab17.dynamicdatasource.service; import cn.iocoder.springboot.lab17.dynamicdatasource.Application; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class OrderServiceTest { @Autowired private OrderService orderService; @Test public void testMethod01() { orderService.method01(); } @Test public void testMethod02() { orderService.method02(); } @Test public void testMethod03() { orderService.method03(); } @Test public void testMethod05() { orderService.method05(); } } ================================================ FILE: lab-17/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-17 pom lab-17-dynamic-datasource-baomidou-01 lab-17-dynamic-datasource-baomidou-02 lab-17-dynamic-datasource-mybatis lab-17-dynamic-datasource-springdatajpa lab-17-dynamic-datasource-jdbctemplate lab-17-dynamic-datasource-sharding-jdbc-01 lab-17-dynamic-datasource-sharding-jdbc-02 ================================================ FILE: lab-17/《芋道 Spring Boot 多数据源(读写分离)入门》.md ================================================ ================================================ FILE: lab-18/lab-18-sharding-datasource-01/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE 4.0.0 lab-18-sharding-datasource-01 org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 5.1.48 org.mybatis.spring.boot mybatis-spring-boot-starter 2.1.1 org.apache.shardingsphere sharding-jdbc-spring-boot-starter 4.0.0-RC2 org.springframework spring-aspects org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-18/lab-18-sharding-datasource-01/src/main/java/cn/iocoder/springboot/lab18/shardingdatasource/Application.java ================================================ package cn.iocoder.springboot.lab18.shardingdatasource; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @MapperScan(basePackages = "cn.iocoder.springboot.lab18.shardingdatasource.mapper") public class Application { } ================================================ FILE: lab-18/lab-18-sharding-datasource-01/src/main/java/cn/iocoder/springboot/lab18/shardingdatasource/dataobject/OrderConfigDO.java ================================================ package cn.iocoder.springboot.lab18.shardingdatasource.dataobject; /** * 订单配置 DO */ public class OrderConfigDO { /** * 编号 */ private Integer id; /** * 支付超时时间 * * 单位:分钟 */ private Integer payTimeout; public Integer getId() { return id; } public OrderConfigDO setId(Integer id) { this.id = id; return this; } public Integer getPayTimeout() { return payTimeout; } public OrderConfigDO setPayTimeout(Integer payTimeout) { this.payTimeout = payTimeout; return this; } } ================================================ FILE: lab-18/lab-18-sharding-datasource-01/src/main/java/cn/iocoder/springboot/lab18/shardingdatasource/dataobject/OrderDO.java ================================================ package cn.iocoder.springboot.lab18.shardingdatasource.dataobject; /** * 订单 DO */ public class OrderDO { /** * 订单编号 */ private Long id; /** * 用户编号 */ private Integer userId; public Long getId() { return id; } public OrderDO setId(Long id) { this.id = id; return this; } public Integer getUserId() { return userId; } public OrderDO setUserId(Integer userId) { this.userId = userId; return this; } @Override public String toString() { return "OrderDO{" + "id=" + id + ", userId=" + userId + '}'; } } ================================================ FILE: lab-18/lab-18-sharding-datasource-01/src/main/java/cn/iocoder/springboot/lab18/shardingdatasource/mapper/OrderConfigMapper.java ================================================ package cn.iocoder.springboot.lab18.shardingdatasource.mapper; import cn.iocoder.springboot.lab18.shardingdatasource.dataobject.OrderConfigDO; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; @Repository public interface OrderConfigMapper { OrderConfigDO selectById(@Param("id") Integer id); } ================================================ FILE: lab-18/lab-18-sharding-datasource-01/src/main/java/cn/iocoder/springboot/lab18/shardingdatasource/mapper/OrderMapper.java ================================================ package cn.iocoder.springboot.lab18.shardingdatasource.mapper; import cn.iocoder.springboot.lab18.shardingdatasource.dataobject.OrderDO; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; import java.util.List; @Repository public interface OrderMapper { OrderDO selectById(@Param("id") Integer id); List selectListByUserId(@Param("userId") Integer userId); void insert(OrderDO order); } ================================================ FILE: lab-18/lab-18-sharding-datasource-01/src/main/resources/application.yaml ================================================ spring: # ShardingSphere 配置项 shardingsphere: datasource: # 所有数据源的名字 names: ds-orders-0, ds-orders-1 # 订单 orders 数据源配置 00 ds-orders-0: type: com.zaxxer.hikari.HikariDataSource # 使用 Hikari 数据库连接池 driver-class-name: com.mysql.jdbc.Driver jdbc-url: jdbc:mysql://127.0.0.1:3306/lab18_orders_0?useSSL=false&useUnicode=true&characterEncoding=UTF-8 username: root password: # 订单 orders 数据源配置 01 ds-orders-1: type: com.zaxxer.hikari.HikariDataSource # 使用 Hikari 数据库连接池 driver-class-name: com.mysql.jdbc.Driver jdbc-url: jdbc:mysql://127.0.0.1:3306/lab18_orders_1?useSSL=false&useUnicode=true&characterEncoding=UTF-8 username: root password: # 分片规则 sharding: tables: # orders 表配置 orders: # actualDataNodes: ds-orders-$->{0..1}.orders_$->{0..4} # 映射到 ds-orders 数据源的 orders 表 # 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 actualDataNodes: ds-orders-0.orders_$->{[0,2,4,6]}, ds-orders-1.orders_$->{[1,3,5,7]} # 映射到 ds-orders-0 和 ds-orders-1 数据源的 orders 表们 key-generator: # 主键生成策略 column: id type: SNOWFLAKE database-strategy: inline: algorithm-expression: ds-orders-$->{user_id % 2} sharding-column: user_id table-strategy: inline: algorithm-expression: orders_$->{user_id % 8} sharding-column: user_id # order_config 表配置 order_config: actualDataNodes: ds-orders-0.order_config # 仅映射到 ds-orders-0 数据源的 order_config 表 # 拓展属性配置 props: sql: show: true # 打印 SQL # mybatis 配置内容 mybatis: config-location: classpath:mybatis-config.xml # 配置 MyBatis 配置文件路径 mapper-locations: classpath:mapper/*.xml # 配置 Mapper XML 地址 type-aliases-package: cn.iocoder.springboot.lab18.shardingdatasource.dataobject # 配置数据库实体包路径 ================================================ FILE: lab-18/lab-18-sharding-datasource-01/src/main/resources/mapper/OrderConfigMapper.xml ================================================ id, pay_timeout ================================================ FILE: lab-18/lab-18-sharding-datasource-01/src/main/resources/mapper/OrderMapper.xml ================================================ id, user_id INSERT INTO orders ( user_id ) VALUES ( #{userId} ) ================================================ FILE: lab-18/lab-18-sharding-datasource-01/src/main/resources/mybatis-config.xml ================================================ ================================================ FILE: lab-18/lab-18-sharding-datasource-01/src/main/resources/sql/lab18_orders_0.sql ================================================ SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for orders_0 -- ---------------------------- DROP TABLE IF EXISTS `orders_0`; CREATE TABLE `orders_0` ( `id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT '订单编号', `user_id` int(16) DEFAULT NULL COMMENT '用户编号', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='订单表'; -- ---------------------------- -- Table structure for orders_2 -- ---------------------------- DROP TABLE IF EXISTS `orders_2`; CREATE TABLE `orders_2` ( `id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT '订单编号', `user_id` int(16) DEFAULT NULL COMMENT '用户编号', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='订单表'; -- ---------------------------- -- Table structure for orders_4 -- ---------------------------- DROP TABLE IF EXISTS `orders_4`; CREATE TABLE `orders_4` ( `id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT '订单编号', `user_id` int(16) DEFAULT NULL COMMENT '用户编号', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='订单表'; -- ---------------------------- -- Table structure for orders_6 -- ---------------------------- DROP TABLE IF EXISTS `orders_6`; CREATE TABLE `orders_6` ( `id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT '订单编号', `user_id` int(16) DEFAULT NULL COMMENT '用户编号', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='订单表'; -- ---------------------------- -- Table structure for order_config -- ---------------------------- DROP TABLE IF EXISTS `order_config`; CREATE TABLE `order_config` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '编号', `pay_timeout` int(11) DEFAULT NULL COMMENT '支付超时时间;单位:分钟', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4; SET FOREIGN_KEY_CHECKS = 1; ================================================ FILE: lab-18/lab-18-sharding-datasource-01/src/main/resources/sql/lab18_orders_1.sql ================================================ SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for orders_1 -- ---------------------------- DROP TABLE IF EXISTS `orders_1`; CREATE TABLE `orders_1` ( `id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT '订单编号', `user_id` int(16) DEFAULT NULL COMMENT '用户编号', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=400675304294580226 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='订单表'; -- ---------------------------- -- Table structure for orders_3 -- ---------------------------- DROP TABLE IF EXISTS `orders_3`; CREATE TABLE `orders_3` ( `id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT '订单编号', `user_id` int(16) DEFAULT NULL COMMENT '用户编号', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='订单表'; -- ---------------------------- -- Table structure for orders_5 -- ---------------------------- DROP TABLE IF EXISTS `orders_5`; CREATE TABLE `orders_5` ( `id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT '订单编号', `user_id` int(16) DEFAULT NULL COMMENT '用户编号', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='订单表'; -- ---------------------------- -- Table structure for orders_7 -- ---------------------------- DROP TABLE IF EXISTS `orders_7`; CREATE TABLE `orders_7` ( `id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT '订单编号', `user_id` int(16) DEFAULT NULL COMMENT '用户编号', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='订单表'; SET FOREIGN_KEY_CHECKS = 1; ================================================ FILE: lab-18/lab-18-sharding-datasource-01/src/test/java/cn/iocoder/springboot/lab18/shardingdatasource/mapper/OrderConfigMapperTest.java ================================================ package cn.iocoder.springboot.lab18.shardingdatasource.mapper; import cn.iocoder.springboot.lab18.shardingdatasource.Application; import cn.iocoder.springboot.lab18.shardingdatasource.dataobject.OrderConfigDO; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class OrderConfigMapperTest { @Autowired private OrderConfigMapper orderConfigMapper; @Test public void testSelectById() { OrderConfigDO orderConfig = orderConfigMapper.selectById(1); System.out.println(orderConfig); } } ================================================ FILE: lab-18/lab-18-sharding-datasource-01/src/test/java/cn/iocoder/springboot/lab18/shardingdatasource/mapper/OrderMapperTest.java ================================================ package cn.iocoder.springboot.lab18.shardingdatasource.mapper; import cn.iocoder.springboot.lab18.shardingdatasource.Application; import cn.iocoder.springboot.lab18.shardingdatasource.dataobject.OrderDO; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.List; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class OrderMapperTest { @Autowired private OrderMapper orderMapper; @Test public void testSelectById() { OrderDO order = orderMapper.selectById(1); System.out.println(order); } @Test public void testSelectListByUserId() { List orders = orderMapper.selectListByUserId(1); System.out.println(orders.size()); } @Test public void testInsert() { OrderDO order = new OrderDO(); order.setUserId(1); orderMapper.insert(order); } } ================================================ FILE: lab-18/lab-18-sharding-datasource-02/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE 4.0.0 lab-18-sharding-datasource-02 org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 5.1.48 org.apache.shardingsphere sharding-jdbc-spring-boot-starter 4.0.0-RC2 com.baomidou mybatis-plus-boot-starter 3.2.0 org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-18/lab-18-sharding-datasource-02/src/main/java/cn/iocoder/springboot/lab18/shardingdatasource/Application.java ================================================ package cn.iocoder.springboot.lab18.shardingdatasource; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @MapperScan(basePackages = "cn.iocoder.springboot.lab18.shardingdatasource.mapper") public class Application { } ================================================ FILE: lab-18/lab-18-sharding-datasource-02/src/main/java/cn/iocoder/springboot/lab18/shardingdatasource/dataobject/OrderDO.java ================================================ package cn.iocoder.springboot.lab18.shardingdatasource.dataobject; import com.baomidou.mybatisplus.annotation.TableName; /** * 订单 DO */ @TableName(value = "orders") public class OrderDO { /** * 订单编号 */ private Long id; /** * 用户编号 */ private Integer userId; public Long getId() { return id; } public OrderDO setId(Long id) { this.id = id; return this; } public Integer getUserId() { return userId; } public OrderDO setUserId(Integer userId) { this.userId = userId; return this; } @Override public String toString() { return "OrderDO{" + "id=" + id + ", userId=" + userId + '}'; } } ================================================ FILE: lab-18/lab-18-sharding-datasource-02/src/main/java/cn/iocoder/springboot/lab18/shardingdatasource/mapper/OrderMapper.java ================================================ package cn.iocoder.springboot.lab18.shardingdatasource.mapper; import cn.iocoder.springboot.lab18.shardingdatasource.dataobject.OrderDO; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.springframework.stereotype.Repository; @Repository public interface OrderMapper extends BaseMapper { } ================================================ FILE: lab-18/lab-18-sharding-datasource-02/src/main/java/cn/iocoder/springboot/lab18/shardingdatasource/service/OrderService.java ================================================ package cn.iocoder.springboot.lab18.shardingdatasource.service; import cn.iocoder.springboot.lab18.shardingdatasource.dataobject.OrderDO; import cn.iocoder.springboot.lab18.shardingdatasource.mapper.OrderMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class OrderService { @Autowired private OrderMapper orderMapper; @Transactional public void add(OrderDO order) { // 这里先假模假样的读取一下。读取从库 OrderDO exists = orderMapper.selectById(1); System.out.println(exists); // 插入订单 orderMapper.insert(order); // 这里先假模假样的读取一下。读取主库 exists = orderMapper.selectById(1); System.out.println(exists); } public OrderDO findById(Integer id) { return orderMapper.selectById(id); } } ================================================ FILE: lab-18/lab-18-sharding-datasource-02/src/main/resources/application.yaml ================================================ spring: # ShardingSphere 配置项 shardingsphere: # 数据源配置 datasource: # 所有数据源的名字 names: ds-master, ds-slave-1, ds-slave-2 # 订单 orders 主库的数据源配置 ds-master: type: com.zaxxer.hikari.HikariDataSource # 使用 Hikari 数据库连接池 driver-class-name: com.mysql.jdbc.Driver jdbc-url: jdbc:mysql://127.0.0.1:3306/test_orders?useSSL=false&useUnicode=true&characterEncoding=UTF-8 username: root password: # 订单 orders 从库数据源配置 ds-slave-1: type: com.zaxxer.hikari.HikariDataSource # 使用 Hikari 数据库连接池 driver-class-name: com.mysql.jdbc.Driver jdbc-url: jdbc:mysql://127.0.0.1:3306/test_orders_01?useSSL=false&useUnicode=true&characterEncoding=UTF-8 username: root password: # 订单 orders 从库数据源配置 ds-slave-2: type: com.zaxxer.hikari.HikariDataSource # 使用 Hikari 数据库连接池 driver-class-name: com.mysql.jdbc.Driver jdbc-url: jdbc:mysql://127.0.0.1:3306/test_orders_02?useSSL=false&useUnicode=true&characterEncoding=UTF-8 username: root password: # 读写分离配置,对应 YamlMasterSlaveRuleConfiguration 配置类 masterslave: name: ms # 名字,任意,需要保证唯一 master-data-source-name: ds-master # 主库数据源 slave-data-source-names: ds-slave-1, ds-slave-2 # 从库数据源 # 拓展属性配置 props: sql: show: true # 打印 SQL # mybatis-plus 配置内容 mybatis-plus: configuration: map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。 global-config: db-config: id-type: none # 虽然 MyBatis Plus 也提供 ID 生成策略,但是还是使用 Sharding-JDBC 的 logic-delete-value: 1 # 逻辑已删除值(默认为 1) logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) mapper-locations: classpath*:mapper/*.xml type-aliases-package: cn.iocoder.springboot.lab18.shardingdatasource.dataobject ================================================ FILE: lab-18/lab-18-sharding-datasource-02/src/test/java/cn/iocoder/springboot/lab18/shardingdatasource/mapper/OrderMapperTest.java ================================================ package cn.iocoder.springboot.lab18.shardingdatasource.mapper; import cn.iocoder.springboot.lab18.shardingdatasource.Application; import cn.iocoder.springboot.lab18.shardingdatasource.dataobject.OrderDO; import org.apache.shardingsphere.api.hint.HintManager; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class OrderMapperTest { @Autowired private OrderMapper orderMapper; @Test public void testSelectById() { // 测试从库的负载均衡 for (int i = 0; i < 2; i++) { OrderDO order = orderMapper.selectById(1); System.out.println(order); } } @Test public void testSelectById02() { // 测试强制访问主库 try (HintManager hintManager = HintManager.getInstance()) { // 设置强制访问主库 hintManager.setMasterRouteOnly(); // 执行查询 OrderDO order = orderMapper.selectById(1); System.out.println(order); } } @Test public void testInsert() { // 插入 OrderDO order = new OrderDO(); order.setUserId(10); orderMapper.insert(order); } } ================================================ FILE: lab-18/lab-18-sharding-datasource-02/src/test/java/cn/iocoder/springboot/lab18/shardingdatasource/service/OrderServiceTest.java ================================================ package cn.iocoder.springboot.lab18.shardingdatasource.service; import cn.iocoder.springboot.lab18.shardingdatasource.Application; import cn.iocoder.springboot.lab18.shardingdatasource.dataobject.OrderDO; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class OrderServiceTest { @Autowired private OrderService orderService; @Test public void testAdd() { OrderDO order = new OrderDO(); order.setUserId(20); orderService.add(order); } @Test public void testFindById() { OrderDO order = orderService.findById(1); System.out.println(order); } } ================================================ FILE: lab-18/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-18 pom lab-18-sharding-datasource-01 lab-18-sharding-datasource-02 ================================================ FILE: lab-18/《芋道 Spring Boot 分库分表入门》.md ================================================ ================================================ FILE: lab-19/lab-19-datasource-pool-druid-multiple/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE 4.0.0 lab-19-datasource-pool-druid-multiple org.springframework.boot spring-boot-starter-jdbc com.alibaba druid-spring-boot-starter 1.1.21 mysql mysql-connector-java 5.1.48 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-19/lab-19-datasource-pool-druid-multiple/src/main/java/cn/iocoder/springboot/lab19/datasourcepool/Application.java ================================================ package cn.iocoder.springboot.lab19.datasourcepool; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import javax.annotation.Resource; import javax.sql.DataSource; @SpringBootApplication public class Application implements CommandLineRunner { private Logger logger = LoggerFactory.getLogger(Application.class); @Resource(name = "ordersDataSource") private DataSource ordersDataSource; @Resource(name = "usersDataSource") private DataSource usersDataSource; public static void main(String[] args) { // 启动 Spring Boot 应用 SpringApplication.run(Application.class, args); } @Override public void run(String... args) { // orders 数据源 logger.info("[run][获得数据源:{}]", ordersDataSource.getClass()); // users 数据源 logger.info("[run][获得数据源:{}]", usersDataSource.getClass()); } } ================================================ FILE: lab-19/lab-19-datasource-pool-druid-multiple/src/main/java/cn/iocoder/springboot/lab19/datasourcepool/config/DataSourceConfig.java ================================================ package cn.iocoder.springboot.lab19.datasourcepool.config; import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import javax.sql.DataSource; @Configuration public class DataSourceConfig { /** * 创建 orders 数据源 */ @Primary @Bean(name = "ordersDataSource") @ConfigurationProperties(prefix = "spring.datasource.orders") // 读取 spring.datasource.orders 配置到 HikariDataSource 对象 public DataSource ordersDataSource() { return DruidDataSourceBuilder.create().build(); } /** * 创建 users 数据源 */ @Bean(name = "usersDataSource") @ConfigurationProperties(prefix = "spring.datasource.users") public DataSource usersDataSource() { return DruidDataSourceBuilder.create().build(); } } ================================================ FILE: lab-19/lab-19-datasource-pool-druid-multiple/src/main/resources/application.yaml ================================================ spring: # datasource 数据源配置内容 datasource: # 订单数据源配置 orders: url: jdbc:mysql://127.0.0.1:3306/test_orders?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: type: com.alibaba.druid.pool.DruidDataSource # 设置类型为 DruidDataSource # Druid 自定义配置,对应 DruidDataSource 中的 setting 方法的属性 min-idle: 0 # 池中维护的最小空闲连接数,默认为 0 个。 max-active: 20 # 池中最大连接数,包括闲置和使用中的连接,默认为 8 个。 # 用户数据源配置 users: url: jdbc:mysql://127.0.0.1:3306/test_users?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: type: com.alibaba.druid.pool.DruidDataSource # 设置类型为 DruidDataSource # Druid 自定义配置,对应 DruidDataSource 中的 setting 方法的属性 min-idle: 0 # 池中维护的最小空闲连接数,默认为 0 个。 max-active: 20 # 池中最大连接数,包括闲置和使用中的连接,默认为 8 个。 # Druid 自定已配置 druid: # 过滤器配置 filter: stat: # 配置 StatFilter ,对应文档 https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_StatFilter log-slow-sql: true # 开启慢查询记录 slow-sql-millis: 5000 # 慢 SQL 的标准,单位:毫秒 # StatViewServlet 配置 stat-view-servlet: # 配置 StatViewServlet ,对应文档 https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_StatViewServlet%E9%85%8D%E7%BD%AE enabled: true # 是否开启 StatViewServlet login-username: yudaoyuanma # 账号 login-password: javaniubi # 密码 ================================================ FILE: lab-19/lab-19-datasource-pool-druid-single/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE 4.0.0 lab-19-datasource-pool-druid-single org.springframework.boot spring-boot-starter-jdbc com.alibaba druid-spring-boot-starter 1.1.21 mysql mysql-connector-java 5.1.48 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-19/lab-19-datasource-pool-druid-single/src/main/java/cn/iocoder/springboot/lab19/datasourcepool/Application.java ================================================ package cn.iocoder.springboot.lab19.datasourcepool; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import javax.sql.DataSource; @SpringBootApplication public class Application implements CommandLineRunner { private Logger logger = LoggerFactory.getLogger(Application.class); @Autowired private DataSource dataSource; public static void main(String[] args) { // 启动 Spring Boot 应用 SpringApplication.run(Application.class, args); } @Override public void run(String... args) { logger.info("[run][获得数据源:{}]", dataSource.getClass()); } } ================================================ FILE: lab-19/lab-19-datasource-pool-druid-single/src/main/java/cn/iocoder/springboot/lab19/datasourcepool/controller/DruidStatController.java ================================================ package cn.iocoder.springboot.lab19.datasourcepool.controller; import com.alibaba.druid.stat.DruidStatManagerFacade; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class DruidStatController { @GetMapping("/monitor/druid/stat") @Deprecated public Object druidStat(){ // `DruidStatManagerFacade#getDataSourceStatDataList()` 方法,可以获取所有数据源的监控数据。 // 除此之外,DruidStatManagerFacade 还提供了一些其他方法,你可以按需选择使用。 return DruidStatManagerFacade.getInstance().getDataSourceStatDataList(); } } ================================================ FILE: lab-19/lab-19-datasource-pool-druid-single/src/main/resources/application.yaml ================================================ spring: # datasource 数据源配置内容,对应 DataSourceProperties 配置属性类 datasource: url: jdbc:mysql://127.0.0.1:3306/test?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root # 数据库账号 password: # 数据库密码 type: com.alibaba.druid.pool.DruidDataSource # 设置类型为 DruidDataSource # Druid 自定义配置,对应 DruidDataSource 中的 setting 方法的属性 druid: min-idle: 0 # 池中维护的最小空闲连接数,默认为 0 个。 max-active: 20 # 池中最大连接数,包括闲置和使用中的连接,默认为 8 个。 filter: stat: # 配置 StatFilter ,对应文档 https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_StatFilter log-slow-sql: true # 开启慢查询记录 slow-sql-millis: 5000 # 慢 SQL 的标准,单位:毫秒 stat-view-servlet: # 配置 StatViewServlet ,对应文档 https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_StatViewServlet%E9%85%8D%E7%BD%AE enabled: true # 是否开启 StatViewServlet login-username: yudaoyuanma # 账号 login-password: javaniubi # 密码 ================================================ FILE: lab-19/lab-19-datasource-pool-hikaricp-multiple/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE 4.0.0 lab-19-datasource-pool-hikaricp-multiple org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 5.1.48 org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-19/lab-19-datasource-pool-hikaricp-multiple/src/main/java/cn/iocoder/springboot/lab19/datasourcepool/Application.java ================================================ package cn.iocoder.springboot.lab19.datasourcepool; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import javax.annotation.Resource; import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException; @SpringBootApplication public class Application implements CommandLineRunner { private Logger logger = LoggerFactory.getLogger(Application.class); @Resource(name = "ordersDataSource") private DataSource ordersDataSource; @Resource(name = "usersDataSource") private DataSource usersDataSource; public static void main(String[] args) { // 启动 Spring Boot 应用 SpringApplication.run(Application.class, args); } @Override public void run(String... args) { // orders 数据源 try (Connection conn = ordersDataSource.getConnection()) { // 这里,可以做点什么 logger.info("[run][ordersDataSource 获得连接:{}]", conn); } catch (SQLException e) { throw new RuntimeException(e); } // users 数据源 try (Connection conn = usersDataSource.getConnection()) { // 这里,可以做点什么 logger.info("[run][usersDataSource 获得连接:{}]", conn); } catch (SQLException e) { throw new RuntimeException(e); } } } ================================================ FILE: lab-19/lab-19-datasource-pool-hikaricp-multiple/src/main/java/cn/iocoder/springboot/lab19/datasourcepool/config/DataSourceConfig.java ================================================ package cn.iocoder.springboot.lab19.datasourcepool.config; import com.zaxxer.hikari.HikariDataSource; import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.util.StringUtils; import javax.sql.DataSource; @Configuration public class DataSourceConfig { /** * 创建 orders 数据源的配置对象 */ @Primary @Bean(name = "ordersDataSourceProperties") @ConfigurationProperties(prefix = "spring.datasource.orders") // 读取 spring.datasource.orders 配置到 DataSourceProperties 对象 public DataSourceProperties ordersDataSourceProperties() { return new DataSourceProperties(); } /** * 创建 orders 数据源 */ @Bean(name = "ordersDataSource") @ConfigurationProperties(prefix = "spring.datasource.orders.hikari") // 读取 spring.datasource.orders 配置到 HikariDataSource 对象 public DataSource ordersDataSource() { // 获得 DataSourceProperties 对象 DataSourceProperties properties = this.ordersDataSourceProperties(); // 创建 HikariDataSource 对象 return createHikariDataSource(properties); } /** * 创建 users 数据源的配置对象 */ @Bean(name = "usersDataSourceProperties") @ConfigurationProperties(prefix = "spring.datasource.users") // 读取 spring.datasource.users 配置到 DataSourceProperties 对象 public DataSourceProperties usersDataSourceProperties() { return new DataSourceProperties(); } /** * 创建 users 数据源 */ @Bean(name = "usersDataSource") @ConfigurationProperties(prefix = "spring.datasource.users.hikari") public DataSource usersDataSource() { // 获得 DataSourceProperties 对象 DataSourceProperties properties = this.usersDataSourceProperties(); // 创建 HikariDataSource 对象 return createHikariDataSource(properties); } private static HikariDataSource createHikariDataSource(DataSourceProperties properties) { // 创建 HikariDataSource 对象 HikariDataSource dataSource = properties.initializeDataSourceBuilder().type(HikariDataSource.class).build(); // 设置线程池名 if (StringUtils.hasText(properties.getName())) { dataSource.setPoolName(properties.getName()); } return dataSource; } // /** // * 创建 orders 数据源 // */ // @Bean(name = "ordersDataSource") // @ConfigurationProperties(prefix = "spring.datasource.orders") // public DataSource ordersDataSource() { // return DataSourceBuilder.create().build(); // } } ================================================ FILE: lab-19/lab-19-datasource-pool-hikaricp-multiple/src/main/resources/application.yaml ================================================ spring: # datasource 数据源配置内容 datasource: # 订单数据源配置 orders: url: jdbc:mysql://127.0.0.1:3306/test_orders?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: # HikariCP 自定义配置,对应 HikariConfig 配置属性类 hikari: minimum-idle: 20 # 池中维护的最小空闲连接数,默认为 10 个。 maximum-pool-size: 20 # 池中最大连接数,包括闲置和使用中的连接,默认为 10 个。 # 用户数据源配置 users: url: jdbc:mysql://127.0.0.1:3306/test_users?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: # HikariCP 自定义配置,对应 HikariConfig 配置属性类 hikari: minimum-idle: 15 # 池中维护的最小空闲连接数,默认为 10 个。 maximum-pool-size: 15 # 池中最大连接数,包括闲置和使用中的连接,默认为 10 个。 ================================================ FILE: lab-19/lab-19-datasource-pool-hikaricp-single/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE 4.0.0 lab-19-datasource-pool-hikaricp-single org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 5.1.48 org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-19/lab-19-datasource-pool-hikaricp-single/src/main/java/cn/iocoder/springboot/lab19/datasourcepool/Application.java ================================================ package cn.iocoder.springboot.lab19.datasourcepool; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException; @SpringBootApplication public class Application implements CommandLineRunner { private Logger logger = LoggerFactory.getLogger(Application.class); @Autowired private DataSource dataSource; public static void main(String[] args) { // 启动 Spring Boot 应用 SpringApplication.run(Application.class, args); } @Override public void run(String... args) { try (Connection conn = dataSource.getConnection()) { // 这里,可以做点什么 logger.info("[run][获得连接:{}]", conn); } catch (SQLException e) { throw new RuntimeException(e); } } } ================================================ FILE: lab-19/lab-19-datasource-pool-hikaricp-single/src/main/resources/application.yaml ================================================ spring: # datasource 数据源配置内容,对应 DataSourceProperties 配置属性类 datasource: url: jdbc:mysql://127.0.0.1:3306/test?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root # 数据库账号 password: # 数据库密码 # HikariCP 自定义配置,对应 HikariConfig 配置属性类 hikari: minimum-idle: 20 # 池中维护的最小空闲连接数,默认为 10 个。 maximum-pool-size: 20 # 池中最大连接数,包括闲置和使用中的连接,默认为 10 个。 ================================================ FILE: lab-19/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-19 pom lab-19-datasource-pool-hikaricp-single lab-19-datasource-pool-hikaricp-multiple lab-19-datasource-pool-druid-single lab-19-datasource-pool-druid-multiple ================================================ FILE: lab-19/《芋道 Spring Boot 数据库连接池入门》.md ================================================ ================================================ FILE: lab-20/lab-20-database-version-control-flyway/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE 4.0.0 lab-20-database-version-control-flyway org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 5.1.48 org.flywaydb flyway-core ================================================ FILE: lab-20/lab-20-database-version-control-flyway/src/main/java/cn/iocoder/springboot/lab20/databaseversioncontrol/Application.java ================================================ package cn.iocoder.springboot.lab20.databaseversioncontrol; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { // 启动 Spring Boot 应用 SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-20/lab-20-database-version-control-flyway/src/main/java/cn/iocoder/springboot/lab20/databaseversioncontrol/migration/ExampleFlywayCallback.java ================================================ package cn.iocoder.springboot.lab20.databaseversioncontrol.migration; import org.flywaydb.core.api.callback.Callback; import org.flywaydb.core.api.callback.Context; import org.flywaydb.core.api.callback.Event; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component public class ExampleFlywayCallback implements Callback { private Logger log = LoggerFactory.getLogger(getClass()); @Override public boolean supports(Event event, Context context) { return true; } @Override public boolean canHandleInTransaction(Event event, Context context) { return false; } @Override public void handle(Event event, Context context) { log.info("event: {}", event); } } ================================================ FILE: lab-20/lab-20-database-version-control-flyway/src/main/java/cn/iocoder/springboot/lab20/databaseversioncontrol/migration/V1_1__FixUsername.java ================================================ package cn.iocoder.springboot.lab20.databaseversioncontrol.migration; import org.flywaydb.core.api.MigrationVersion; import org.flywaydb.core.api.migration.BaseJavaMigration; import org.flywaydb.core.api.migration.Context; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowCallbackHandler; import java.sql.ResultSet; import java.sql.SQLException; public class V1_1__FixUsername extends BaseJavaMigration { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public void migrate(Context context) throws Exception { // 创建 JdbcTemplate ,方便 JDBC 操作 JdbcTemplate template = new JdbcTemplate(context.getConfiguration().getDataSource()); // 查询所有用户,如果用户名为 yudaoyuanma ,则变更成 yutou template.query("SELECT id, username, password, create_time FROM users", new RowCallbackHandler() { @Override public void processRow(ResultSet rs) throws SQLException { // 遍历返回的结果 do { String username = rs.getString("username"); if ("yudaoyuanma".equals(username)) { Integer id = rs.getInt("id"); template.update("UPDATE users SET username = ? WHERE id = ?", "yutou", id); logger.info("[migrate][更新 user({}) 的用户名({} => {})", id, username, "yutou"); } } while (rs.next()); } }); } @Override public Integer getChecksum() { return 11; // 默认返回,是 null 。 } @Override public boolean canExecuteInTransaction() { return true; // 默认返回,也是 true } @Override public MigrationVersion getVersion() { return super.getVersion(); // 默认按照约定的规则,从类名中解析获得。可以自定义 } } ================================================ FILE: lab-20/lab-20-database-version-control-flyway/src/main/resources/application.yaml ================================================ spring: # datasource 数据源配置内容,对应 DataSourceProperties 配置属性类 datasource: url: jdbc:mysql://127.0.0.1:3306/lab-20-flyway?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root # 数据库账号 password: # 数据库密码 # flyway 配置内容,对应 FlywayAutoConfiguration.FlywayConfiguration 配置项 flyway: enabled: true # 开启 Flyway 功能 cleanDisabled: true # 禁用 Flyway 所有的 drop 相关的逻辑,避免出现跑路的情况。 locations: # 迁移脚本目录 - classpath:db/migration # 配置 SQL-based 的 SQL 脚本在该目录下 - classpath:cn.iocoder.springboot.lab20.databaseversioncontrol.migration # 配置 Java-based 的 Java 文件在该目录下 check-location: false # 是否校验迁移脚本目录下。如果配置为 true ,代表需要校验。此时,如果目录下没有迁移脚本,会抛出 IllegalStateException 异常 url: jdbc:mysql://127.0.0.1:3306/lab-20-flyway?useSSL=false&useUnicode=true&characterEncoding=UTF-8 # 数据库地址 user: root # 数据库账号 password: # 数据库密码 ================================================ FILE: lab-20/lab-20-database-version-control-flyway/src/main/resources/db/migration/V1.0__INIT_DB.sql ================================================ -- 创建用户表 CREATE TABLE `users` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号', `username` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号', `password` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '密码', `create_time` datetime DEFAULT NULL COMMENT '创建时间', PRIMARY KEY (`id`), UNIQUE KEY `idx_username` (`username`) ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; -- 插入一条数据 INSERT INTO `users`(username, password, create_time) VALUES('yudaoyuanma', 'password', now()); ================================================ FILE: lab-20/lab-20-database-version-control-liquibase/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE 4.0.0 lab-20-database-version-control-liquibase org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java org.liquibase liquibase-core ================================================ FILE: lab-20/lab-20-database-version-control-liquibase/src/main/java/cn/iocoder/springboot/lab20/databaseversioncontrol/Application.java ================================================ package cn.iocoder.springboot.lab20.databaseversioncontrol; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { // 启动 Spring Boot 应用 SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-20/lab-20-database-version-control-liquibase/src/main/java/cn/iocoder/springboot/lab20/databaseversioncontrol/migration/CHANGE_SET_3_FixUsername.java ================================================ package cn.iocoder.springboot.lab20.databaseversioncontrol.migration; import liquibase.change.custom.CustomTaskChange; import liquibase.database.Database; import liquibase.database.jvm.JdbcConnection; import liquibase.exception.CustomChangeException; import liquibase.exception.SetupException; import liquibase.exception.ValidationErrors; import liquibase.resource.ResourceAccessor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.sql.PreparedStatement; import java.sql.ResultSet; public class CHANGE_SET_3_FixUsername implements CustomTaskChange { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public void execute(Database database) throws CustomChangeException { JdbcConnection connection = (JdbcConnection) database.getConnection(); try (PreparedStatement psmt = connection.prepareStatement("SELECT id, username, password, create_time FROM users")) { try (ResultSet rs = psmt.executeQuery()) { while (rs.next()) { String username = rs.getString("username"); if ("yudaoyuanma".equals(username)) { Integer id = rs.getInt("id"); // 这里,再来一刀更新操作,偷懒不写了。 logger.info("[migrate][更新 user({}) 的用户名({} => {})", id, username, "yutou"); } } } } catch (Exception e) { throw new RuntimeException(e); } } @Override public String getConfirmationMessage() { return null; } @Override public void setUp() throws SetupException { } @Override public void setFileOpener(ResourceAccessor resourceAccessor) { } @Override public ValidationErrors validate(Database database) { return null; } } ================================================ FILE: lab-20/lab-20-database-version-control-liquibase/src/main/resources/application.yaml ================================================ spring: # datasource 数据源配置内容,对应 DataSourceProperties 配置属性类 datasource: url: jdbc:mysql://127.0.0.1:3306/lab-20-liquibase?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root # 数据库账号 password: # 数据库密码 # Liquibase 配置内容,对应 LiquibaseProperties 配置项 liquibase: enabled: true # 开启 Liquibase 功能。默认为 true 。 change-log: classpath:/db/changelog/db.changelog-master.yaml # Liquibase 配置文件地址 url: jdbc:mysql://127.0.0.1:3306/lab-20-liquibase?useSSL=false&useUnicode=true&characterEncoding=UTF-8 # 数据库地址 user: root # 数据库账号 password: # 数据库密码 ================================================ FILE: lab-20/lab-20-database-version-control-liquibase/src/main/resources/db/changelog/db.changelog-master-bak.yaml ================================================ databaseChangeLog: ================================================ FILE: lab-20/lab-20-database-version-control-liquibase/src/main/resources/db/changelog/db.changelog-master.yaml ================================================ databaseChangeLog: - changeSet: # 对应一个 ChangeSet 对象 id: 0 # ChangeSet 编号 author: yunai # 作者 comments: 空 # 备注 - changeSet: # 对应一个 ChangeSet 对象 id: 1 # ChangeSet 编号 author: yunai # 作者 comments: 初始化 users 表 # 备注 changes: # 对应 Change 数组。Change 是一个接口,每种操作对应一种 Change 实现类 - createTable: # 创建表,对应 CreateTableChange 对象。 tableName: users # 表名 remarkds: 用户表 # 表注释 columns: # 对应 ColumnConfig 数组 - column: name: id # 字段名 type: int # 字段类型 autoIncrement: true # 自增 constraints: # 限制条件,对应一个 ConstraintsConfig 对象 primaryKey: true # 主键 nullable: false # 不允许空 - column: name: username type: varchar(64) constraints: nullable: false - column: name: password type: varchar(32) constraints: nullable: false - column: name: create_time type: datetime constraints: nullable: false - insert: # 插入记录,对应 InsertDataChange 对象。 tableName: users # 表名 columns: # 对应 ColumnConfig 数组 - column: name: username # 字段名 value: yudaoyuanma # 值 - column: name: password value: password - column: name: create_time value: now() - changeSet: # 对应一个 ChangeSet 对象 id: 2 # ChangeSet 编号 author: yunai # 作者 comments: 初始化 users2 表 # 备注 changes: # 对应 Change 数组。Change 是一个接口,每种操作对应一种 Change 实现类 - sqlFile: # 使用 SQL 文件,对应 SQLFileChange 对象 encoding: utf8 path: classpath:db/changelog/sqlfile/CHAGE_SET_2_INIT_DB.sql - changeSet: # 对应一个 ChangeSet 对象 id: 3 # ChangeSet 编号 author: yunai # 作者 comments: 修复 `users` 的用户名 # 备注 changes: # 对应 Change 数组。Change 是一个接口,每种操作对应一种 Change 实现类 - customChange: {class: cn.iocoder.springboot.lab20.databaseversioncontrol.migration.CHANGE_SET_3_FixUsername} # 对应 CustomTaskChange ================================================ FILE: lab-20/lab-20-database-version-control-liquibase/src/main/resources/db/changelog/sqlfile/CHAGE_SET_2_INIT_DB.sql ================================================ -- 创建用户表 CREATE TABLE `users2` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号', `username` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号', `password` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '密码', `create_time` datetime DEFAULT NULL COMMENT '创建时间', PRIMARY KEY (`id`), UNIQUE KEY `idx_username` (`username`) ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; -- 插入一条数据 INSERT INTO `users2`(username, password, create_time) VALUES('yudaoyuanma', 'password', now()); ================================================ FILE: lab-20/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-20 pom lab-20-database-version-control-flyway lab-20-database-version-control-liquibase ================================================ FILE: lab-20/《芋道 Spring Boot 数据库版本管理入门》.md ================================================ ================================================ FILE: lab-21/lab-21-cache-demo/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE 4.0.0 lab-21-cache-demo org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java com.baomidou mybatis-plus-boot-starter 3.2.0 org.springframework.boot spring-boot-starter-cache org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-21/lab-21-cache-demo/src/main/java/cn/iocoder/springboot/lab21/cache/Application.java ================================================ package cn.iocoder.springboot.lab21.cache; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCaching; @SpringBootApplication @EnableCaching @MapperScan(basePackages = "cn.iocoder.springboot.lab21.cache.mapper") public class Application { } ================================================ FILE: lab-21/lab-21-cache-demo/src/main/java/cn/iocoder/springboot/lab21/cache/dataobject/UserDO.java ================================================ package cn.iocoder.springboot.lab21.cache.dataobject; import com.baomidou.mybatisplus.annotation.TableLogic; import com.baomidou.mybatisplus.annotation.TableName; import java.util.Date; /** * 用户 DO */ @TableName(value = "users") public class UserDO { /** * 用户编号 */ private Integer id; /** * 账号 */ private String username; /** * 密码(明文) * * ps:生产环境下,千万不要明文噢 */ private String password; /** * 创建时间 */ private Date createTime; /** * 是否删除 */ @TableLogic private Integer deleted; public Integer getId() { return id; } public UserDO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserDO setUsername(String username) { this.username = username; return this; } public String getPassword() { return password; } public UserDO setPassword(String password) { this.password = password; return this; } public Date getCreateTime() { return createTime; } public UserDO setCreateTime(Date createTime) { this.createTime = createTime; return this; } public Integer getDeleted() { return deleted; } public UserDO setDeleted(Integer deleted) { this.deleted = deleted; return this; } } ================================================ FILE: lab-21/lab-21-cache-demo/src/main/java/cn/iocoder/springboot/lab21/cache/mapper/UserCacheDao.java ================================================ package cn.iocoder.springboot.lab21.cache.mapper; import cn.iocoder.springboot.lab21.cache.dataobject.UserDO; import org.springframework.stereotype.Repository; @Repository public class UserCacheDao { public UserDO get(Integer id) { return new UserDO(); } public void put(UserDO user) { } } ================================================ FILE: lab-21/lab-21-cache-demo/src/main/java/cn/iocoder/springboot/lab21/cache/mapper/UserMapper.java ================================================ package cn.iocoder.springboot.lab21.cache.mapper; import cn.iocoder.springboot.lab21.cache.dataobject.UserDO; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Repository; @Repository public interface UserMapper extends BaseMapper { @Cacheable(value = "users", key = "#id") UserDO selectById(Integer id); @CachePut(value = "users", key = "#user.id") default UserDO insert0(UserDO user) { // 插入记录 this.insert(user); // 返回用户 return user; } } ================================================ FILE: lab-21/lab-21-cache-demo/src/main/java/cn/iocoder/springboot/lab21/cache/service/UserService.java ================================================ package cn.iocoder.springboot.lab21.cache.service; import cn.iocoder.springboot.lab21.cache.dataobject.UserDO; import cn.iocoder.springboot.lab21.cache.mapper.UserCacheDao; import cn.iocoder.springboot.lab21.cache.mapper.UserMapper; import org.springframework.beans.factory.annotation.Autowired; public class UserService { @Autowired private UserMapper userMapper; @Autowired private UserCacheDao userCacheDao; public UserDO getUser(Integer id) { // 从 Cache 中,查询用户信息 UserDO user = userCacheDao.get(id); if (user != null) { return user; } // 如果 Cache 查询不到,从 DB 中读取 user = userMapper.selectById(id); if (user != null) { // 非空,则缓存到 Cache 中 userCacheDao.put(user); } // 返回结果 return user; } public UserDO getUser2(Integer id) { return userMapper.selectById(id); } } ================================================ FILE: lab-21/lab-21-cache-demo/src/main/resources/application.yaml ================================================ spring: # datasource 数据源配置内容 datasource: url: jdbc:mysql://127.0.0.1:3306/lab-21-cache-demo?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: # mybatis-plus 配置内容 mybatis-plus: configuration: map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。 global-config: db-config: id-type: auto # ID 主键自增 logic-delete-value: 1 # 逻辑已删除值(默认为 1) logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) mapper-locations: classpath*:mapper/*.xml type-aliases-package: cn.iocoder.springboot.lab21.cache.dataobject # logging logging: level: # dao 开启 debug 模式 mybatis 输入 sql cn: iocoder: springboot: lab21: cache: mapper: debug ================================================ FILE: lab-21/lab-21-cache-demo/src/main/resources/sql/users.sql ================================================ CREATE TABLE `users` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号', `username` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号', `password` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '密码', `create_time` datetime DEFAULT NULL COMMENT '创建时间', `deleted` bit(1) DEFAULT NULL COMMENT '是否删除。0-未删除;1-删除', PRIMARY KEY (`id`), UNIQUE KEY `idx_username` (`username`) ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; ================================================ FILE: lab-21/lab-21-cache-demo/src/test/java/cn/iocoder/springboot/lab21/cache/UserMapperTest.java ================================================ package cn.iocoder.springboot.lab21.cache; import cn.iocoder.springboot.lab21.cache.dataobject.UserDO; import cn.iocoder.springboot.lab21.cache.mapper.UserMapper; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.Date; import java.util.UUID; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class UserMapperTest { @Autowired private UserMapper userMapper; @Test public void testSelectById() { UserDO user = userMapper.selectById(2); System.out.println("user:" + user); user = userMapper.selectById(2); System.out.println("user:" + user); } @Test public void testInsert () { // 插入记录 UserDO user = new UserDO(); user.setUsername(UUID.randomUUID().toString()); // 随机账号,因为唯一索引 user.setPassword("nicai"); user.setCreateTime(new Date()); user.setDeleted(0); userMapper.insert0(user); // 查询数据 user = userMapper.selectById(user.getId()); System.out.println(user); } } ================================================ FILE: lab-21/lab-21-cache-ehcache/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE 4.0.0 lab-21-cache-ehcache org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java com.baomidou mybatis-plus-boot-starter 3.2.0 org.springframework.boot spring-boot-starter-cache net.sf.ehcache ehcache org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-21/lab-21-cache-ehcache/src/main/java/cn/iocoder/springboot/lab21/cache/Application.java ================================================ package cn.iocoder.springboot.lab21.cache; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCaching; @SpringBootApplication @EnableCaching @MapperScan(basePackages = "cn.iocoder.springboot.lab21.cache.mapper") public class Application { } ================================================ FILE: lab-21/lab-21-cache-ehcache/src/main/java/cn/iocoder/springboot/lab21/cache/dataobject/UserDO.java ================================================ package cn.iocoder.springboot.lab21.cache.dataobject; import com.baomidou.mybatisplus.annotation.TableLogic; import com.baomidou.mybatisplus.annotation.TableName; import java.util.Date; /** * 用户 DO */ @TableName(value = "users") public class UserDO { /** * 用户编号 */ private Integer id; /** * 账号 */ private String username; /** * 密码(明文) * * ps:生产环境下,千万不要明文噢 */ private String password; /** * 创建时间 */ private Date createTime; /** * 是否删除 */ @TableLogic private Integer deleted; public Integer getId() { return id; } public UserDO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserDO setUsername(String username) { this.username = username; return this; } public String getPassword() { return password; } public UserDO setPassword(String password) { this.password = password; return this; } public Date getCreateTime() { return createTime; } public UserDO setCreateTime(Date createTime) { this.createTime = createTime; return this; } public Integer getDeleted() { return deleted; } public UserDO setDeleted(Integer deleted) { this.deleted = deleted; return this; } @Override public String toString() { return "UserDO{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", createTime=" + createTime + ", deleted=" + deleted + '}'; } } ================================================ FILE: lab-21/lab-21-cache-ehcache/src/main/java/cn/iocoder/springboot/lab21/cache/mapper/UserMapper.java ================================================ package cn.iocoder.springboot.lab21.cache.mapper; import cn.iocoder.springboot.lab21.cache.dataobject.UserDO; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.springframework.cache.annotation.CacheConfig; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Repository; @Repository @CacheConfig(cacheNames = "users") public interface UserMapper extends BaseMapper { @Cacheable(key = "#id") UserDO selectById(Integer id); @CachePut(key = "#user.id") default UserDO insert0(UserDO user) { // 插入记录 this.insert(user); // 返回用户 return user; } @CacheEvict(key = "#id") int deleteById(Integer id); } ================================================ FILE: lab-21/lab-21-cache-ehcache/src/main/resources/application.yaml ================================================ spring: # datasource 数据源配置内容 datasource: url: jdbc:mysql://127.0.0.1:3306/lab-21-cache-demo?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: # cache 缓存配置内容 cache: type: ehcache # mybatis-plus 配置内容 mybatis-plus: configuration: map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。 global-config: db-config: id-type: auto # ID 主键自增 logic-delete-value: 1 # 逻辑已删除值(默认为 1) logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) mapper-locations: classpath*:mapper/*.xml type-aliases-package: cn.iocoder.springboot.lab21.cache.dataobject # logging logging: level: # dao 开启 debug 模式 mybatis 输入 sql cn: iocoder: springboot: lab21: cache: mapper: debug ================================================ FILE: lab-21/lab-21-cache-ehcache/src/main/resources/ehcache.xml ================================================ ================================================ FILE: lab-21/lab-21-cache-ehcache/src/main/resources/sql/users.sql ================================================ CREATE TABLE `users` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号', `username` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号', `password` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '密码', `create_time` datetime DEFAULT NULL COMMENT '创建时间', `deleted` bit(1) DEFAULT NULL COMMENT '是否删除。0-未删除;1-删除', PRIMARY KEY (`id`), UNIQUE KEY `idx_username` (`username`) ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; INSERT INTO `users`(`id`, `username`, `password`, `create_time`, `deleted`) VALUES (1, 'yudaoyuanma', 'buzhidao', now(), 0); ================================================ FILE: lab-21/lab-21-cache-ehcache/src/test/java/cn/iocoder/springboot/lab21/cache/UserMapperTest.java ================================================ package cn.iocoder.springboot.lab21.cache; import cn.iocoder.springboot.lab21.cache.dataobject.UserDO; import cn.iocoder.springboot.lab21.cache.mapper.UserMapper; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.cache.CacheManager; import org.springframework.test.context.junit4.SpringRunner; import java.util.Date; import java.util.UUID; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class UserMapperTest { private static final String CACHE_NAME_USER = "users"; @Autowired private UserMapper userMapper; @Autowired private CacheManager cacheManager; @Test public void testCacheManager() { System.out.println(cacheManager); } @Test public void testSelectById() { // 这里,胖友事先插入一条 id = 1 的记录。 Integer id = 1; // 查询 id = 1 的记录 UserDO user = userMapper.selectById(id); System.out.println("user:" + user); // 判断缓存中,是不是存在 Assert.assertNotNull("缓存为空", cacheManager.getCache(CACHE_NAME_USER).get(user.getId(), UserDO.class)); // 查询 id = 1 的记录 user = userMapper.selectById(id); System.out.println("user:" + user); } @Test public void testInsert() { // 插入记录 UserDO user = new UserDO(); user.setUsername(UUID.randomUUID().toString()); // 随机账号,因为唯一索引 user.setPassword("nicai"); user.setCreateTime(new Date()); user.setDeleted(0); userMapper.insert0(user); // 判断缓存中,是不是存在 Assert.assertNotNull("缓存为空", cacheManager.getCache(CACHE_NAME_USER).get(user.getId(), UserDO.class)); } @Test public void testDeleteById() { // 插入记录,为了让缓存里有记录 UserDO user = new UserDO(); user.setUsername(UUID.randomUUID().toString()); // 随机账号,因为唯一索引 user.setPassword("nicai"); user.setCreateTime(new Date()); user.setDeleted(0); userMapper.insert0(user); // 判断缓存中,是不是存在 Assert.assertNotNull("缓存为空", cacheManager.getCache(CACHE_NAME_USER).get(user.getId(), UserDO.class)); // 删除记录,为了让缓存被删除 userMapper.deleteById(user.getId()); // 判断缓存中,是不是存在 Assert.assertNull("缓存不为空", cacheManager.getCache(CACHE_NAME_USER).get(user.getId(), UserDO.class)); } } ================================================ FILE: lab-21/lab-21-cache-redis/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE 4.0.0 lab-21-cache-redis org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java com.baomidou mybatis-plus-boot-starter 3.2.0 org.springframework.boot spring-boot-starter-cache org.springframework.boot spring-boot-starter-data-redis io.lettuce lettuce-core redis.clients jedis org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-21/lab-21-cache-redis/src/main/java/cn/iocoder/springboot/lab21/cache/Application.java ================================================ package cn.iocoder.springboot.lab21.cache; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCaching; @SpringBootApplication @EnableCaching @MapperScan(basePackages = "cn.iocoder.springboot.lab21.cache.mapper") public class Application { } ================================================ FILE: lab-21/lab-21-cache-redis/src/main/java/cn/iocoder/springboot/lab21/cache/dataobject/UserDO.java ================================================ package cn.iocoder.springboot.lab21.cache.dataobject; import com.baomidou.mybatisplus.annotation.TableLogic; import com.baomidou.mybatisplus.annotation.TableName; import java.io.Serializable; import java.util.Date; /** * 用户 DO */ @TableName(value = "users") public class UserDO implements Serializable { /** * 用户编号 */ private Integer id; /** * 账号 */ private String username; /** * 密码(明文) * * ps:生产环境下,千万不要明文噢 */ private String password; /** * 创建时间 */ private Date createTime; /** * 是否删除 */ @TableLogic private Integer deleted; public Integer getId() { return id; } public UserDO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserDO setUsername(String username) { this.username = username; return this; } public String getPassword() { return password; } public UserDO setPassword(String password) { this.password = password; return this; } public Date getCreateTime() { return createTime; } public UserDO setCreateTime(Date createTime) { this.createTime = createTime; return this; } public Integer getDeleted() { return deleted; } public UserDO setDeleted(Integer deleted) { this.deleted = deleted; return this; } @Override public String toString() { return "UserDO{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", createTime=" + createTime + ", deleted=" + deleted + '}'; } } ================================================ FILE: lab-21/lab-21-cache-redis/src/main/java/cn/iocoder/springboot/lab21/cache/mapper/UserMapper.java ================================================ package cn.iocoder.springboot.lab21.cache.mapper; import cn.iocoder.springboot.lab21.cache.dataobject.UserDO; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.springframework.cache.annotation.CacheConfig; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Repository; @Repository @CacheConfig(cacheNames = "users") public interface UserMapper extends BaseMapper { @Cacheable(key = "#id") UserDO selectById(Integer id); @CachePut(key = "#user.id") default UserDO insert0(UserDO user) { // 插入记录 this.insert(user); // 返回用户 return user; } @CacheEvict(key = "#id") int deleteById(Integer id); } ================================================ FILE: lab-21/lab-21-cache-redis/src/main/resources/application.yaml ================================================ spring: # datasource 数据源配置内容 datasource: url: jdbc:mysql://127.0.0.1:3306/lab-21-cache-demo?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: # 对应 RedisProperties 类 redis: host: 127.0.0.1 port: 6379 password: # Redis 服务器密码,默认为空。生产中,一定要设置 Redis 密码! database: 0 # Redis 数据库号,默认为 0 。 timeout: 0 # Redis 连接超时时间,单位:毫秒。 # 对应 RedisProperties.Jedis 内部类 jedis: pool: max-active: 8 # 连接池最大连接数,默认为 8 。使用负数表示没有限制。 max-idle: 8 # 默认连接数最小空闲的连接数,默认为 8 。使用负数表示没有限制。 min-idle: 0 # 默认连接池最小空闲的连接数,默认为 0 。允许设置 0 和 正数。 max-wait: -1 # 连接池最大阻塞等待时间,单位:毫秒。默认为 -1 ,表示不限制。 # cache 缓存配置内容 cache: type: redis # mybatis-plus 配置内容 mybatis-plus: configuration: map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。 global-config: db-config: id-type: auto # ID 主键自增 logic-delete-value: 1 # 逻辑已删除值(默认为 1) logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) mapper-locations: classpath*:mapper/*.xml type-aliases-package: cn.iocoder.springboot.lab21.cache.dataobject # logging logging: level: # dao 开启 debug 模式 mybatis 输入 sql cn: iocoder: springboot: lab21: cache: mapper: debug ================================================ FILE: lab-21/lab-21-cache-redis/src/main/resources/sql/users.sql ================================================ CREATE TABLE `users` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号', `username` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号', `password` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '密码', `create_time` datetime DEFAULT NULL COMMENT '创建时间', `deleted` bit(1) DEFAULT NULL COMMENT '是否删除。0-未删除;1-删除', PRIMARY KEY (`id`), UNIQUE KEY `idx_username` (`username`) ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; INSERT INTO `users`(`id`, `username`, `password`, `create_time`, `deleted`) VALUES (1, 'yudaoyuanma', 'buzhidao', now(), 0); ================================================ FILE: lab-21/lab-21-cache-redis/src/test/java/cn/iocoder/springboot/lab21/cache/UserMapperTest.java ================================================ package cn.iocoder.springboot.lab21.cache; import cn.iocoder.springboot.lab21.cache.dataobject.UserDO; import cn.iocoder.springboot.lab21.cache.mapper.UserMapper; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.cache.CacheManager; import org.springframework.test.context.junit4.SpringRunner; import java.util.Date; import java.util.UUID; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class UserMapperTest { private static final String CACHE_NAME_USER = "users"; @Autowired private UserMapper userMapper; @Autowired private CacheManager cacheManager; @Test public void testCacheManager() { System.out.println(cacheManager); } @Test public void testSelectById() { // 这里,胖友事先插入一条 id = 1 的记录。 Integer id = 1; // 查询 id = 1 的记录 UserDO user = userMapper.selectById(id); System.out.println("user:" + user); // 判断缓存中,是不是存在 Assert.assertNotNull("缓存为空", cacheManager.getCache(CACHE_NAME_USER).get(user.getId(), UserDO.class)); // 查询 id = 1 的记录 user = userMapper.selectById(id); System.out.println("user:" + user); } @Test public void testInsert() { // 插入记录 UserDO user = new UserDO(); user.setUsername(UUID.randomUUID().toString()); // 随机账号,因为唯一索引 user.setPassword("nicai"); user.setCreateTime(new Date()); user.setDeleted(0); userMapper.insert0(user); // 判断缓存中,是不是存在 Assert.assertNotNull("缓存为空", cacheManager.getCache(CACHE_NAME_USER).get(user.getId(), UserDO.class)); } @Test public void testDeleteById() { // 插入记录,为了让缓存里有记录 UserDO user = new UserDO(); user.setUsername(UUID.randomUUID().toString()); // 随机账号,因为唯一索引 user.setPassword("nicai"); user.setCreateTime(new Date()); user.setDeleted(0); userMapper.insert0(user); // 判断缓存中,是不是存在 Assert.assertNotNull("缓存为空", cacheManager.getCache(CACHE_NAME_USER).get(user.getId(), UserDO.class)); // 删除记录,为了让缓存被删除 userMapper.deleteById(user.getId()); // 判断缓存中,是不是存在 Assert.assertNull("缓存不为空", cacheManager.getCache(CACHE_NAME_USER).get(user.getId(), UserDO.class)); } } ================================================ FILE: lab-21/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-21 pom lab-21-cache-demo lab-21-cache-ehcache lab-21-cache-redis ================================================ FILE: lab-21/《芋道 Spring Boot 缓存 Cache 入门》.md ================================================ ================================================ FILE: lab-22/lab-22-validation-01/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE 4.0.0 lab-22-validation-01 org.springframework.boot spring-boot-starter-web org.springframework spring-aspects org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-22/lab-22-validation-01/src/main/java/cn/iocoder/springboot/lab22/validation/Application.java ================================================ package cn.iocoder.springboot.lab22.validation; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.EnableAspectJAutoProxy; @SpringBootApplication @EnableAspectJAutoProxy(exposeProxy = true) // http://www.voidcn.com/article/p-zddcuyii-bpt.html public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-22/lab-22-validation-01/src/main/java/cn/iocoder/springboot/lab22/validation/config/ValidationConfiguration.java ================================================ package cn.iocoder.springboot.lab22.validation.config; import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration; import org.springframework.context.MessageSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; import javax.validation.Validator; @Configuration public class ValidationConfiguration { /** * 参考 {@link ValidationAutoConfiguration#defaultValidator()} 方法,构建 Validator Bean * * @return Validator 对象 */ @Bean public Validator validator(MessageSource messageSource) { // 创建 LocalValidatorFactoryBean 对象 LocalValidatorFactoryBean validator = ValidationAutoConfiguration.defaultValidator(); // 设置 messageSource 属性,实现 i18 国际化 validator.setValidationMessageSource(messageSource); // 返回 return validator; } } ================================================ FILE: lab-22/lab-22-validation-01/src/main/java/cn/iocoder/springboot/lab22/validation/constants/GenderEnum.java ================================================ package cn.iocoder.springboot.lab22.validation.constants; import cn.iocoder.springboot.lab22.validation.core.validator.IntArrayValuable; import java.util.Arrays; public enum GenderEnum implements IntArrayValuable { MALE(1, "男"), FEMALE(2, "女"); /** * 值数组 */ public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(GenderEnum::getValue).toArray(); /** * 性别值 */ private final Integer value; /** * 性别名 */ private final String name; GenderEnum(Integer value, String name) { this.value = value; this.name = name; } public Integer getValue() { return value; } public String getName() { return name; } @Override public int[] array() { return ARRAYS; } } ================================================ FILE: lab-22/lab-22-validation-01/src/main/java/cn/iocoder/springboot/lab22/validation/constants/ServiceExceptionEnum.java ================================================ package cn.iocoder.springboot.lab22.validation.constants; /** * 业务异常枚举 */ public enum ServiceExceptionEnum { // ========== 系统级别 ========== SUCCESS(0, "成功"), SYS_ERROR(2001001000, "服务端发生异常"), MISSING_REQUEST_PARAM_ERROR(2001001001, "参数缺失"), INVALID_REQUEST_PARAM_ERROR(2001001002, "请求参数不合法"), // ========== 用户模块 ========== USER_NOT_FOUND(1001002000, "用户不存在"), // ========== 订单模块 ========== // ========== 商品模块 ========== ; /** * 错误码 */ private final int code; /** * 错误提示 */ private final String message; ServiceExceptionEnum(int code, String message) { this.code = code; this.message = message; } public int getCode() { return code; } public String getMessage() { return message; } } ================================================ FILE: lab-22/lab-22-validation-01/src/main/java/cn/iocoder/springboot/lab22/validation/controller/UserController.java ================================================ package cn.iocoder.springboot.lab22.validation.controller; import cn.iocoder.springboot.lab22.validation.dto.UserAddDTO; import cn.iocoder.springboot.lab22.validation.dto.UserUpdateDTO; import cn.iocoder.springboot.lab22.validation.dto.UserUpdateGenderDTO; import cn.iocoder.springboot.lab22.validation.dto.UserUpdateStatusDTO; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.validation.Valid; import javax.validation.constraints.Min; @RestController @RequestMapping("/users") @Validated public class UserController { private Logger logger = LoggerFactory.getLogger(getClass()); @GetMapping("/get") public void get(@RequestParam("id") @Min(value = 1L, message = "编号必须大于 0") Integer id) { logger.info("[get][id: {}]", id); } @PostMapping("/add") public void add(@Valid UserAddDTO addDTO) { logger.info("[add][addDTO: {}]", addDTO); } @PostMapping("/update_gender") public void updateGender(@Valid UserUpdateGenderDTO updateGenderDTO) { logger.info("[updateGender][updateGenderDTO: {}]", updateGenderDTO); } @PostMapping("/update_status_true") public void updateStatusTrue(@Validated(UserUpdateStatusDTO.Group01.class) UserUpdateStatusDTO updateStatusDTO) { logger.info("[updateStatusTrue][updateStatusDTO: {}]", updateStatusDTO); } @PostMapping("/update_status_false") public void updateStatusFalse(@Validated(UserUpdateStatusDTO.Group02.class) UserUpdateStatusDTO updateStatusDTO) { logger.info("[updateStatusFalse][updateStatusDTO: {}]", updateStatusDTO); } @PostMapping("/update") public void update(@Valid UserUpdateDTO updateDTO) { logger.info("[update][updateDTO: {}]", updateDTO); } } ================================================ FILE: lab-22/lab-22-validation-01/src/main/java/cn/iocoder/springboot/lab22/validation/core/exception/ServiceException.java ================================================ package cn.iocoder.springboot.lab22.validation.core.exception; import cn.iocoder.springboot.lab22.validation.constants.ServiceExceptionEnum; /** * 服务异常 * * 参考 https://www.kancloud.cn/onebase/ob/484204 文章 * * 一共 10 位,分成四段 * * 第一段,1 位,类型 * 1 - 业务级别异常 * 2 - 系统级别异常 * 第二段,3 位,系统类型 * 001 - 用户系统 * 002 - 商品系统 * 003 - 订单系统 * 004 - 支付系统 * 005 - 优惠劵系统 * ... - ... * 第三段,3 位,模块 * 不限制规则。 * 一般建议,每个系统里面,可能有多个模块,可以再去做分段。以用户系统为例子: * 001 - OAuth2 模块 * 002 - User 模块 * 003 - MobileCode 模块 * 第四段,3 位,错误码 * 不限制规则。 * 一般建议,每个模块自增。 */ public final class ServiceException extends RuntimeException { /** * 错误码 */ private final Integer code; public ServiceException(ServiceExceptionEnum serviceExceptionEnum) { // 使用父类的 message 字段 super(serviceExceptionEnum.getMessage()); // 设置错误码 this.code = serviceExceptionEnum.getCode(); } public Integer getCode() { return code; } } ================================================ FILE: lab-22/lab-22-validation-01/src/main/java/cn/iocoder/springboot/lab22/validation/core/package-info.java ================================================ /** * 提供核心封装 */ package cn.iocoder.springboot.lab22.validation.core; ================================================ FILE: lab-22/lab-22-validation-01/src/main/java/cn/iocoder/springboot/lab22/validation/core/validator/InEnum.java ================================================ package cn.iocoder.springboot.lab22.validation.core.validator; import javax.validation.Constraint; import javax.validation.Payload; import java.lang.annotation.*; import static java.lang.annotation.ElementType.*; @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Constraint(validatedBy = InEnumValidator.class) public @interface InEnum { /** * @return 实现 IntArrayValuable 接口的 */ Class value(); /** * @return 提示内容 */ String message() default "必须在指定范围 {value}"; /** * @return 分组 */ Class[] groups() default {}; /** * @return Payload 数组 */ Class[] payload() default {}; /** * Defines several {@code @InEnum} constraints on the same element. */ @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) @Retention(RetentionPolicy.RUNTIME) @Documented @interface List { InEnum[] value(); } } ================================================ FILE: lab-22/lab-22-validation-01/src/main/java/cn/iocoder/springboot/lab22/validation/core/validator/InEnumValidator.java ================================================ package cn.iocoder.springboot.lab22.validation.core.validator; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import java.util.Arrays; import java.util.Collections; import java.util.Set; import java.util.stream.Collectors; public class InEnumValidator implements ConstraintValidator { /** * 值数组 */ private Set values; @Override public void initialize(InEnum annotation) { IntArrayValuable[] values = annotation.value().getEnumConstants(); if (values.length == 0) { this.values = Collections.emptySet(); } else { this.values = Arrays.stream(values[0].array()).boxed().collect(Collectors.toSet()); } } @Override public boolean isValid(Integer value, ConstraintValidatorContext context) { // 校验通过 if (values.contains(value)) { return true; } // 校验不通过,自定义提示语句(因为,注解上的 value 是枚举类,无法获得枚举类的实际值) context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值 context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate() .replaceAll("\\{value}", values.toString())).addConstraintViolation(); // 重新添加错误提示语句 return false; } } ================================================ FILE: lab-22/lab-22-validation-01/src/main/java/cn/iocoder/springboot/lab22/validation/core/validator/IntArrayValuable.java ================================================ package cn.iocoder.springboot.lab22.validation.core.validator; /** * 可生成 Int 数组的接口 */ public interface IntArrayValuable { /** * @return int 数组 */ int[] array(); } ================================================ FILE: lab-22/lab-22-validation-01/src/main/java/cn/iocoder/springboot/lab22/validation/core/vo/CommonResult.java ================================================ package cn.iocoder.springboot.lab22.validation.core.vo; import com.fasterxml.jackson.annotation.JsonIgnore; import org.springframework.util.Assert; import java.io.Serializable; /** * 通用返回结果 * * @param 结果泛型 */ public class CommonResult implements Serializable { public static Integer CODE_SUCCESS = 0; /** * 错误码 */ private Integer code; /** * 错误提示 */ private String message; /** * 返回数据 */ private T data; /** * 将传入的 result 对象,转换成另外一个泛型结果的对象 * * 因为 A 方法返回的 CommonResult 对象,不满足调用其的 B 方法的返回,所以需要进行转换。 * * @param result 传入的 result 对象 * @param 返回的泛型 * @return 新的 CommonResult 对象 */ public static CommonResult error(CommonResult result) { return error(result.getCode(), result.getMessage()); } public static CommonResult error(Integer code, String message) { Assert.isTrue(!CODE_SUCCESS.equals(code), "code 必须是错误的!"); CommonResult result = new CommonResult<>(); result.code = code; result.message = message; return result; } public static CommonResult success(T data) { CommonResult result = new CommonResult<>(); result.code = CODE_SUCCESS; result.data = data; result.message = ""; return result; } public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public T getData() { return data; } public void setData(T data) { this.data = data; } @JsonIgnore public boolean isSuccess() { return CODE_SUCCESS.equals(code); } @JsonIgnore public boolean isError() { return !isSuccess(); } @Override public String toString() { return "CommonResult{" + "code=" + code + ", message='" + message + '\'' + ", data=" + data + '}'; } } ================================================ FILE: lab-22/lab-22-validation-01/src/main/java/cn/iocoder/springboot/lab22/validation/core/web/GlobalExceptionHandler.java ================================================ package cn.iocoder.springboot.lab22.validation.core.web; import cn.iocoder.springboot.lab22.validation.constants.ServiceExceptionEnum; import cn.iocoder.springboot.lab22.validation.core.exception.ServiceException; import cn.iocoder.springboot.lab22.validation.core.vo.CommonResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.validation.BindException; import org.springframework.validation.ObjectError; import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletRequest; import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolationException; @ControllerAdvice(basePackages = "cn.iocoder.springboot.lab22.validation.controller") public class GlobalExceptionHandler { private Logger logger = LoggerFactory.getLogger(getClass()); /** * 处理 ServiceException 异常 */ @ResponseBody @ExceptionHandler(value = ServiceException.class) public CommonResult serviceExceptionHandler(HttpServletRequest req, ServiceException ex) { logger.debug("[serviceExceptionHandler]", ex); // 包装 CommonResult 结果 return CommonResult.error(ex.getCode(), ex.getMessage()); } /** * 处理 MissingServletRequestParameterException 异常 * * SpringMVC 参数不正确 */ @ResponseBody @ExceptionHandler(value = MissingServletRequestParameterException.class) public CommonResult missingServletRequestParameterExceptionHandler(HttpServletRequest req, MissingServletRequestParameterException ex) { logger.debug("[missingServletRequestParameterExceptionHandler]", ex); // 包装 CommonResult 结果 return CommonResult.error(ServiceExceptionEnum.MISSING_REQUEST_PARAM_ERROR.getCode(), ServiceExceptionEnum.MISSING_REQUEST_PARAM_ERROR.getMessage()); } @ResponseBody @ExceptionHandler(value = ConstraintViolationException.class) public CommonResult constraintViolationExceptionHandler(HttpServletRequest req, ConstraintViolationException ex) { logger.debug("[constraintViolationExceptionHandler]", ex); // 拼接错误 StringBuilder detailMessage = new StringBuilder(); for (ConstraintViolation constraintViolation : ex.getConstraintViolations()) { // 使用 ; 分隔多个错误 if (detailMessage.length() > 0) { detailMessage.append(";"); } // 拼接内容到其中 detailMessage.append(constraintViolation.getMessage()); } // 包装 CommonResult 结果 return CommonResult.error(ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR.getCode(), ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR.getMessage() + ":" + detailMessage.toString()); } @ResponseBody @ExceptionHandler(value = BindException.class) public CommonResult bindExceptionHandler(HttpServletRequest req, BindException ex) { logger.debug("[bindExceptionHandler]", ex); // 拼接错误 StringBuilder detailMessage = new StringBuilder(); for (ObjectError objectError : ex.getAllErrors()) { // 使用 ; 分隔多个错误 if (detailMessage.length() > 0) { detailMessage.append(";"); } // 拼接内容到其中 detailMessage.append(objectError.getDefaultMessage()); } // 包装 CommonResult 结果 return CommonResult.error(ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR.getCode(), ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR.getMessage() + ":" + detailMessage.toString()); } /** * 处理其它 Exception 异常 */ @ResponseBody @ExceptionHandler(value = Exception.class) public CommonResult exceptionHandler(HttpServletRequest req, Exception e) { // 记录异常日志 logger.error("[exceptionHandler]", e); // 返回 ERROR CommonResult return CommonResult.error(ServiceExceptionEnum.SYS_ERROR.getCode(), ServiceExceptionEnum.SYS_ERROR.getMessage()); } } ================================================ FILE: lab-22/lab-22-validation-01/src/main/java/cn/iocoder/springboot/lab22/validation/core/web/GlobalResponseBodyHandler.java ================================================ package cn.iocoder.springboot.lab22.validation.core.web; import cn.iocoder.springboot.lab22.validation.core.vo.CommonResult; import org.springframework.core.MethodParameter; import org.springframework.http.MediaType; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; // 只拦截我们的 Controller 所在包,避免其它类似 swagger 提供的 API 被切面拦截 @ControllerAdvice(basePackages = "cn.iocoder.springboot.lab22.validation.controller") public class GlobalResponseBodyHandler implements ResponseBodyAdvice { @Override public boolean supports(MethodParameter returnType, Class converterType) { return true; } @Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { // 如果已经是 CommonResult 类型,则直接返回 if (body instanceof CommonResult) { return body; } // 如果不是,则包装成 CommonResult 类型 return CommonResult.success(body); } } ================================================ FILE: lab-22/lab-22-validation-01/src/main/java/cn/iocoder/springboot/lab22/validation/dto/UserAddDTO.java ================================================ package cn.iocoder.springboot.lab22.validation.dto; import org.hibernate.validator.constraints.Length; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.Pattern; /** * 用户添加 DTO */ public class UserAddDTO { /** * 账号 */ @NotEmpty(message = "登陆账号不能为空") @Length(min = 5, max = 16, message = "账号长度为 5-16 位") @Pattern(regexp = "^[A-Za-z0-9]+$", message = "账号格式为数字以及字母") private String username; /** * 密码 */ @NotEmpty(message = "密码不能为空") @Length(min = 4, max = 16, message = "密码长度为 4-16 位") private String password; public String getUsername() { return username; } public UserAddDTO setUsername(String username) { this.username = username; return this; } public String getPassword() { return password; } public UserAddDTO setPassword(String password) { this.password = password; return this; } @Override public String toString() { return "UserAddDTO{" + "username='" + username + '\'' + ", password='" + password + '\'' + '}'; } } ================================================ FILE: lab-22/lab-22-validation-01/src/main/java/cn/iocoder/springboot/lab22/validation/dto/UserUpdateDTO.java ================================================ package cn.iocoder.springboot.lab22.validation.dto; import javax.validation.constraints.NotNull; /** * 用户更新 DTO */ public class UserUpdateDTO { /** * 用户编号 */ @NotNull(message = "{UserUpdateDTO.id.NotNull}") private Integer id; public Integer getId() { return id; } public UserUpdateDTO setId(Integer id) { this.id = id; return this; } } ================================================ FILE: lab-22/lab-22-validation-01/src/main/java/cn/iocoder/springboot/lab22/validation/dto/UserUpdateGenderDTO.java ================================================ package cn.iocoder.springboot.lab22.validation.dto; import cn.iocoder.springboot.lab22.validation.constants.GenderEnum; import cn.iocoder.springboot.lab22.validation.core.validator.InEnum; import javax.validation.constraints.NotNull; /** * 用户更新性别 DTO */ public class UserUpdateGenderDTO { /** * 用户编号 */ @NotNull(message = "用户编号不能为空") private Integer id; /** * 性别 */ @NotNull(message = "性别不能为空") @InEnum(value = GenderEnum.class, message = "性别必须是 {value}") private Integer gender; public Integer getId() { return id; } public UserUpdateGenderDTO setId(Integer id) { this.id = id; return this; } public Integer getGender() { return gender; } public UserUpdateGenderDTO setGender(Integer gender) { this.gender = gender; return this; } @Override public String toString() { return "UserUpdateGenderDTO{" + "id=" + id + ", gender=" + gender + '}'; } } ================================================ FILE: lab-22/lab-22-validation-01/src/main/java/cn/iocoder/springboot/lab22/validation/dto/UserUpdateStatusDTO.java ================================================ package cn.iocoder.springboot.lab22.validation.dto; import javax.validation.constraints.AssertFalse; import javax.validation.constraints.AssertTrue; /** * 用户更新状态 DTO */ public class UserUpdateStatusDTO { /** * 分组 01 ,要求状态必须为 true */ public interface Group01 {} /** * 状态 02 ,要求状态必须为 false */ public interface Group02 {} /** * 状态 */ @AssertTrue(message = "状态必须为 true", groups = Group01.class) @AssertFalse(message = "状态必须为 false", groups = Group02.class) private Boolean status; public Boolean getStatus() { return status; } public UserUpdateStatusDTO setStatus(Boolean status) { this.status = status; return this; } } ================================================ FILE: lab-22/lab-22-validation-01/src/main/java/cn/iocoder/springboot/lab22/validation/service/UserService.java ================================================ package cn.iocoder.springboot.lab22.validation.service; import cn.iocoder.springboot.lab22.validation.dto.UserAddDTO; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.aop.framework.AopContext; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; import javax.validation.Valid; import javax.validation.constraints.Min; @Service @Validated public class UserService { private Logger logger = LoggerFactory.getLogger(getClass()); public void get(@Min(value = 1L, message = "编号必须大于 0") Integer id) { logger.info("[get][id: {}]", id); } public void add(@Valid UserAddDTO addDTO) { logger.info("[add][addDTO: {}]", addDTO); } public void add01(UserAddDTO addDTO) { this.add(addDTO); } public void add02(UserAddDTO addDTO) { self().add(addDTO); } private UserService self() { return (UserService) AopContext.currentProxy(); } } ================================================ FILE: lab-22/lab-22-validation-01/src/main/resources/application.yaml ================================================ spring: # i18 message 配置,对应 MessageSourceProperties 配置类 messages: basename: i18n/messages # 文件路径基础名 encoding: UTF-8 # 使用 UTF-8 编码 ================================================ FILE: lab-22/lab-22-validation-01/src/main/resources/i18n/messages.properties ================================================ UserUpdateDTO.id.NotNull=用户编号不能为空 ================================================ FILE: lab-22/lab-22-validation-01/src/main/resources/i18n/messages_en.properties ================================================ UserUpdateDTO.id.NotNull=userId cannot be empty ================================================ FILE: lab-22/lab-22-validation-01/src/main/resources/i18n/messages_ja.properties ================================================ UserUpdateDTO.id.NotNull=ユーザー番号は空にできません ================================================ FILE: lab-22/lab-22-validation-01/src/test/java/cn/iocoder/springboot/lab22/validation/service/UserServiceTest.java ================================================ package cn.iocoder.springboot.lab22.validation.service; import cn.iocoder.springboot.lab22.validation.Application; import cn.iocoder.springboot.lab22.validation.dto.UserAddDTO; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import javax.validation.ConstraintViolation; import javax.validation.Validator; import java.util.Set; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class UserServiceTest { @Autowired private UserService userService; @Autowired private Validator validator; @Test public void testGet() { userService.get(-1); } @Test public void testAdd() { UserAddDTO addDTO = new UserAddDTO(); userService.add(addDTO); } @Test public void testAdd01() { UserAddDTO addDTO = new UserAddDTO(); userService.add01(addDTO); } @Test public void testAdd02() { UserAddDTO addDTO = new UserAddDTO(); userService.add02(addDTO); } @Test public void testValidator() { // 打印,查看 validator 的类型 System.out.println(validator); // 创建 UserAddDTO 对象 UserAddDTO addDTO = new UserAddDTO(); // 校验 Set> result = validator.validate(addDTO); // 打印校验结果 for (ConstraintViolation constraintViolation : result) { // 属性:消息 System.out.println(constraintViolation.getPropertyPath() + ":" + constraintViolation.getMessage()); } } } ================================================ FILE: lab-22/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-22 pom lab-22-validation-01 ================================================ FILE: lab-22/《芋道 Spring Boot 参数校验 Validation 入门》.md ================================================ ================================================ FILE: lab-23/lab-springmvc-23-01/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE 4.0.0 lab-springmvc-23-01 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-23/lab-springmvc-23-01/src/main/java/cn/iocoder/springboot/lab23/springmvc/Application.java ================================================ package cn.iocoder.springboot.lab23.springmvc; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-23/lab-springmvc-23-01/src/main/java/cn/iocoder/springboot/lab23/springmvc/controller/UserController.java ================================================ package cn.iocoder.springboot.lab23.springmvc.controller; import cn.iocoder.springboot.lab23.springmvc.dto.UserAddDTO; import cn.iocoder.springboot.lab23.springmvc.dto.UserUpdateDTO; import cn.iocoder.springboot.lab23.springmvc.service.UserService; import cn.iocoder.springboot.lab23.springmvc.vo.UserVO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.ArrayList; import java.util.List; /** * 用户 Controller */ @RestController @RequestMapping("/users") public class UserController { @Autowired private UserService userService; /** * 查询用户列表 * * @return 用户列表 */ @GetMapping("") public List list() { // 查询列表 List result = new ArrayList<>(); result.add(new UserVO().setId(1).setUsername("yudaoyuanma")); result.add(new UserVO().setId(2).setUsername("woshiyutou")); result.add(new UserVO().setId(3).setUsername("chifanshuijiao")); // 返回列表 return result; } /** * 获得指定用户编号的用户 * * @param id 用户编号 * @return 用户 */ @GetMapping("/{id}") public UserVO get(@PathVariable("id") Integer id) { // 查询并返回用户 return new UserVO().setId(id).setUsername("username:" + id); } /** * 获得指定用户编号的用户 * * @param id 用户编号 * @return 用户 */ @GetMapping("/v2/{id}") public UserVO get2(@PathVariable("id") Integer id) { return userService.get(id); } /** * 添加用户 * * @param addDTO 添加用户信息 DTO * @return 添加成功的用户编号 */ @PostMapping("") public Integer add(UserAddDTO addDTO) { // 插入用户记录,返回编号 Integer returnId = 1; // 返回用户编号 return returnId; } /** * 更新指定用户编号的用户 * * @param id 用户编号 * @param updateDTO 更新用户信息 DTO * @return 是否修改成功 */ @PutMapping("/{id}") public Boolean update(@PathVariable("id") Integer id, UserUpdateDTO updateDTO) { // 将 id 设置到 updateDTO 中 updateDTO.setId(id); // 更新用户记录 Boolean success = true; // 返回更新是否成功 return success; } /** * 删除指定用户编号的用户 * * @param id 用户编号 * @return 是否删除成功 */ @DeleteMapping("/{id}") public Boolean delete(@PathVariable("id") Integer id) { // 删除用户记录 Boolean success = false; // 返回是否更新成功 return success; } } ================================================ FILE: lab-23/lab-springmvc-23-01/src/main/java/cn/iocoder/springboot/lab23/springmvc/controller/UserController2.java ================================================ package cn.iocoder.springboot.lab23.springmvc.controller; import cn.iocoder.springboot.lab23.springmvc.dto.UserAddDTO; import cn.iocoder.springboot.lab23.springmvc.dto.UserUpdateDTO; import cn.iocoder.springboot.lab23.springmvc.vo.UserVO; import org.springframework.web.bind.annotation.*; import java.util.ArrayList; import java.util.List; import java.util.UUID; /** * 用户2 Controller */ @RestController @RequestMapping("/users2") public class UserController2 { /** * 查询用户列表 * * @return 用户列表 */ @GetMapping("/list") // URL 修改成 /list public List list() { // 查询列表 List result = new ArrayList<>(); result.add(new UserVO().setId(1).setUsername("yudaoyuanma")); result.add(new UserVO().setId(2).setUsername("woshiyutou")); result.add(new UserVO().setId(3).setUsername("chifanshuijiao")); // 返回列表 return result; } /** * 获得指定用户编号的用户 * * @param id 用户编号 * @return 用户 */ @GetMapping("/get") // URL 修改成 /get public UserVO get(@RequestParam("id") Integer id) { // 查询并返回用户 return new UserVO().setId(id).setUsername(UUID.randomUUID().toString()); } /** * 添加用户 * * @param addDTO 添加用户信息 DTO * @return 添加成功的用户编号 */ @PostMapping("add") // URL 修改成 /add public Integer add(UserAddDTO addDTO) { // 插入用户记录,返回编号 Integer returnId = UUID.randomUUID().hashCode(); // 返回用户编号 return returnId; } /** * 更新指定用户编号的用户 * * @param updateDTO 更新用户信息 DTO * @return 是否修改成功 */ @PostMapping("/update") // URL 修改成 /update ,RequestMethod 改成 POST public Boolean update(UserUpdateDTO updateDTO) { // 更新用户记录 Boolean success = true; // 返回更新是否成功 return success; } /** * 删除指定用户编号的用户 * * @param id 用户编号 * @return 是否删除成功 */ @DeleteMapping("/delete") // URL 修改成 /delete ,RequestMethod 改成 DELETE public Boolean delete(@RequestParam("id") Integer id) { // 删除用户记录 Boolean success = false; // 返回是否更新成功 return success; } } ================================================ FILE: lab-23/lab-springmvc-23-01/src/main/java/cn/iocoder/springboot/lab23/springmvc/dto/UserAddDTO.java ================================================ package cn.iocoder.springboot.lab23.springmvc.dto; /** * 用户添加 DTO */ public class UserAddDTO { /** * 账号 */ private String username; /** * 密码 */ private String password; public String getUsername() { return username; } public UserAddDTO setUsername(String username) { this.username = username; return this; } public String getPassword() { return password; } public UserAddDTO setPassword(String password) { this.password = password; return this; } } ================================================ FILE: lab-23/lab-springmvc-23-01/src/main/java/cn/iocoder/springboot/lab23/springmvc/dto/UserUpdateDTO.java ================================================ package cn.iocoder.springboot.lab23.springmvc.dto; public class UserUpdateDTO { /** * 编号 */ private Integer id; /** * 账号 */ private String username; /** * 密码 */ private String password; public Integer getId() { return id; } public UserUpdateDTO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserUpdateDTO setUsername(String username) { this.username = username; return this; } public String getPassword() { return password; } public UserUpdateDTO setPassword(String password) { this.password = password; return this; } } ================================================ FILE: lab-23/lab-springmvc-23-01/src/main/java/cn/iocoder/springboot/lab23/springmvc/service/UserService.java ================================================ package cn.iocoder.springboot.lab23.springmvc.service; import cn.iocoder.springboot.lab23.springmvc.vo.UserVO; import org.springframework.stereotype.Service; @Service public class UserService { public UserVO get(Integer id) { return new UserVO().setId(id).setUsername("test"); } } ================================================ FILE: lab-23/lab-springmvc-23-01/src/main/java/cn/iocoder/springboot/lab23/springmvc/vo/UserVO.java ================================================ package cn.iocoder.springboot.lab23.springmvc.vo; /** * 用户 VO */ public class UserVO { /** * 编号 */ private Integer id; /** * 账号 */ private String username; public Integer getId() { return id; } public UserVO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserVO setUsername(String username) { this.username = username; return this; } } ================================================ FILE: lab-23/lab-springmvc-23-01/src/test/java/cn/iocoder/springboot/lab23/springmvc/controller/UserControllerTest.java ================================================ package cn.iocoder.springboot.lab23.springmvc.controller; import cn.iocoder.springboot.lab23.springmvc.Application; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultHandlers; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; /** * UserController 集成测试 */ @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) @AutoConfigureMockMvc public class UserControllerTest { @Autowired private MockMvc mvc; @Test public void testList() throws Exception { // 查询用户列表 ResultActions resultActions = mvc.perform(MockMvcRequestBuilders.get("/users")); // 校验结果 resultActions.andExpect(MockMvcResultMatchers.status().isOk()); // 响应状态码 200 resultActions.andExpect(MockMvcResultMatchers.content().json("[\n" + " {\n" + " \"id\": 1,\n" + " \"username\": \"yudaoyuanma\"\n" + " },\n" + " {\n" + " \"id\": 2,\n" + " \"username\": \"woshiyutou\"\n" + " },\n" + " {\n" + " \"id\": 3,\n" + " \"username\": \"chifanshuijiao\"\n" + " }\n" + "]")); // 响应结果 } @Test public void testGet() throws Exception { // 获得指定用户编号的用户 ResultActions resultActions = mvc.perform(MockMvcRequestBuilders.get("/users/1")); // 校验结果 resultActions.andExpect(MockMvcResultMatchers.status().isOk()); // 响应状态码 200 resultActions.andExpect(MockMvcResultMatchers.content().json("{\n" + "\"id\": 1,\n" + "\"username\": \"username:1\"\n" + "}")); // 响应结果 } @Test public void testGet2() throws Exception { // 获得指定用户编号的用户 ResultActions resultActions = mvc.perform(MockMvcRequestBuilders.get("/users/1")); // 校验结果 resultActions.andExpect(MockMvcResultMatchers.status().isOk()); // 响应状态码 200 resultActions.andExpect(MockMvcResultMatchers.content().json("{\n" + "\"id\": 1,\n" + "\"username\": \"username:1\"\n" + "}")); // 响应结果 // 打印结果 resultActions.andDo(MockMvcResultHandlers.print()); // 获得 MvcResult ,后续执行各种自定义逻辑 MvcResult mvcResult = resultActions.andReturn(); System.out.println("拦截器数量:" + mvcResult.getInterceptors().length); } @Test public void testAdd() throws Exception { // 获得指定用户编号的用户 ResultActions resultActions = mvc.perform(MockMvcRequestBuilders.post("/users") .param("username", "yudaoyuanma") .param("passowrd", "nicai")); // 校验结果 resultActions.andExpect(MockMvcResultMatchers.status().isOk()); // 响应状态码 200 resultActions.andExpect(MockMvcResultMatchers.content().string("1")); // 响应结果 } @Test public void testUpdate() throws Exception { // 获得指定用户编号的用户 ResultActions resultActions = mvc.perform(MockMvcRequestBuilders.put("/users/1") .param("username", "yudaoyuanma") .param("passowrd", "nicai")); // 校验结果 resultActions.andExpect(MockMvcResultMatchers.status().isOk()); // 响应状态码 200 resultActions.andExpect(MockMvcResultMatchers.content().string("true")); // 响应结果 } @Test public void testDelete() throws Exception { // 获得指定用户编号的用户 ResultActions resultActions = mvc.perform(MockMvcRequestBuilders.delete("/users/1")); // 校验结果 resultActions.andExpect(MockMvcResultMatchers.status().isOk()); // 响应状态码 200 resultActions.andExpect(MockMvcResultMatchers.content().string("false")); // 响应结果 } } ================================================ FILE: lab-23/lab-springmvc-23-01/src/test/java/cn/iocoder/springboot/lab23/springmvc/controller/UserControllerTest2.java ================================================ package cn.iocoder.springboot.lab23.springmvc.controller; import cn.iocoder.springboot.lab23.springmvc.service.UserService; import cn.iocoder.springboot.lab23.springmvc.vo.UserVO; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; /** * UserController 单元测试 * * 参考 https://spring.io/guides/gs/testing-web/ 文章 */ @RunWith(SpringRunner.class) @WebMvcTest(UserController.class) public class UserControllerTest2 { @Autowired private MockMvc mvc; @MockBean private UserService userService; @Test public void testGet2() throws Exception { // Mock UserService 的 get 方法 System.out.println("before mock:" + userService.get(1)); Mockito.when(userService.get(1)).thenReturn( new UserVO().setId(1).setUsername("username:1")); System.out.println("after mock:" + userService.get(1)); // 查询用户列表 ResultActions resultActions = mvc.perform(MockMvcRequestBuilders.get("/users/v2/1")); // 校验结果 resultActions.andExpect(MockMvcResultMatchers.status().isOk()); // 响应状态码 200 resultActions.andExpect(MockMvcResultMatchers.content().json("{\n" + " \"id\": 1,\n" + " \"username\": \"username:1\"\n" + "}")); // 响应结果 } } ================================================ FILE: lab-23/lab-springmvc-23-01/src/test/java/cn/iocoder/springboot/lab23/springmvc/package-info.java ================================================ package cn.iocoder.springboot.lab23.springmvc; ================================================ FILE: lab-23/lab-springmvc-23-02/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE 4.0.0 lab-springmvc-23-02 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-test test com.fasterxml.jackson.dataformat jackson-dataformat-xml com.alibaba fastjson 1.2.62 ================================================ FILE: lab-23/lab-springmvc-23-02/src/main/java/cn/iocoder/springboot/lab23/springmvc/Application.java ================================================ package cn.iocoder.springboot.lab23.springmvc; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.ServletComponentScan; @SpringBootApplication @ServletComponentScan public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-23/lab-springmvc-23-02/src/main/java/cn/iocoder/springboot/lab23/springmvc/config/SpringMVCConfiguration.java ================================================ package cn.iocoder.springboot.lab23.springmvc.config; import cn.iocoder.springboot.lab23.springmvc.core.interceptor.FirstInterceptor; import cn.iocoder.springboot.lab23.springmvc.core.interceptor.SecondInterceptor; import cn.iocoder.springboot.lab23.springmvc.core.interceptor.ThirdInterceptor; import com.alibaba.fastjson.serializer.SerializerFeature; import com.alibaba.fastjson.support.config.FastJsonConfig; import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.ServletListenerRegistrationBean; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import javax.servlet.*; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.nio.charset.Charset; import java.util.Arrays; import java.util.Collections; import java.util.List; @Configuration public class SpringMVCConfiguration implements WebMvcConfigurer { private Logger logger = LoggerFactory.getLogger(getClass()); @Bean public FirstInterceptor firstInterceptor() { return new FirstInterceptor(); } @Bean public SecondInterceptor secondInterceptor() { return new SecondInterceptor(); } @Bean public ThirdInterceptor thirdInterceptor() { return new ThirdInterceptor(); } @Override public void addInterceptors(InterceptorRegistry registry) { // 拦截器一 registry.addInterceptor(this.firstInterceptor()).addPathPatterns("/**"); // 拦截器二 registry.addInterceptor(this.secondInterceptor()).addPathPatterns("/users/current_user"); // 拦截器三 registry.addInterceptor(this.thirdInterceptor()).addPathPatterns("/**"); } @Bean public ServletRegistrationBean testServlet01() { ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean<>(new HttpServlet() { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { logger.info("[doGet][uri: {}]", req.getRequestURI()); } }); servletRegistrationBean.setUrlMappings(Collections.singleton("/test/01")); return servletRegistrationBean; } @Bean public FilterRegistrationBean testFilter01() { FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean<>(new Filter() { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { logger.info("[doFilter]"); filterChain.doFilter(servletRequest, servletResponse); } }); filterRegistrationBean.setUrlPatterns(Collections.singleton("/test/*")); return filterRegistrationBean; } @Bean public ServletListenerRegistrationBean testListener01() { return new ServletListenerRegistrationBean<>(new ServletContextListener() { @Override public void contextInitialized(ServletContextEvent sce) { logger.info("[contextInitialized]"); } @Override public void contextDestroyed(ServletContextEvent sce) { } }); } // @Override // public void addCorsMappings(CorsRegistry registry) { // // 添加全局的 CORS 配置 // registry.addMapping("/**") // 匹配所有 URL ,相当于全局配置 // .allowedOrigins("*") // 允许所有请求来源 // .allowCredentials(true) // 允许发送 Cookie // .allowedMethods("*") // 允许所有请求 Method // .allowedHeaders("*") // 允许所有请求 Header //// .exposedHeaders("*") // 允许所有响应 Header // .maxAge(1800L); // 有效期 1800 秒,2 小时 // } @Bean public FilterRegistrationBean corsFilter() { // 创建 UrlBasedCorsConfigurationSource 配置源,类似 CorsRegistry 注册表 UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); // 创建 CorsConfiguration 配置,相当于 CorsRegistration 注册信息 CorsConfiguration config = new CorsConfiguration(); config.setAllowedOrigins(Collections.singletonList("*")); // 允许所有请求来源 config.setAllowCredentials(true); // 允许发送 Cookie config.addAllowedMethod("*"); // 允许所有请求 Method config.setAllowedHeaders(Collections.singletonList("*")); // 允许所有请求 Header // config.setExposedHeaders(Collections.singletonList("*")); // 允许所有响应 Header config.setMaxAge(1800L); // 有效期 1800 秒,2 小时 source.registerCorsConfiguration("/**", config); // 创建 FilterRegistrationBean 对象 FilterRegistrationBean bean = new FilterRegistrationBean<>( new CorsFilter(source)); // 创建 CorsFilter 过滤器 bean.setOrder(0); // 设置 order 排序。这个顺序很重要哦,为避免麻烦请设置在最前 return bean; } // @Override // public void configureMessageConverters(List> converters) { // // 增加 XML 消息转换器 // Jackson2ObjectMapperBuilder xmlBuilder = Jackson2ObjectMapperBuilder.xml(); // xmlBuilder.indentOutput(true); // converters.add(new MappingJackson2XmlHttpMessageConverter(xmlBuilder.build())); // } // @Override // public void configureMessageConverters(List> converters) { // // 增加 XML 消息转换器 // Jackson2ObjectMapperBuilder xmlBuilder = Jackson2ObjectMapperBuilder.xml(); // xmlBuilder.indentOutput(true); // converters.add(new MappingJackson2XmlHttpMessageConverter(xmlBuilder.build())); // } @Override public void configureMessageConverters(List> converters) { // 创建 FastJsonHttpMessageConverter 对象 FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter(); // 自定义 FastJson 配置 FastJsonConfig fastJsonConfig = new FastJsonConfig(); fastJsonConfig.setCharset(Charset.defaultCharset()); // 设置字符集 fastJsonConfig.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect); // 剔除循环引用 fastJsonHttpMessageConverter.setFastJsonConfig(fastJsonConfig); // 设置支持的 MediaType fastJsonHttpMessageConverter.setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_JSON, MediaType.APPLICATION_JSON_UTF8)); // 添加到 converters 中 converters.add(0, fastJsonHttpMessageConverter); // 注意,添加到最开头,放在 MappingJackson2XmlHttpMessageConverter 前面 } } ================================================ FILE: lab-23/lab-springmvc-23-02/src/main/java/cn/iocoder/springboot/lab23/springmvc/constants/ServiceExceptionEnum.java ================================================ package cn.iocoder.springboot.lab23.springmvc.constants; /** * 业务异常枚举 */ public enum ServiceExceptionEnum { // ========== 系统级别 ========== SUCCESS(0, "成功"), SYS_ERROR(2001001000, "服务端发生异常"), MISSING_REQUEST_PARAM_ERROR(2001001001, "参数缺失"), // ========== 用户模块 ========== USER_NOT_FOUND(1001002000, "用户不存在"), // ========== 订单模块 ========== // ========== 商品模块 ========== ; /** * 错误码 */ private final int code; /** * 错误提示 */ private final String message; ServiceExceptionEnum(int code, String message) { this.code = code; this.message = message; } public int getCode() { return code; } public String getMessage() { return message; } } ================================================ FILE: lab-23/lab-springmvc-23-02/src/main/java/cn/iocoder/springboot/lab23/springmvc/controller/ProductController.java ================================================ package cn.iocoder.springboot.lab23.springmvc.controller; import cn.iocoder.springboot.lab23.springmvc.vo.ProductVO; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; /** * 产品 Controller */ @Deprecated @RestController @RequestMapping("/products") public class ProductController { @PostMapping(value = "/add", consumes = {MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE}, produces = {MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE} ) public ProductVO add(@RequestBody ProductVO product) { return product; } } ================================================ FILE: lab-23/lab-springmvc-23-02/src/main/java/cn/iocoder/springboot/lab23/springmvc/controller/UserController.java ================================================ package cn.iocoder.springboot.lab23.springmvc.controller; import cn.iocoder.springboot.lab23.springmvc.constants.ServiceExceptionEnum; import cn.iocoder.springboot.lab23.springmvc.core.exception.ServiceException; import cn.iocoder.springboot.lab23.springmvc.core.vo.CommonResult; import cn.iocoder.springboot.lab23.springmvc.vo.UserVO; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; import java.util.UUID; /** * 用户 Controller */ @RestController @RequestMapping("/users") //@CrossOrigin(value = "*") public class UserController { private Logger logger = LoggerFactory.getLogger(getClass()); /** * 获得指定用户编号的用户 * * 提供不使用 CommonResult 包装 * * @param id 用户编号 * @return 用户 */ @GetMapping("/get") public UserVO get(@RequestParam("id") Integer id) { // 查询并返回用户 return new UserVO().setId(id).setUsername(UUID.randomUUID().toString()); } /** * 获得指定用户编号的用户 * * 提供使用 CommonResult 包装 * * @param id 用户编号 * @return 用户 */ @GetMapping("/get2") public CommonResult get2(@RequestParam("id") Integer id) { // 查询用户 UserVO user = new UserVO().setId(id).setUsername(UUID.randomUUID().toString()); // 返回结果 return CommonResult.success(user); } /** * 获得指定用户编号的用户 * * 测试个问题 * * @param id 用户编号 * @return 用户 */ @PostMapping("/get") public UserVO get3(@RequestParam("id") Integer id) { // 查询并返回用户 return new UserVO().setId(id).setUsername(UUID.randomUUID().toString()); } /** * 测试抛出 NullPointerException 异常 */ @GetMapping("/exception-01") public UserVO exception01() { throw new NullPointerException("没有粗面鱼丸"); } /** * 测试抛出 ServiceException 异常 */ @GetMapping("/exception-02") public UserVO exception02() { throw new ServiceException(ServiceExceptionEnum.USER_NOT_FOUND); } @GetMapping("/do_something") public void doSomething() { logger.info("[doSomething]"); } @GetMapping("/current_user") public UserVO currentUser() { logger.info("[currentUser]"); return new UserVO().setId(10).setUsername(UUID.randomUUID().toString()); } @GetMapping("/exception-03") public void exception03() { logger.info("[exception03]"); throw new ServiceException(ServiceExceptionEnum.USER_NOT_FOUND); } @PostMapping(value = "/add", // ↓ 增加 "application/xml"、"application/json" ,针对 Content-Type 请求头 consumes = {MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE}, // ↓ 增加 "application/xml"、"application/json" ,针对 Accept 请求头 produces = {MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE} ) public UserVO add(@RequestBody UserVO user) { return user; } } ================================================ FILE: lab-23/lab-springmvc-23-02/src/main/java/cn/iocoder/springboot/lab23/springmvc/controller2/TestController.java ================================================ package cn.iocoder.springboot.lab23.springmvc.controller2; import cn.iocoder.springboot.lab23.springmvc.core.web.GlobalResponseBodyHandler; import cn.iocoder.springboot.lab23.springmvc.vo.UserVO; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.UUID; /** * 测试 Controller * * 这个类的目的,主要是为了测试 {@link GlobalResponseBodyHandler} ,不拦截处理这个包 */ @RestController @RequestMapping("/test") //@CrossOrigin(origins = "*", allowCredentials = "true") // 允许所有来源,允许发送 Cookie public class TestController { /** * 获得指定用户编号的用户 * * @return 用户 */ @GetMapping("/get") // @CrossOrigin(allowCredentials = "false") // 允许所有来源,不允许发送 Cookie public UserVO get() { return new UserVO().setId(1).setUsername(UUID.randomUUID().toString()); } } ================================================ FILE: lab-23/lab-springmvc-23-02/src/main/java/cn/iocoder/springboot/lab23/springmvc/core/exception/ServiceException.java ================================================ package cn.iocoder.springboot.lab23.springmvc.core.exception; import cn.iocoder.springboot.lab23.springmvc.constants.ServiceExceptionEnum; /** * 服务异常 * * 参考 https://www.kancloud.cn/onebase/ob/484204 文章 * * 一共 10 位,分成四段 * * 第一段,1 位,类型 * 1 - 业务级别异常 * 2 - 系统级别异常 * 第二段,3 位,系统类型 * 001 - 用户系统 * 002 - 商品系统 * 003 - 订单系统 * 004 - 支付系统 * 005 - 优惠劵系统 * ... - ... * 第三段,3 位,模块 * 不限制规则。 * 一般建议,每个系统里面,可能有多个模块,可以再去做分段。以用户系统为例子: * 001 - OAuth2 模块 * 002 - User 模块 * 003 - MobileCode 模块 * 第四段,3 位,错误码 * 不限制规则。 * 一般建议,每个模块自增。 */ public final class ServiceException extends RuntimeException { /** * 错误码 */ private final Integer code; public ServiceException(ServiceExceptionEnum serviceExceptionEnum) { // 使用父类的 message 字段 super(serviceExceptionEnum.getMessage()); // 设置错误码 this.code = serviceExceptionEnum.getCode(); } public Integer getCode() { return code; } } ================================================ FILE: lab-23/lab-springmvc-23-02/src/main/java/cn/iocoder/springboot/lab23/springmvc/core/interceptor/FirstInterceptor.java ================================================ package cn.iocoder.springboot.lab23.springmvc.core.interceptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class FirstInterceptor implements HandlerInterceptor { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { logger.info("[preHandle][handler({})]", handler); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { logger.info("[postHandle][handler({})]", handler); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { logger.info("[afterCompletion][handler({})]", handler, ex); } } ================================================ FILE: lab-23/lab-springmvc-23-02/src/main/java/cn/iocoder/springboot/lab23/springmvc/core/interceptor/SecondInterceptor.java ================================================ package cn.iocoder.springboot.lab23.springmvc.core.interceptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class SecondInterceptor implements HandlerInterceptor { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { logger.info("[preHandle][handler({})]", handler); return false; // 故意返回 false } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { logger.info("[postHandle][handler({})]", handler); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { logger.info("[afterCompletion][handler({})]", handler, ex); } } ================================================ FILE: lab-23/lab-springmvc-23-02/src/main/java/cn/iocoder/springboot/lab23/springmvc/core/interceptor/ThirdInterceptor.java ================================================ package cn.iocoder.springboot.lab23.springmvc.core.interceptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class ThirdInterceptor implements HandlerInterceptor { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { logger.info("[preHandle][handler({})]", handler); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { logger.info("[postHandle][handler({})]", handler); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { logger.info("[afterCompletion][handler({})]", handler, ex); throw new RuntimeException("故意抛个错误"); // 故意抛出异常 } } ================================================ FILE: lab-23/lab-springmvc-23-02/src/main/java/cn/iocoder/springboot/lab23/springmvc/core/package-info.java ================================================ /** * 提供核心封装 */ package cn.iocoder.springboot.lab23.springmvc.core; ================================================ FILE: lab-23/lab-springmvc-23-02/src/main/java/cn/iocoder/springboot/lab23/springmvc/core/servlet/TestFilter02.java ================================================ package cn.iocoder.springboot.lab23.springmvc.core.servlet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import java.io.IOException; @WebFilter("/test/*") public class TestFilter02 implements Filter { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { logger.info("[doFilter]"); filterChain.doFilter(servletRequest, servletResponse); } } ================================================ FILE: lab-23/lab-springmvc-23-02/src/main/java/cn/iocoder/springboot/lab23/springmvc/core/servlet/TestServlet02.java ================================================ package cn.iocoder.springboot.lab23.springmvc.core.servlet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet(urlPatterns = "/test/02") public class TestServlet02 extends HttpServlet { private Logger logger = LoggerFactory.getLogger(getClass()); @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { logger.info("[doGet][uri: {}]", req.getRequestURI()); } } ================================================ FILE: lab-23/lab-springmvc-23-02/src/main/java/cn/iocoder/springboot/lab23/springmvc/core/servlet/TestServletContextListener02.java ================================================ package cn.iocoder.springboot.lab23.springmvc.core.servlet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.annotation.WebListener; @WebListener public class TestServletContextListener02 implements ServletContextListener { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public void contextInitialized(ServletContextEvent sce) { logger.info("[contextInitialized]"); } @Override public void contextDestroyed(ServletContextEvent sce) { } } ================================================ FILE: lab-23/lab-springmvc-23-02/src/main/java/cn/iocoder/springboot/lab23/springmvc/core/vo/CommonResult.java ================================================ package cn.iocoder.springboot.lab23.springmvc.core.vo; import com.fasterxml.jackson.annotation.JsonIgnore; import org.springframework.util.Assert; import java.io.Serializable; /** * 通用返回结果 * * @param 结果泛型 */ public class CommonResult implements Serializable { public static Integer CODE_SUCCESS = 0; /** * 错误码 */ private Integer code; /** * 错误提示 */ private String message; /** * 返回数据 */ private T data; /** * 将传入的 result 对象,转换成另外一个泛型结果的对象 * * 因为 A 方法返回的 CommonResult 对象,不满足调用其的 B 方法的返回,所以需要进行转换。 * * @param result 传入的 result 对象 * @param 返回的泛型 * @return 新的 CommonResult 对象 */ public static CommonResult error(CommonResult result) { return error(result.getCode(), result.getMessage()); } public static CommonResult error(Integer code, String message) { Assert.isTrue(!CODE_SUCCESS.equals(code), "code 必须是错误的!"); CommonResult result = new CommonResult<>(); result.code = code; result.message = message; return result; } public static CommonResult success(T data) { CommonResult result = new CommonResult<>(); result.code = CODE_SUCCESS; result.data = data; result.message = ""; return result; } public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public T getData() { return data; } public void setData(T data) { this.data = data; } @JsonIgnore public boolean isSuccess() { return CODE_SUCCESS.equals(code); } @JsonIgnore public boolean isError() { return !isSuccess(); } @Override public String toString() { return "CommonResult{" + "code=" + code + ", message='" + message + '\'' + ", data=" + data + '}'; } } ================================================ FILE: lab-23/lab-springmvc-23-02/src/main/java/cn/iocoder/springboot/lab23/springmvc/core/web/GlobalExceptionHandler.java ================================================ package cn.iocoder.springboot.lab23.springmvc.core.web; import cn.iocoder.springboot.lab23.springmvc.constants.ServiceExceptionEnum; import cn.iocoder.springboot.lab23.springmvc.core.exception.ServiceException; import cn.iocoder.springboot.lab23.springmvc.core.vo.CommonResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletRequest; @ControllerAdvice(basePackages = "cn.iocoder.springboot.lab23.springmvc.controller") public class GlobalExceptionHandler { private Logger logger = LoggerFactory.getLogger(getClass()); /** * 处理 ServiceException 异常 */ @ResponseBody @ExceptionHandler(value = ServiceException.class) public CommonResult serviceExceptionHandler(HttpServletRequest req, ServiceException ex) { logger.debug("[serviceExceptionHandler]", ex); // 包装 CommonResult 结果 return CommonResult.error(ex.getCode(), ex.getMessage()); } /** * 处理 MissingServletRequestParameterException 异常 * * SpringMVC 参数不正确 */ @ResponseBody @ExceptionHandler(value = MissingServletRequestParameterException.class) public CommonResult missingServletRequestParameterExceptionHandler(HttpServletRequest req, MissingServletRequestParameterException ex) { logger.debug("[missingServletRequestParameterExceptionHandler]", ex); // 包装 CommonResult 结果 return CommonResult.error(ServiceExceptionEnum.MISSING_REQUEST_PARAM_ERROR.getCode(), ServiceExceptionEnum.MISSING_REQUEST_PARAM_ERROR.getMessage()); } /** * 处理其它 Exception 异常 */ @ResponseBody @ExceptionHandler(value = Exception.class) public CommonResult exceptionHandler(HttpServletRequest req, Exception e) { // 记录异常日志 logger.error("[exceptionHandler]", e); // 返回 ERROR CommonResult return CommonResult.error(ServiceExceptionEnum.SYS_ERROR.getCode(), ServiceExceptionEnum.SYS_ERROR.getMessage()); } } ================================================ FILE: lab-23/lab-springmvc-23-02/src/main/java/cn/iocoder/springboot/lab23/springmvc/core/web/GlobalResponseBodyHandler.java ================================================ package cn.iocoder.springboot.lab23.springmvc.core.web; import cn.iocoder.springboot.lab23.springmvc.core.vo.CommonResult; import org.springframework.core.MethodParameter; import org.springframework.http.MediaType; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; // 只拦截我们的 Controller 所在包,避免其它类似 swagger 提供的 API 被切面拦截 @ControllerAdvice(basePackages = "cn.iocoder.springboot.lab23.springmvc.controller") public class GlobalResponseBodyHandler implements ResponseBodyAdvice { @Override public boolean supports(MethodParameter returnType, Class converterType) { return true; } @Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { // 如果已经是 CommonResult 类型,则直接返回 if (body instanceof CommonResult) { return body; } // 如果不是,则包装成 CommonResult 类型 return CommonResult.success(body); } } ================================================ FILE: lab-23/lab-springmvc-23-02/src/main/java/cn/iocoder/springboot/lab23/springmvc/vo/ProductVO.java ================================================ package cn.iocoder.springboot.lab23.springmvc.vo; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; /** * 产品 VO */ public class ProductVO { /** * 商品编号 */ @JacksonXmlProperty(localName = "id") private Integer id; /** * 商品标题 */ @JacksonXmlProperty(localName = "title") private String title; public Integer getId() { return id; } public ProductVO setId(Integer id) { this.id = id; return this; } public String getTitle() { return title; } public ProductVO setTitle(String title) { this.title = title; return this; } } ================================================ FILE: lab-23/lab-springmvc-23-02/src/main/java/cn/iocoder/springboot/lab23/springmvc/vo/UserVO.java ================================================ package cn.iocoder.springboot.lab23.springmvc.vo; /** * 用户 VO */ public class UserVO { /** * 编号 */ private Integer id; /** * 账号 */ private String username; public Integer getId() { return id; } public UserVO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserVO setUsername(String username) { this.username = username; return this; } } ================================================ FILE: lab-23/lab-springmvc-23-02/src/test/java/cn/iocoder/springboot/lab23/springmvc/package-info.java ================================================ package cn.iocoder.springboot.lab23.springmvc; ================================================ FILE: lab-23/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-23 pom lab-springmvc-23-01 lab-springmvc-23-02 ================================================ FILE: lab-23/《芋道 Spring Boot SpringMVC 入门》.md ================================================ ================================================ FILE: lab-24/lab-24-apidoc-japidocs/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE 4.0.0 lab-24-apidoc-japidocs org.springframework.boot spring-boot-starter-web io.github.yedaxia japidocs 1.4.4 ================================================ FILE: lab-24/lab-24-apidoc-japidocs/src/main/java/cn/iocoder/springboot/lab24/Application.java ================================================ package cn.iocoder.springboot.lab24; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-24/lab-24-apidoc-japidocs/src/main/java/cn/iocoder/springboot/lab24/TestJApiDocs.java ================================================ package cn.iocoder.springboot.lab24; import io.github.yedaxia.apidocs.Docs; import io.github.yedaxia.apidocs.DocsConfig; import io.github.yedaxia.apidocs.plugin.markdown.MarkdownDocPlugin; public class TestJApiDocs { public static void main(String[] args) { // 1. 创建生成文档的配置 DocsConfig config = new DocsConfig(); config.setProjectPath("/Users/yunai/Java/SpringBoot-Labs/lab-24/lab-24-apidoc-japidocs"); // 项目所在目录 config.setDocsPath("/Users/yunai/Downloads/"); // 生成 HTML 接口文档的目标目录 config.setAutoGenerate(true); // 是否给所有 Controller 生成接口文档 config.setProjectName("示例项目"); // 项目名 config.setApiVersion("V1.0"); // API 版本号 config.addPlugin(new MarkdownDocPlugin()); // 使用 MD 插件,额外生成 MD 格式的接口文档 // 2. 执行生成 HTML 接口文档 Docs.buildHtmlDocs(config); } } ================================================ FILE: lab-24/lab-24-apidoc-japidocs/src/main/java/cn/iocoder/springboot/lab24/controller/UserController.java ================================================ package cn.iocoder.springboot.lab24.controller; import cn.iocoder.springboot.lab24.vo.UserCreateReqVO; import cn.iocoder.springboot.lab24.vo.UserListReqVO; import cn.iocoder.springboot.lab24.vo.UserRespVO; import org.springframework.web.bind.annotation.*; import java.util.List; /** * 用户 API */ @RestController @RequestMapping("/api/user/") public class UserController { /** * 获得用户列表 * * @param listReqVO 列表筛选条件 * @return 用户列表 */ @GetMapping("list") public List list(UserListReqVO listReqVO){ return null; } /** * 保存用户 * * @param createReqVO 创建用户信息 * @return 用户编号 */ @PostMapping("save") public Integer saveUser(@RequestBody UserCreateReqVO createReqVO){ return 1; } /** * 删除指定编号的用户 * * @param id 用户编号 * @return 是否成功 */ @DeleteMapping("delete") public Boolean deleteUser(@RequestParam Long id){ return true; } } ================================================ FILE: lab-24/lab-24-apidoc-japidocs/src/main/java/cn/iocoder/springboot/lab24/vo/UserCreateReqVO.java ================================================ package cn.iocoder.springboot.lab24.vo; /** * 用户创建请求 VO */ public class UserCreateReqVO { /** * 昵称 */ private String nickname; /** * 年龄 */ private Integer age; } ================================================ FILE: lab-24/lab-24-apidoc-japidocs/src/main/java/cn/iocoder/springboot/lab24/vo/UserListReqVO.java ================================================ package cn.iocoder.springboot.lab24.vo; /** * 用户列表请求 VO */ public class UserListReqVO { /** * 昵称,模糊匹配 */ private String nickname; } ================================================ FILE: lab-24/lab-24-apidoc-japidocs/src/main/java/cn/iocoder/springboot/lab24/vo/UserRespVO.java ================================================ package cn.iocoder.springboot.lab24.vo; /** * 用户响应 VO */ public class UserRespVO { /** * 用户编号 */ private Integer id; /** * 昵称 */ private String nickname; /** * 年龄 */ private Integer age; } ================================================ FILE: lab-24/lab-24-apidoc-showdoc/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.11.RELEASE 4.0.0 lab-24-apidoc-showdoc org.springframework.boot spring-boot-starter-web ================================================ FILE: lab-24/lab-24-apidoc-showdoc/showdoc_api.sh ================================================ #! /bin/bash # # 文档说明: https://www.showdoc.com.cn/page/741656402509783 # api_key="60fc53cea6af4758c1686cb22ba20566472255580" #api_key api_token="0bbb5f564a9ee66333115b1abb8f8d541979489118" #api_token url="https://www.showdoc.com.cn/server/?s=/api/open/fromComments" #同步到的url。使用www.showdoc.com.cn的不需要修改,使用私有版的请修改 # # # # # # 如果第一个参数是目录,则使用参数目录。若无,则使用脚本所在的目录。 if [[ -z "$1" ]] || [[ ! -d "$1" ]] ; then #目录判断,如果$1不是目录或者是空,则使用当前目录 curren_dir=$(dirname $(readlink -f $0)) else curren_dir=$(cd $1; pwd) fi #echo "$curren_dir" # 递归搜索文件 searchfile() { old_IFS="$IFS" IFS=$'\n' #IFS修改 for chkfile in $1/* do filesize=`ls -l $chkfile | awk '{ print $5 }'` maxsize=$((1024*1024*1)) # 1M以下的文本文件才会被扫描 if [[ -f "$chkfile" ]] && [ $filesize -le $maxsize ] && [[ -n $(file $chkfile | grep text) ]] ; then # 只对text文件类型操作 echo "正在扫描 $chkfile" result=$(sed -n -e '/\/\*\*/,/\*\//p' $chkfile | grep showdoc) # 正则匹配 if [ ! -z "$result" ] ; then txt=$(sed -n -e '/\/\*\*/,/\*\//p' $chkfile) #echo "sed -n -e '/\/\*\*/,/\*\//p' $chkfile" #echo $result if [[ $txt =~ "@url" ]] && [[ $txt =~ "@title" ]]; then echo -e "\033[32m $chkfile 扫描到内容 , 正在生成文档 \033[0m " txt2=${txt//&/_this_and_change_} # 通过接口生成文档 curl -H 'Content-Type: application/x-www-form-urlencoded; charset=UTF-8' "${url}" --data-binary @- < org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE 4.0.0 lab-24-apidoc-swagger org.springframework.boot spring-boot-starter-web io.springfox springfox-swagger2 2.9.2 io.springfox springfox-swagger-ui 2.9.2 ================================================ FILE: lab-24/lab-24-apidoc-swagger/src/main/java/cn/iocoder/springboot/lab24/apidoc/Application.java ================================================ package cn.iocoder.springboot.lab24.apidoc; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-24/lab-24-apidoc-swagger/src/main/java/cn/iocoder/springboot/lab24/apidoc/config/SwaggerConfiguration.java ================================================ package cn.iocoder.springboot.lab24.apidoc.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.service.Contact; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; @Configuration @EnableSwagger2 // 标记项目启用 Swagger API 接口文档 public class SwaggerConfiguration { @Bean public Docket createRestApi() { // 创建 Docket 对象 return new Docket(DocumentationType.SWAGGER_2) // 文档类型,使用 Swagger2 .apiInfo(this.apiInfo()) // 设置 API 信息 // 扫描 Controller 包路径,获得 API 接口 .select() .apis(RequestHandlerSelectors.basePackage("cn.iocoder.springboot.lab24.apidoc.controller")) .paths(PathSelectors.any()) // 构建出 Docket 对象 .build(); } /** * 创建 API 信息 */ private ApiInfo apiInfo() { return new ApiInfoBuilder() .title("测试接口文档示例") .description("我是一段描述") .version("1.0.0") // 版本号 .contact(new Contact("芋艿", "http://www.iocoder.cn", "zhijiantianya@gmail.com")) // 联系人 .build(); } } ================================================ FILE: lab-24/lab-24-apidoc-swagger/src/main/java/cn/iocoder/springboot/lab24/apidoc/controller/TestController.java ================================================ package cn.iocoder.springboot.lab24.apidoc.controller; import cn.iocoder.springboot.lab24.apidoc.dto.UserAddDTO; import cn.iocoder.springboot.lab24.apidoc.dto.UserUpdateDTO; import cn.iocoder.springboot.lab24.apidoc.vo.UserVO; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiOperation; import org.springframework.web.bind.annotation.*; import java.util.ArrayList; import java.util.List; import java.util.UUID; //@RestController @RequestMapping("/tests") //@Api(tags = "用户 API 接口") public class TestController { @GetMapping("/list") @ApiOperation(value = "查询用户列表", notes = "目前仅仅是作为测试,所以返回用户全列表") public List list() { // 查询列表 List result = new ArrayList<>(); result.add(new UserVO().setId(1).setUsername("yudaoyuanma")); result.add(new UserVO().setId(2).setUsername("woshiyutou")); result.add(new UserVO().setId(3).setUsername("chifanshuijiao")); // 返回列表 return result; } @GetMapping("/get") @ApiOperation("获得指定用户编号的用户") @ApiImplicitParam(paramType = "query", dataType = "Integer", name = "id", value = "用户编号", required = true, example = "1024") public UserVO get(@RequestParam("id") Integer id) { // 查询并返回用户 return new UserVO().setId(id).setUsername(UUID.randomUUID().toString()); } @PostMapping("add") @ApiOperation("添加用户") public Integer add(UserAddDTO addDTO) { // 插入用户记录,返回编号 Integer returnId = UUID.randomUUID().hashCode(); // 返回用户编号 return returnId; } @PostMapping("/update") @ApiOperation("更新指定用户编号的用户") public Boolean update(UserUpdateDTO updateDTO) { // 更新用户记录 Boolean success = true; // 返回更新是否成功 return success; } @PostMapping("/delete") @ApiOperation("删除指定用户编号的用户") @ApiImplicitParam(paramType = "query", dataTypeClass = Integer.class, name = "id", value = "用户编号", required = true, example = "1024") public Boolean delete(@RequestParam("id") Integer id) { // 删除用户记录 Boolean success = false; // 返回是否更新成功 return success; } } ================================================ FILE: lab-24/lab-24-apidoc-swagger/src/main/java/cn/iocoder/springboot/lab24/apidoc/controller/UserController.java ================================================ package cn.iocoder.springboot.lab24.apidoc.controller; import cn.iocoder.springboot.lab24.apidoc.dto.UserAddDTO; import cn.iocoder.springboot.lab24.apidoc.dto.UserUpdateDTO; import cn.iocoder.springboot.lab24.apidoc.vo.UserVO; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiOperation; import org.springframework.web.bind.annotation.*; import java.util.ArrayList; import java.util.List; import java.util.UUID; @RestController @RequestMapping("/users") @Api(tags = "用户 API 接口") public class UserController { @GetMapping("/list") @ApiOperation(value = "查询用户列表", notes = "目前仅仅是作为测试,所以返回用户全列表") public List list() { // 查询列表 List result = new ArrayList<>(); result.add(new UserVO().setId(1).setUsername("yudaoyuanma")); result.add(new UserVO().setId(2).setUsername("woshiyutou")); result.add(new UserVO().setId(3).setUsername("chifanshuijiao")); // 返回列表 return result; } @GetMapping("/get") @ApiOperation("获得指定用户编号的用户") @ApiImplicitParam(name = "id", value = "用户编号", paramType = "query", dataTypeClass = Integer.class, required = true, example = "1024") public UserVO get(@RequestParam("id") Integer id) { // 查询并返回用户 return new UserVO().setId(id).setUsername(UUID.randomUUID().toString()); } @PostMapping("add") @ApiOperation("添加用户") public Integer add(UserAddDTO addDTO) { // 插入用户记录,返回编号 Integer returnId = UUID.randomUUID().hashCode(); // 返回用户编号 return returnId; } @PostMapping("/update") @ApiOperation("更新指定用户编号的用户") public Boolean update(UserUpdateDTO updateDTO) { // 更新用户记录 Boolean success = true; // 返回更新是否成功 return success; } @PostMapping("/delete") @ApiOperation(value = "删除指定用户编号的用户") @ApiImplicitParam(name = "id", value = "用户编号", paramType = "query", dataTypeClass = Integer.class, required = true, example = "1024") public Boolean delete(@RequestParam("id") Integer id) { // 删除用户记录 Boolean success = false; // 返回是否更新成功 return success; } } ================================================ FILE: lab-24/lab-24-apidoc-swagger/src/main/java/cn/iocoder/springboot/lab24/apidoc/dto/UserAddDTO.java ================================================ package cn.iocoder.springboot.lab24.apidoc.dto; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; @ApiModel("用户添加 DTO") public class UserAddDTO { @ApiModelProperty(value = "账号", required = true, example = "yudaoyuanma") private String username; @ApiModelProperty(value = "密码", required = true, example = "nicai") private String password; public String getUsername() { return username; } public UserAddDTO setUsername(String username) { this.username = username; return this; } public String getPassword() { return password; } public UserAddDTO setPassword(String password) { this.password = password; return this; } } ================================================ FILE: lab-24/lab-24-apidoc-swagger/src/main/java/cn/iocoder/springboot/lab24/apidoc/dto/UserUpdateDTO.java ================================================ package cn.iocoder.springboot.lab24.apidoc.dto; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; @ApiModel("用户更新 DTO") public class UserUpdateDTO { @ApiModelProperty(value = "用户编号", required = true, example = "1024") private Integer id; @ApiModelProperty(value = "账号", required = true, example = "yudaoyuanma") private String username; @ApiModelProperty(value = "密码", required = true, example = "nicai") private String password; public Integer getId() { return id; } public UserUpdateDTO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserUpdateDTO setUsername(String username) { this.username = username; return this; } public String getPassword() { return password; } public UserUpdateDTO setPassword(String password) { this.password = password; return this; } } ================================================ FILE: lab-24/lab-24-apidoc-swagger/src/main/java/cn/iocoder/springboot/lab24/apidoc/vo/UserVO.java ================================================ package cn.iocoder.springboot.lab24.apidoc.vo; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; @ApiModel("用户 VO") public class UserVO { @ApiModelProperty(value = "用户编号", required = true, example = "1024") private Integer id; @ApiModelProperty(value = "账号", required = true, example = "yudaoyuanma") private String username; public Integer getId() { return id; } public UserVO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserVO setUsername(String username) { this.username = username; return this; } } ================================================ FILE: lab-24/lab-24-apidoc-swagger-knife4j/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE 4.0.0 lab-24-apidoc-swagger-knife4j org.springframework.boot spring-boot-starter-web com.github.xiaoymin knife4j-spring 1.9.6 com.github.xiaoymin knife4j-spring-ui 1.9.6 ================================================ FILE: lab-24/lab-24-apidoc-swagger-knife4j/src/main/java/cn/iocoder/springboot/lab24/apidoc/Application.java ================================================ package cn.iocoder.springboot.lab24.apidoc; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-24/lab-24-apidoc-swagger-knife4j/src/main/java/cn/iocoder/springboot/lab24/apidoc/config/SwaggerConfiguration.java ================================================ package cn.iocoder.springboot.lab24.apidoc.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.service.Contact; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; @Configuration @EnableSwagger2 // 标记项目启用 Swagger API 接口文档 public class SwaggerConfiguration { @Bean public Docket createRestApi() { // 创建 Docket 对象 return new Docket(DocumentationType.SWAGGER_2) // 文档类型,使用 Swagger2 .apiInfo(this.apiInfo()) // 设置 API 信息 // 扫描 Controller 包路径,获得 API 接口 .select() .apis(RequestHandlerSelectors.basePackage("cn.iocoder.springboot.lab24.apidoc.controller")) .paths(PathSelectors.any()) // 构建出 Docket 对象 .build(); } /** * 创建 API 信息 */ private ApiInfo apiInfo() { return new ApiInfoBuilder() .title("测试接口文档示例") .description("我是一段描述") .version("1.0.0") // 版本号 .contact(new Contact("芋艿", "http://www.iocoder.cn", "zhijiantianya@gmail.com")) // 联系人 .build(); } } ================================================ FILE: lab-24/lab-24-apidoc-swagger-knife4j/src/main/java/cn/iocoder/springboot/lab24/apidoc/controller/TestController.java ================================================ package cn.iocoder.springboot.lab24.apidoc.controller; import cn.iocoder.springboot.lab24.apidoc.dto.UserAddDTO; import cn.iocoder.springboot.lab24.apidoc.dto.UserUpdateDTO; import cn.iocoder.springboot.lab24.apidoc.vo.UserVO; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiOperation; import org.springframework.web.bind.annotation.*; import java.util.ArrayList; import java.util.List; import java.util.UUID; //@RestController @RequestMapping("/tests") //@Api(tags = "用户 API 接口") public class TestController { @GetMapping("/list") @ApiOperation(value = "查询用户列表", notes = "目前仅仅是作为测试,所以返回用户全列表") public List list() { // 查询列表 List result = new ArrayList<>(); result.add(new UserVO().setId(1).setUsername("yudaoyuanma")); result.add(new UserVO().setId(2).setUsername("woshiyutou")); result.add(new UserVO().setId(3).setUsername("chifanshuijiao")); // 返回列表 return result; } @GetMapping("/get") @ApiOperation("获得指定用户编号的用户") @ApiImplicitParam(paramType = "query", dataType = "Integer", name = "id", value = "用户编号", required = true, example = "1024") public UserVO get(@RequestParam("id") Integer id) { // 查询并返回用户 return new UserVO().setId(id).setUsername(UUID.randomUUID().toString()); } @PostMapping("add") @ApiOperation("添加用户") public Integer add(UserAddDTO addDTO) { // 插入用户记录,返回编号 Integer returnId = UUID.randomUUID().hashCode(); // 返回用户编号 return returnId; } @PostMapping("/update") @ApiOperation("更新指定用户编号的用户") public Boolean update(UserUpdateDTO updateDTO) { // 更新用户记录 Boolean success = true; // 返回更新是否成功 return success; } @PostMapping("/delete") @ApiOperation("删除指定用户编号的用户") @ApiImplicitParam(paramType = "query", dataTypeClass = Integer.class, name = "id", value = "用户编号", required = true, example = "1024") public Boolean delete(@RequestParam("id") Integer id) { // 删除用户记录 Boolean success = false; // 返回是否更新成功 return success; } } ================================================ FILE: lab-24/lab-24-apidoc-swagger-knife4j/src/main/java/cn/iocoder/springboot/lab24/apidoc/controller/UserController.java ================================================ package cn.iocoder.springboot.lab24.apidoc.controller; import cn.iocoder.springboot.lab24.apidoc.dto.UserAddDTO; import cn.iocoder.springboot.lab24.apidoc.dto.UserUpdateDTO; import cn.iocoder.springboot.lab24.apidoc.vo.UserVO; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiOperation; import org.springframework.web.bind.annotation.*; import java.util.ArrayList; import java.util.List; import java.util.UUID; @RestController @RequestMapping("/users") @Api(tags = "用户 API 接口") public class UserController { @GetMapping("/list") @ApiOperation(value = "查询用户列表", notes = "目前仅仅是作为测试,所以返回用户全列表") public List list() { // 查询列表 List result = new ArrayList<>(); result.add(new UserVO().setId(1).setUsername("yudaoyuanma")); result.add(new UserVO().setId(2).setUsername("woshiyutou")); result.add(new UserVO().setId(3).setUsername("chifanshuijiao")); // 返回列表 return result; } @GetMapping("/get") @ApiOperation("获得指定用户编号的用户") @ApiImplicitParam(name = "id", value = "用户编号", paramType = "query", dataTypeClass = Integer.class, required = true, example = "1024") public UserVO get(@RequestParam("id") Integer id) { // 查询并返回用户 return new UserVO().setId(id).setUsername(UUID.randomUUID().toString()); } @PostMapping("add") @ApiOperation("添加用户") public Integer add(UserAddDTO addDTO) { // 插入用户记录,返回编号 Integer returnId = UUID.randomUUID().hashCode(); // 返回用户编号 return returnId; } @PostMapping("/update") @ApiOperation("更新指定用户编号的用户") public Boolean update(UserUpdateDTO updateDTO) { // 更新用户记录 Boolean success = true; // 返回更新是否成功 return success; } @PostMapping("/delete") @ApiOperation(value = "删除指定用户编号的用户") @ApiImplicitParam(name = "id", value = "用户编号", paramType = "query", dataTypeClass = Integer.class, required = true, example = "1024") public Boolean delete(@RequestParam("id") Integer id) { // 删除用户记录 Boolean success = false; // 返回是否更新成功 return success; } } ================================================ FILE: lab-24/lab-24-apidoc-swagger-knife4j/src/main/java/cn/iocoder/springboot/lab24/apidoc/dto/UserAddDTO.java ================================================ package cn.iocoder.springboot.lab24.apidoc.dto; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; @ApiModel("用户添加 DTO") public class UserAddDTO { @ApiModelProperty(value = "账号", required = true, example = "yudaoyuanma") private String username; @ApiModelProperty(value = "密码", required = true, example = "nicai") private String password; public String getUsername() { return username; } public UserAddDTO setUsername(String username) { this.username = username; return this; } public String getPassword() { return password; } public UserAddDTO setPassword(String password) { this.password = password; return this; } } ================================================ FILE: lab-24/lab-24-apidoc-swagger-knife4j/src/main/java/cn/iocoder/springboot/lab24/apidoc/dto/UserUpdateDTO.java ================================================ package cn.iocoder.springboot.lab24.apidoc.dto; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; @ApiModel("用户更新 DTO") public class UserUpdateDTO { @ApiModelProperty(value = "用户编号", required = true, example = "1024") private Integer id; @ApiModelProperty(value = "账号", required = true, example = "yudaoyuanma") private String username; @ApiModelProperty(value = "密码", required = true, example = "nicai") private String password; public Integer getId() { return id; } public UserUpdateDTO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserUpdateDTO setUsername(String username) { this.username = username; return this; } public String getPassword() { return password; } public UserUpdateDTO setPassword(String password) { this.password = password; return this; } } ================================================ FILE: lab-24/lab-24-apidoc-swagger-knife4j/src/main/java/cn/iocoder/springboot/lab24/apidoc/vo/UserVO.java ================================================ package cn.iocoder.springboot.lab24.apidoc.vo; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; @ApiModel("用户 VO") public class UserVO { @ApiModelProperty(value = "用户编号", required = true, example = "1024") private Integer id; @ApiModelProperty(value = "账号", required = true, example = "yudaoyuanma") private String username; public Integer getId() { return id; } public UserVO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserVO setUsername(String username) { this.username = username; return this; } } ================================================ FILE: lab-24/lab-24-apidoc-swagger-starter/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.11.RELEASE 4.0.0 lab-24-apidoc-swagger-starter org.springframework.boot spring-boot-starter-web io.springfox springfox-boot-starter 3.0.0 ================================================ FILE: lab-24/lab-24-apidoc-swagger-starter/src/main/java/cn/iocoder/springboot/lab24/Application.java ================================================ package cn.iocoder.springboot.lab24; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-24/lab-24-apidoc-swagger-starter/src/main/java/cn/iocoder/springboot/lab24/config/SwaggerConfiguration.java ================================================ package cn.iocoder.springboot.lab24.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.service.Contact; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; @Configuration // @EnableSwagger2 无需使用该注解 public class SwaggerConfiguration { @Bean public Docket createRestApi() { // 创建 Docket 对象 return new Docket(DocumentationType.SWAGGER_2) // 文档类型,使用 Swagger2 .apiInfo(this.apiInfo()) // 设置 API 信息 // 扫描 Controller 包路径,获得 API 接口 .select() .apis(RequestHandlerSelectors.basePackage("cn.iocoder.springboot.lab24.controller")) .paths(PathSelectors.any()) // 构建出 Docket 对象 .build(); } /** * 创建 API 信息 */ private ApiInfo apiInfo() { return new ApiInfoBuilder() .title("测试接口文档示例") .description("我是一段描述") .version("1.0.0") // 版本号 .contact(new Contact("芋艿", "http://www.iocoder.cn", "zhijiantianya@gmail.com")) // 联系人 .build(); } } ================================================ FILE: lab-24/lab-24-apidoc-swagger-starter/src/main/java/cn/iocoder/springboot/lab24/controller/UserController.java ================================================ package cn.iocoder.springboot.lab24.controller; import cn.iocoder.springboot.lab24.dto.UserAddDTO; import cn.iocoder.springboot.lab24.dto.UserUpdateDTO; import cn.iocoder.springboot.lab24.vo.UserVO; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiOperation; import org.springframework.web.bind.annotation.*; import java.util.ArrayList; import java.util.List; import java.util.UUID; @RestController @RequestMapping("/users") @Api(tags = "用户 API 接口") public class UserController { @GetMapping("/list") @ApiOperation(value = "查询用户列表", notes = "目前仅仅是作为测试,所以返回用户全列表") public List list() { // 查询列表 List result = new ArrayList<>(); result.add(new UserVO().setId(1).setUsername("yudaoyuanma")); result.add(new UserVO().setId(2).setUsername("woshiyutou")); result.add(new UserVO().setId(3).setUsername("chifanshuijiao")); // 返回列表 return result; } @GetMapping("/get") @ApiOperation("获得指定用户编号的用户") @ApiImplicitParam(name = "id", value = "用户编号", paramType = "query", dataTypeClass = Integer.class, required = true, example = "1024") public UserVO get(@RequestParam("id") Integer id) { // 查询并返回用户 return new UserVO().setId(id).setUsername(UUID.randomUUID().toString()); } @PostMapping("add") @ApiOperation("添加用户") public Integer add(UserAddDTO addDTO) { // 插入用户记录,返回编号 Integer returnId = UUID.randomUUID().hashCode(); // 返回用户编号 return returnId; } @PostMapping("/update") @ApiOperation("更新指定用户编号的用户") public Boolean update(UserUpdateDTO updateDTO) { // 更新用户记录 Boolean success = true; // 返回更新是否成功 return success; } @PostMapping("/delete") @ApiOperation(value = "删除指定用户编号的用户") @ApiImplicitParam(name = "id", value = "用户编号", paramType = "query", dataTypeClass = Integer.class, required = true, example = "1024") public Boolean delete(@RequestParam("id") Integer id) { // 删除用户记录 Boolean success = false; // 返回是否更新成功 return success; } } ================================================ FILE: lab-24/lab-24-apidoc-swagger-starter/src/main/java/cn/iocoder/springboot/lab24/dto/UserAddDTO.java ================================================ package cn.iocoder.springboot.lab24.dto; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; @ApiModel("用户添加 DTO") public class UserAddDTO { @ApiModelProperty(value = "账号", required = true, example = "yudaoyuanma") private String username; @ApiModelProperty(value = "密码", required = true, example = "nicai") private String password; public String getUsername() { return username; } public UserAddDTO setUsername(String username) { this.username = username; return this; } public String getPassword() { return password; } public UserAddDTO setPassword(String password) { this.password = password; return this; } } ================================================ FILE: lab-24/lab-24-apidoc-swagger-starter/src/main/java/cn/iocoder/springboot/lab24/dto/UserUpdateDTO.java ================================================ package cn.iocoder.springboot.lab24.dto; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; @ApiModel("用户更新 DTO") public class UserUpdateDTO { @ApiModelProperty(value = "用户编号", required = true, example = "1024") private Integer id; @ApiModelProperty(value = "账号", required = true, example = "yudaoyuanma") private String username; @ApiModelProperty(value = "密码", required = true, example = "nicai") private String password; public Integer getId() { return id; } public UserUpdateDTO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserUpdateDTO setUsername(String username) { this.username = username; return this; } public String getPassword() { return password; } public UserUpdateDTO setPassword(String password) { this.password = password; return this; } } ================================================ FILE: lab-24/lab-24-apidoc-swagger-starter/src/main/java/cn/iocoder/springboot/lab24/vo/UserVO.java ================================================ package cn.iocoder.springboot.lab24.vo; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; @ApiModel("用户 VO") public class UserVO { @ApiModelProperty(value = "用户编号", required = true, example = "1024") private Integer id; @ApiModelProperty(value = "账号", required = true, example = "yudaoyuanma") private String username; public Integer getId() { return id; } public UserVO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserVO setUsername(String username) { this.username = username; return this; } } ================================================ FILE: lab-24/lab-24-apidoc-swagger-starter/src/main/resources/application.yaml ================================================ # 对应 SpringfoxConfigurationProperties 配置类 springfox: documentation: swagger-ui: enabled: true # 是否开启 Swagger UI 功能。默认为 true ================================================ FILE: lab-24/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-24 pom lab-24-apidoc-swagger lab-24-apidoc-swagger-knife4j lab-24-apidoc-japidocs lab-24-apidoc-swagger-starter lab-24-apidoc-showdoc ================================================ FILE: lab-24/《芋道 Spring Boot API 接口文档 JApiDocs 入门》.md ================================================ ================================================ FILE: lab-24/《芋道 Spring Boot API 接口文档 ShowDoc 入门》.md ================================================ ================================================ FILE: lab-24/《芋道 Spring Boot API 接口文档 Swagger Starter 入门》.md ================================================ ================================================ FILE: lab-24/《芋道 Spring Boot API 接口文档 Swagger 入门》.md ================================================ ================================================ FILE: lab-25/lab-websocket-25-01/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.10.RELEASE 4.0.0 lab-websocket-25-01 org.springframework.boot spring-boot-starter-websocket com.alibaba fastjson 1.2.62 ================================================ FILE: lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/Application.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/config/WebSocketConfiguration.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.server.standard.ServerEndpointExporter; @Configuration // @EnableWebSocket // 无需添加该注解,因为我们并不是使用 Spring WebSocket public class WebSocketConfiguration { @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } } ================================================ FILE: lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/handler/AuthMessageHandler.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.handler; import cn.iocoder.springboot.lab25.springwebsocket.message.AuthRequest; import cn.iocoder.springboot.lab25.springwebsocket.message.AuthResponse; import cn.iocoder.springboot.lab25.springwebsocket.message.UserJoinNoticeRequest; import cn.iocoder.springboot.lab25.springwebsocket.util.WebSocketUtil; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import javax.websocket.Session; @Component public class AuthMessageHandler implements MessageHandler { @Override public void execute(Session session, AuthRequest message) { // 如果未传递 accessToken if (StringUtils.isEmpty(message.getAccessToken())) { WebSocketUtil.send(session, AuthResponse.TYPE, new AuthResponse().setCode(1).setMessage("认证 accessToken 未传入")); return; } // 添加到 WebSocketUtil 中 WebSocketUtil.addSession(session, message.getAccessToken()); // 考虑到代码简化,我们先直接使用 accessToken 作为 User // 判断是否认证成功。这里,假装直接成功 WebSocketUtil.send(session, AuthResponse.TYPE, new AuthResponse().setCode(0)); // 通知所有人,某个人加入了。这个是可选逻辑,仅仅是为了演示 WebSocketUtil.broadcast(UserJoinNoticeRequest.TYPE, new UserJoinNoticeRequest().setNickname(message.getAccessToken())); // 考虑到代码简化,我们先直接使用 accessToken 作为 User } @Override public String getType() { return AuthRequest.TYPE; } } ================================================ FILE: lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/handler/MessageHandler.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.handler; import cn.iocoder.springboot.lab25.springwebsocket.message.Message; import javax.websocket.Session; /** * 消息处理器接口 */ public interface MessageHandler { /** * 执行处理消息 * * @param session 会话 * @param message 消息 */ void execute(Session session, T message); /** * @return 消息类型,即每个 Message 实现类上的 TYPE 静态字段 */ String getType(); } ================================================ FILE: lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/handler/SendToAllHandler.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.handler; import cn.iocoder.springboot.lab25.springwebsocket.message.SendResponse; import cn.iocoder.springboot.lab25.springwebsocket.message.SendToAllRequest; import cn.iocoder.springboot.lab25.springwebsocket.message.SendToUserRequest; import cn.iocoder.springboot.lab25.springwebsocket.util.WebSocketUtil; import org.springframework.stereotype.Component; import javax.websocket.Session; @Component public class SendToAllHandler implements MessageHandler { @Override public void execute(Session session, SendToAllRequest message) { // 这里,假装直接成功 SendResponse sendResponse = new SendResponse().setMsgId(message.getMsgId()).setCode(0); WebSocketUtil.send(session, SendResponse.TYPE, sendResponse); // 创建转发的消息 SendToUserRequest sendToUserRequest = new SendToUserRequest().setMsgId(message.getMsgId()) .setContent(message.getContent()); // 广播发送 WebSocketUtil.broadcast(SendToUserRequest.TYPE, sendToUserRequest); } @Override public String getType() { return SendToAllRequest.TYPE; } } ================================================ FILE: lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/handler/SendToOneHandler.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.handler; import cn.iocoder.springboot.lab25.springwebsocket.message.SendResponse; import cn.iocoder.springboot.lab25.springwebsocket.message.SendToOneRequest; import cn.iocoder.springboot.lab25.springwebsocket.message.SendToUserRequest; import cn.iocoder.springboot.lab25.springwebsocket.util.WebSocketUtil; import org.springframework.stereotype.Component; import javax.websocket.Session; @Component public class SendToOneHandler implements MessageHandler { @Override public void execute(Session session, SendToOneRequest message) { // 这里,假装直接成功 SendResponse sendResponse = new SendResponse().setMsgId(message.getMsgId()).setCode(0); WebSocketUtil.send(session, SendResponse.TYPE, sendResponse); // 创建转发的消息 SendToUserRequest sendToUserRequest = new SendToUserRequest().setMsgId(message.getMsgId()) .setContent(message.getContent()); // 广播发送 WebSocketUtil.send(message.getToUser(), SendToUserRequest.TYPE, sendToUserRequest); } @Override public String getType() { return SendToOneRequest.TYPE; } } ================================================ FILE: lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/AuthRequest.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.message; /** * 用户认证请求 */ public class AuthRequest implements Message { public static final String TYPE = "AUTH_REQUEST"; /** * 认证 Token */ private String accessToken; public String getAccessToken() { return accessToken; } public AuthRequest setAccessToken(String accessToken) { this.accessToken = accessToken; return this; } } ================================================ FILE: lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/AuthResponse.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.message; /** * 用户认证响应 */ public class AuthResponse implements Message { public static final String TYPE = "AUTH_RESPONSE"; /** * 响应状态码 */ private Integer code; /** * 响应提示 */ private String message; public Integer getCode() { return code; } public AuthResponse setCode(Integer code) { this.code = code; return this; } public String getMessage() { return message; } public AuthResponse setMessage(String message) { this.message = message; return this; } } ================================================ FILE: lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/Message.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.message; /** * 基础消息体 */ public interface Message { } ================================================ FILE: lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/SendResponse.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.message; /** * 发送消息响应结果的 Message */ public class SendResponse implements Message { public static final String TYPE = "SEND_RESPONSE"; /** * 消息编号 */ private String msgId; /** * 响应状态码 */ private Integer code; /** * 响应提示 */ private String message; public String getMsgId() { return msgId; } public SendResponse setMsgId(String msgId) { this.msgId = msgId; return this; } public Integer getCode() { return code; } public SendResponse setCode(Integer code) { this.code = code; return this; } public String getMessage() { return message; } public SendResponse setMessage(String message) { this.message = message; return this; } } ================================================ FILE: lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/SendToAllRequest.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.message; /** * 发送给所有人的群聊消息的 Message */ public class SendToAllRequest implements Message { public static final String TYPE = "SEND_TO_ALL_REQUEST"; /** * 消息编号 */ private String msgId; /** * 内容 */ private String content; public String getContent() { return content; } public SendToAllRequest setContent(String content) { this.content = content; return this; } public String getMsgId() { return msgId; } public SendToAllRequest setMsgId(String msgId) { this.msgId = msgId; return this; } } ================================================ FILE: lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/SendToOneRequest.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.message; /** * 发送给指定人的私聊消息的 Message */ public class SendToOneRequest implements Message { public static final String TYPE = "SEND_TO_ONE_REQUEST"; /** * 发送给的用户 */ private String toUser; /** * 消息编号 */ private String msgId; /** * 内容 */ private String content; public String getToUser() { return toUser; } public SendToOneRequest setToUser(String toUser) { this.toUser = toUser; return this; } public String getMsgId() { return msgId; } public SendToOneRequest setMsgId(String msgId) { this.msgId = msgId; return this; } public String getContent() { return content; } public SendToOneRequest setContent(String content) { this.content = content; return this; } } ================================================ FILE: lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/SendToUserRequest.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.message; /** * 发送消息给一个用户的 Message */ public class SendToUserRequest implements Message { public static final String TYPE = "SEND_TO_USER_REQUEST"; /** * 消息编号 */ private String msgId; /** * 内容 */ private String content; public String getMsgId() { return msgId; } public SendToUserRequest setMsgId(String msgId) { this.msgId = msgId; return this; } public String getContent() { return content; } public SendToUserRequest setContent(String content) { this.content = content; return this; } } ================================================ FILE: lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/UserJoinNoticeRequest.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.message; /** * 用户加入群聊的通知 Message */ public class UserJoinNoticeRequest implements Message { public static final String TYPE = "USER_JOIN_NOTICE_REQUEST"; /** * 昵称 */ private String nickname; public String getNickname() { return nickname; } public UserJoinNoticeRequest setNickname(String nickname) { this.nickname = nickname; return this; } } ================================================ FILE: lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/util/WebSocketUtil.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.util; import cn.iocoder.springboot.lab25.springwebsocket.message.Message; import com.alibaba.fastjson.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.websocket.RemoteEndpoint; import javax.websocket.Session; import java.io.IOException; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * WebSocket 工具类,提供客户端连接的管理等功能 */ public class WebSocketUtil { private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketUtil.class); // ========== 会话相关 ========== /** * Session 与用户的映射 */ private static final Map SESSION_USER_MAP = new ConcurrentHashMap<>(); /** * 用户与 Session 的映射 */ private static final Map USER_SESSION_MAP = new ConcurrentHashMap<>(); /** * 添加 Session 。在这个方法中,会添加用户和 Session 之间的映射 * * @param session Session * @param user 用户 */ public static void addSession(Session session, String user) { // 更新 USER_SESSION_MAP USER_SESSION_MAP.put(user, session); // 更新 SESSION_USER_MAP SESSION_USER_MAP.put(session, user); } /** * 移除 Session 。 * * @param session Session */ public static void removeSession(Session session) { // 从 SESSION_USER_MAP 中移除 String user = SESSION_USER_MAP.remove(session); // 从 USER_SESSION_MAP 中移除 if (user != null && user.length() > 0) { USER_SESSION_MAP.remove(user); } } // ========== 消息相关 ========== /** * 广播发送消息给所有在线用户 * * @param type 消息类型 * @param message 消息体 * @param 消息类型 */ public static void broadcast(String type, T message) { // 创建消息 String messageText = buildTextMessage(type, message); // 遍历 SESSION_USER_MAP ,进行逐个发送 for (Session session : SESSION_USER_MAP.keySet()) { sendTextMessage(session, messageText); } } /** * 发送消息给单个用户的 Session * * @param session Session * @param type 消息类型 * @param message 消息体 * @param 消息类型 */ public static void send(Session session, String type, T message) { // 创建消息 String messageText = buildTextMessage(type, message); // 遍历给单个 Session ,进行逐个发送 sendTextMessage(session, messageText); } /** * 发送消息给指定用户 * * @param user 指定用户 * @param type 消息类型 * @param message 消息体 * @param 消息类型 * @return 发送是否成功你那个 */ public static boolean send(String user, String type, T message) { // 获得用户对应的 Session Session session = USER_SESSION_MAP.get(user); if (session == null) { LOGGER.error("[send][user({}) 不存在对应的 session]", user); return false; } // 发送消息 send(session, type, message); return true; } /** * 构建完整的消息 * * @param type 消息类型 * @param message 消息体 * @param 消息类型 * @return 消息 */ private static String buildTextMessage(String type, T message) { JSONObject messageObject = new JSONObject(); messageObject.put("type", type); messageObject.put("body", message); return messageObject.toString(); } /** * 真正发送消息 * * @param session Session * @param messageText 消息 */ private static void sendTextMessage(Session session, String messageText) { if (session == null) { LOGGER.error("[sendTextMessage][session 为 null]"); return; } RemoteEndpoint.Basic basic = session.getBasicRemote(); if (basic == null) { LOGGER.error("[sendTextMessage][session 的 为 null]"); return; } try { basic.sendText(messageText); } catch (IOException e) { LOGGER.error("[sendTextMessage][session({}) 发送消息{}) 发生异常", session, messageText, e); } } } ================================================ FILE: lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/websocket/WebsocketServerEndpoint.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.websocket; import cn.iocoder.springboot.lab25.springwebsocket.handler.MessageHandler; import cn.iocoder.springboot.lab25.springwebsocket.message.AuthRequest; import cn.iocoder.springboot.lab25.springwebsocket.message.Message; import cn.iocoder.springboot.lab25.springwebsocket.util.WebSocketUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.aop.framework.AopProxyUtils; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Controller; import org.springframework.util.CollectionUtils; import javax.websocket.*; import javax.websocket.server.ServerEndpoint; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @Controller @ServerEndpoint("/") public class WebsocketServerEndpoint implements InitializingBean { private Logger logger = LoggerFactory.getLogger(getClass()); /** * 消息类型与 MessageHandler 的映射 * * 注意,这里设置成静态变量。虽然说 WebsocketServerEndpoint 是单例,但是 Spring Boot 还是会为每个 WebSocket 创建一个 WebsocketServerEndpoint Bean 。 */ private static final Map HANDLERS = new HashMap<>(); @Autowired private ApplicationContext applicationContext; @OnOpen public void onOpen(Session session, EndpointConfig config) { logger.info("[onOpen][session({}) 接入]", session); // 解析 accessToken List accessTokenValues = session.getRequestParameterMap().get("accessToken"); String accessToken = !CollectionUtils.isEmpty(accessTokenValues) ? accessTokenValues.get(0) : null; // 创建 AuthRequest 消息类型 AuthRequest authRequest = new AuthRequest().setAccessToken(accessToken); // 获得消息处理器 MessageHandler messageHandler = HANDLERS.get(AuthRequest.TYPE); if (messageHandler == null) { logger.error("[onOpen][认证消息类型,不存在消息处理器]"); return; } messageHandler.execute(session, authRequest); } @OnMessage public void onMessage(Session session, String message) { logger.info("[onOpen][session({}) 接收到一条消息({})]", session, message); // 生产环境下,请设置成 debug 级别 try { // 获得消息类型 JSONObject jsonMessage = JSON.parseObject(message); String messageType = jsonMessage.getString("type"); // 获得消息处理器 MessageHandler messageHandler = HANDLERS.get(messageType); if (messageHandler == null) { logger.error("[onMessage][消息类型({}) 不存在消息处理器]", messageType); return; } // 解析消息 Class messageClass = this.getMessageClass(messageHandler); // 处理消息 Message messageObj = JSON.parseObject(jsonMessage.getString("body"), messageClass); messageHandler.execute(session, messageObj); } catch (Throwable throwable) { logger.info("[onMessage][session({}) message({}) 发生异常]", session, throwable); } } @OnClose public void onClose(Session session, CloseReason closeReason) { logger.info("[onClose][session({}) 连接关闭。关闭原因是({})}]", session, closeReason); WebSocketUtil.removeSession(session); } @OnError public void onError(Session session, Throwable throwable) { logger.info("[onClose][session({}) 发生异常]", session, throwable); } @Override public void afterPropertiesSet() throws Exception { // 通过 ApplicationContext 获得所有 MessageHandler Bean applicationContext.getBeansOfType(MessageHandler.class).values() // 获得所有 MessageHandler Bean .forEach(messageHandler -> HANDLERS.put(messageHandler.getType(), messageHandler)); // 添加到 handlers 中 logger.info("[afterPropertiesSet][消息处理器数量:{}]", HANDLERS.size()); } private Class getMessageClass(MessageHandler handler) { // 获得 Bean 对应的 Class 类名。因为有可能被 AOP 代理过。 Class targetClass = AopProxyUtils.ultimateTargetClass(handler); // 获得接口的 Type 数组 Type[] interfaces = targetClass.getGenericInterfaces(); Class superclass = targetClass.getSuperclass(); while ((Objects.isNull(interfaces) || 0 == interfaces.length) && Objects.nonNull(superclass)) { // 此处,是以父类的接口为准 interfaces = superclass.getGenericInterfaces(); superclass = targetClass.getSuperclass(); } if (Objects.nonNull(interfaces)) { // 遍历 interfaces 数组 for (Type type : interfaces) { // 要求 type 是泛型参数 if (type instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) type; // 要求是 MessageHandler 接口 if (Objects.equals(parameterizedType.getRawType(), MessageHandler.class)) { Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); // 取首个元素 if (Objects.nonNull(actualTypeArguments) && actualTypeArguments.length > 0) { return (Class) actualTypeArguments[0]; } else { throw new IllegalStateException(String.format("类型(%s) 获得不到消息类型", handler)); } } } } } throw new IllegalStateException(String.format("类型(%s) 获得不到消息类型", handler)); } } ================================================ FILE: lab-25/lab-websocket-25-02/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.10.RELEASE 4.0.0 lab-websocket-25-02 org.springframework.boot spring-boot-starter-websocket com.alibaba fastjson 1.2.62 ================================================ FILE: lab-25/lab-websocket-25-02/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/Application.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-25/lab-websocket-25-02/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/config/WebSocketConfiguration.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.config; import cn.iocoder.springboot.lab25.springwebsocket.websocket.DemoWebSocketHandler; import cn.iocoder.springboot.lab25.springwebsocket.websocket.DemoWebSocketShakeInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.config.annotation.EnableWebSocket; import org.springframework.web.socket.config.annotation.WebSocketConfigurer; import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; @Configuration @EnableWebSocket // 开启 Spring WebSocket public class WebSocketConfiguration implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(this.webSocketHandler(), "/") // 配置处理器 .addInterceptors(new DemoWebSocketShakeInterceptor()) // 配置拦截器 .setAllowedOrigins("*"); // 解决跨域问题 } @Bean public DemoWebSocketHandler webSocketHandler() { return new DemoWebSocketHandler(); } @Bean public DemoWebSocketShakeInterceptor webSocketShakeInterceptor() { return new DemoWebSocketShakeInterceptor(); } } ================================================ FILE: lab-25/lab-websocket-25-02/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/handler/AuthMessageHandler.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.handler; import cn.iocoder.springboot.lab25.springwebsocket.message.AuthRequest; import cn.iocoder.springboot.lab25.springwebsocket.message.AuthResponse; import cn.iocoder.springboot.lab25.springwebsocket.message.UserJoinNoticeRequest; import cn.iocoder.springboot.lab25.springwebsocket.util.WebSocketUtil; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import org.springframework.web.socket.WebSocketSession; @Component public class AuthMessageHandler implements MessageHandler { @Override public void execute(WebSocketSession session, AuthRequest message) { // 如果未传递 accessToken if (StringUtils.isEmpty(message.getAccessToken())) { WebSocketUtil.send(session, AuthResponse.TYPE, new AuthResponse().setCode(1).setMessage("认证 accessToken 未传入")); return; } // 添加到 WebSocketUtil 中 WebSocketUtil.addSession(session, message.getAccessToken()); // 考虑到代码简化,我们先直接使用 accessToken 作为 User // 判断是否认证成功。这里,假装直接成功 WebSocketUtil.send(session, AuthResponse.TYPE, new AuthResponse().setCode(0)); // 通知所有人,某个人加入了。这个是可选逻辑,仅仅是为了演示 WebSocketUtil.broadcast(UserJoinNoticeRequest.TYPE, new UserJoinNoticeRequest().setNickname(message.getAccessToken())); // 考虑到代码简化,我们先直接使用 accessToken 作为 User } @Override public String getType() { return AuthRequest.TYPE; } } ================================================ FILE: lab-25/lab-websocket-25-02/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/handler/MessageHandler.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.handler; import cn.iocoder.springboot.lab25.springwebsocket.message.Message; import org.springframework.web.socket.WebSocketSession; /** * 消息处理器接口 */ public interface MessageHandler { /** * 执行处理消息 * * @param session 会话 * @param message 消息 */ void execute(WebSocketSession session, T message); /** * @return 消息类型,即每个 Message 实现类上的 TYPE 静态字段 */ String getType(); } ================================================ FILE: lab-25/lab-websocket-25-02/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/handler/SendToAllHandler.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.handler; import cn.iocoder.springboot.lab25.springwebsocket.message.SendResponse; import cn.iocoder.springboot.lab25.springwebsocket.message.SendToAllRequest; import cn.iocoder.springboot.lab25.springwebsocket.message.SendToUserRequest; import cn.iocoder.springboot.lab25.springwebsocket.util.WebSocketUtil; import org.springframework.stereotype.Component; import org.springframework.web.socket.WebSocketSession; @Component public class SendToAllHandler implements MessageHandler { @Override public void execute(WebSocketSession session, SendToAllRequest message) { // 这里,假装直接成功 SendResponse sendResponse = new SendResponse().setMsgId(message.getMsgId()).setCode(0); WebSocketUtil.send(session, SendResponse.TYPE, sendResponse); // 创建转发的消息 SendToUserRequest sendToUserRequest = new SendToUserRequest().setMsgId(message.getMsgId()) .setContent(message.getContent()); // 广播发送 WebSocketUtil.broadcast(SendToUserRequest.TYPE, sendToUserRequest); } @Override public String getType() { return SendToAllRequest.TYPE; } } ================================================ FILE: lab-25/lab-websocket-25-02/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/handler/SendToOneHandler.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.handler; import cn.iocoder.springboot.lab25.springwebsocket.message.SendResponse; import cn.iocoder.springboot.lab25.springwebsocket.message.SendToOneRequest; import cn.iocoder.springboot.lab25.springwebsocket.message.SendToUserRequest; import cn.iocoder.springboot.lab25.springwebsocket.util.WebSocketUtil; import org.springframework.stereotype.Component; import org.springframework.web.socket.WebSocketSession; @Component public class SendToOneHandler implements MessageHandler { @Override public void execute(WebSocketSession session, SendToOneRequest message) { // 这里,假装直接成功 SendResponse sendResponse = new SendResponse().setMsgId(message.getMsgId()).setCode(0); WebSocketUtil.send(session, SendResponse.TYPE, sendResponse); // 创建转发的消息 SendToUserRequest sendToUserRequest = new SendToUserRequest().setMsgId(message.getMsgId()) .setContent(message.getContent()); // 广播发送 WebSocketUtil.send(message.getToUser(), SendToUserRequest.TYPE, sendToUserRequest); } @Override public String getType() { return SendToOneRequest.TYPE; } } ================================================ FILE: lab-25/lab-websocket-25-02/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/AuthRequest.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.message; /** * 用户认证请求 */ public class AuthRequest implements Message { public static final String TYPE = "AUTH_REQUEST"; /** * 认证 Token */ private String accessToken; public String getAccessToken() { return accessToken; } public AuthRequest setAccessToken(String accessToken) { this.accessToken = accessToken; return this; } } ================================================ FILE: lab-25/lab-websocket-25-02/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/AuthResponse.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.message; /** * 用户认证响应 */ public class AuthResponse implements Message { public static final String TYPE = "AUTH_RESPONSE"; /** * 响应状态码 */ private Integer code; /** * 响应提示 */ private String message; public Integer getCode() { return code; } public AuthResponse setCode(Integer code) { this.code = code; return this; } public String getMessage() { return message; } public AuthResponse setMessage(String message) { this.message = message; return this; } } ================================================ FILE: lab-25/lab-websocket-25-02/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/Message.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.message; /** * 基础消息体 */ public interface Message { } ================================================ FILE: lab-25/lab-websocket-25-02/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/SendResponse.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.message; /** * 发送消息响应结果的 Message */ public class SendResponse implements Message { public static final String TYPE = "SEND_RESPONSE"; /** * 消息编号 */ private String msgId; /** * 响应状态码 */ private Integer code; /** * 响应提示 */ private String message; public String getMsgId() { return msgId; } public SendResponse setMsgId(String msgId) { this.msgId = msgId; return this; } public Integer getCode() { return code; } public SendResponse setCode(Integer code) { this.code = code; return this; } public String getMessage() { return message; } public SendResponse setMessage(String message) { this.message = message; return this; } } ================================================ FILE: lab-25/lab-websocket-25-02/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/SendToAllRequest.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.message; /** * 发送给所有人的群聊消息的 Message */ public class SendToAllRequest implements Message { public static final String TYPE = "SEND_TO_ALL_REQUEST"; /** * 消息编号 */ private String msgId; /** * 内容 */ private String content; public String getContent() { return content; } public SendToAllRequest setContent(String content) { this.content = content; return this; } public String getMsgId() { return msgId; } public SendToAllRequest setMsgId(String msgId) { this.msgId = msgId; return this; } } ================================================ FILE: lab-25/lab-websocket-25-02/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/SendToOneRequest.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.message; /** * 发送给指定人的私聊消息的 Message */ public class SendToOneRequest implements Message { public static final String TYPE = "SEND_TO_ONE_REQUEST"; /** * 发送给的用户 */ private String toUser; /** * 消息编号 */ private String msgId; /** * 内容 */ private String content; public String getToUser() { return toUser; } public SendToOneRequest setToUser(String toUser) { this.toUser = toUser; return this; } public String getMsgId() { return msgId; } public SendToOneRequest setMsgId(String msgId) { this.msgId = msgId; return this; } public String getContent() { return content; } public SendToOneRequest setContent(String content) { this.content = content; return this; } } ================================================ FILE: lab-25/lab-websocket-25-02/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/SendToUserRequest.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.message; /** * 发送消息给一个用户的 Message */ public class SendToUserRequest implements Message { public static final String TYPE = "SEND_TO_USER_REQUEST"; /** * 消息编号 */ private String msgId; /** * 内容 */ private String content; public String getMsgId() { return msgId; } public SendToUserRequest setMsgId(String msgId) { this.msgId = msgId; return this; } public String getContent() { return content; } public SendToUserRequest setContent(String content) { this.content = content; return this; } } ================================================ FILE: lab-25/lab-websocket-25-02/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/UserJoinNoticeRequest.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.message; /** * 用户加入群聊的通知 Message */ public class UserJoinNoticeRequest implements Message { public static final String TYPE = "USER_JOIN_NOTICE_REQUEST"; /** * 昵称 */ private String nickname; public String getNickname() { return nickname; } public UserJoinNoticeRequest setNickname(String nickname) { this.nickname = nickname; return this; } } ================================================ FILE: lab-25/lab-websocket-25-02/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/util/WebSocketUtil.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.util; import cn.iocoder.springboot.lab25.springwebsocket.message.Message; import com.alibaba.fastjson.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.socket.TextMessage; import org.springframework.web.socket.WebSocketSession; import java.io.IOException; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * WebSocket 工具类,提供客户端连接的管理等功能 */ public class WebSocketUtil { private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketUtil.class); // ========== 会话相关 ========== /** * Session 与用户的映射 */ private static final Map SESSION_USER_MAP = new ConcurrentHashMap<>(); /** * 用户与 Session 的映射 */ private static final Map USER_SESSION_MAP = new ConcurrentHashMap<>(); /** * 添加 Session 。在这个方法中,会添加用户和 Session 之间的映射 * * @param session Session * @param user 用户 */ public static void addSession(WebSocketSession session, String user) { // 更新 USER_SESSION_MAP USER_SESSION_MAP.put(user, session); // 更新 SESSION_USER_MAP SESSION_USER_MAP.put(session, user); } /** * 移除 Session 。 * * @param session Session */ public static void removeSession(WebSocketSession session) { // 从 SESSION_USER_MAP 中移除 String user = SESSION_USER_MAP.remove(session); // 从 USER_SESSION_MAP 中移除 if (user != null && user.length() > 0) { USER_SESSION_MAP.remove(user); } } // ========== 消息相关 ========== /** * 广播发送消息给所有在线用户 * * @param type 消息类型 * @param message 消息体 * @param 消息类型 */ public static void broadcast(String type, T message) { // 创建消息 TextMessage textMessage = buildTextMessage(type, message); // 遍历 SESSION_USER_MAP ,进行逐个发送 for (WebSocketSession session : SESSION_USER_MAP.keySet()) { sendTextMessage(session, textMessage); } } /** * 发送消息给单个用户的 Session * * @param session Session * @param type 消息类型 * @param message 消息体 * @param 消息类型 */ public static void send(WebSocketSession session, String type, T message) { // 创建消息 TextMessage textMessage = buildTextMessage(type, message); // 遍历给单个 Session ,进行逐个发送 sendTextMessage(session, textMessage); } /** * 发送消息给指定用户 * * @param user 指定用户 * @param type 消息类型 * @param message 消息体 * @param 消息类型 * @return 发送是否成功你那个 */ public static boolean send(String user, String type, T message) { // 获得用户对应的 Session WebSocketSession session = USER_SESSION_MAP.get(user); if (session == null) { LOGGER.error("[send][user({}) 不存在对应的 session]", user); return false; } // 发送消息 send(session, type, message); return true; } /** * 构建完整的消息 * * @param type 消息类型 * @param message 消息体 * @param 消息类型 * @return 消息 */ private static TextMessage buildTextMessage(String type, T message) { JSONObject messageObject = new JSONObject(); messageObject.put("type", type); messageObject.put("body", message); return new TextMessage(messageObject.toString()); } /** * 真正发送消息 * * @param session Session * @param textMessage 消息 */ private static void sendTextMessage(WebSocketSession session, TextMessage textMessage) { if (session == null) { LOGGER.error("[sendTextMessage][session 为 null]"); return; } try { session.sendMessage(textMessage); } catch (IOException e) { LOGGER.error("[sendTextMessage][session({}) 发送消息{}) 发生异常", session, textMessage, e); } } } ================================================ FILE: lab-25/lab-websocket-25-02/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/websocket/DemoWebSocketHandler.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.websocket; import cn.iocoder.springboot.lab25.springwebsocket.handler.MessageHandler; import cn.iocoder.springboot.lab25.springwebsocket.message.AuthRequest; import cn.iocoder.springboot.lab25.springwebsocket.message.Message; import cn.iocoder.springboot.lab25.springwebsocket.util.WebSocketUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.aop.framework.AopProxyUtils; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.web.socket.CloseStatus; import org.springframework.web.socket.TextMessage; import org.springframework.web.socket.WebSocketSession; import org.springframework.web.socket.handler.TextWebSocketHandler; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.HashMap; import java.util.Map; import java.util.Objects; public class DemoWebSocketHandler extends TextWebSocketHandler implements InitializingBean { private Logger logger = LoggerFactory.getLogger(getClass()); /** * 消息类型与 MessageHandler 的映射 * * 无需设置成静态变量 */ private final Map HANDLERS = new HashMap<>(); @Autowired private ApplicationContext applicationContext; @Override // 对应 open 事件 public void afterConnectionEstablished(WebSocketSession session) throws Exception { logger.info("[afterConnectionEstablished][session({}) 接入]", session); // 解析 accessToken String accessToken = (String) session.getAttributes().get("accessToken"); // 创建 AuthRequest 消息类型 AuthRequest authRequest = new AuthRequest().setAccessToken(accessToken); // 获得消息处理器 MessageHandler messageHandler = HANDLERS.get(AuthRequest.TYPE); if (messageHandler == null) { logger.error("[onOpen][认证消息类型,不存在消息处理器]"); return; } messageHandler.execute(session, authRequest); } @Override // 对应 message 事件 public void handleTextMessage(WebSocketSession session, TextMessage textMessage) throws Exception { logger.info("[handleMessage][session({}) 接收到一条消息({})]", session, textMessage); // 生产环境下,请设置成 debug 级别 try { // 获得消息类型 JSONObject jsonMessage = JSON.parseObject(textMessage.getPayload()); String messageType = jsonMessage.getString("type"); // 获得消息处理器 MessageHandler messageHandler = HANDLERS.get(messageType); if (messageHandler == null) { logger.error("[onMessage][消息类型({}) 不存在消息处理器]", messageType); return; } // 解析消息 Class messageClass = this.getMessageClass(messageHandler); // 处理消息 Message messageObj = JSON.parseObject(jsonMessage.getString("body"), messageClass); messageHandler.execute(session, messageObj); } catch (Throwable throwable) { logger.info("[onMessage][session({}) message({}) 发生异常]", session, throwable); } } @Override // 对应 close 事件 public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { logger.info("[afterConnectionClosed][session({}) 连接关闭。关闭原因是({})}]", session, status); WebSocketUtil.removeSession(session); } @Override // 对应 error 事件 public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { logger.info("[handleTransportError][session({}) 发生异常]", session, exception); } @Override public void afterPropertiesSet() throws Exception { // 通过 ApplicationContext 获得所有 MessageHandler Bean applicationContext.getBeansOfType(MessageHandler.class).values() // 获得所有 MessageHandler Bean .forEach(messageHandler -> HANDLERS.put(messageHandler.getType(), messageHandler)); // 添加到 handlers 中 logger.info("[afterPropertiesSet][消息处理器数量:{}]", HANDLERS.size()); } private Class getMessageClass(MessageHandler handler) { // 获得 Bean 对应的 Class 类名。因为有可能被 AOP 代理过。 Class targetClass = AopProxyUtils.ultimateTargetClass(handler); // 获得接口的 Type 数组 Type[] interfaces = targetClass.getGenericInterfaces(); Class superclass = targetClass.getSuperclass(); while ((Objects.isNull(interfaces) || 0 == interfaces.length) && Objects.nonNull(superclass)) { // 此处,是以父类的接口为准 interfaces = superclass.getGenericInterfaces(); superclass = targetClass.getSuperclass(); } if (Objects.nonNull(interfaces)) { // 遍历 interfaces 数组 for (Type type : interfaces) { // 要求 type 是泛型参数 if (type instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) type; // 要求是 MessageHandler 接口 if (Objects.equals(parameterizedType.getRawType(), MessageHandler.class)) { Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); // 取首个元素 if (Objects.nonNull(actualTypeArguments) && actualTypeArguments.length > 0) { return (Class) actualTypeArguments[0]; } else { throw new IllegalStateException(String.format("类型(%s) 获得不到消息类型", handler)); } } } } } throw new IllegalStateException(String.format("类型(%s) 获得不到消息类型", handler)); } } ================================================ FILE: lab-25/lab-websocket-25-02/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/websocket/DemoWebSocketShakeInterceptor.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.websocket; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.http.server.ServletServerHttpRequest; import org.springframework.web.socket.WebSocketHandler; import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor; import java.util.Map; /** * 自定义 HttpSessionHandshakeInterceptor 拦截器 * * 因为 WebSocketSession 无法获得 ws 地址上的请求参数,所以只好通过该拦截器,获得 accessToken 请求参数,设置到 attributes 中 */ public class DemoWebSocketShakeInterceptor extends HttpSessionHandshakeInterceptor { @Override // 拦截 Handshake 事件 public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map attributes) throws Exception { // 获得 accessToken if (request instanceof ServletServerHttpRequest) { ServletServerHttpRequest serverRequest = (ServletServerHttpRequest) request; attributes.put("accessToken", serverRequest.getServletRequest().getParameter("accessToken")); } // 调用父方法,继续执行逻辑 return super.beforeHandshake(request, response, wsHandler, attributes); } } ================================================ FILE: lab-25/lab-websocket-25-03/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.10.RELEASE 4.0.0 lab-websocket-25-03 org.springframework.boot spring-boot-starter-websocket com.alibaba fastjson 1.2.62 ================================================ FILE: lab-25/lab-websocket-25-03/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/Application.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-25/lab-websocket-25-03/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/config/WebSocketConfiguration.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.config; import org.springframework.context.annotation.Configuration; import org.springframework.messaging.simp.config.MessageBrokerRegistry; import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; import org.springframework.web.socket.config.annotation.StompEndpointRegistry; import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; @Configuration @EnableWebSocketMessageBroker // 开启 Spring WebSocket 对 Stomp 的支持 public class WebSocketConfiguration implements WebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry registry) { registry.enableSimpleBroker("/topic"); registry.setApplicationDestinationPrefixes("/app"); } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { // registry.addEndpoint("/send_to_all") // .setAllowedOrigins("*") // .withSockJS(); registry.addEndpoint("/") .setAllowedOrigins("*") .withSockJS(); // RequestUpgradeStrategy upgradeStrategy = new TomcatRequestUpgradeStrategy(); // registry.addEndpoint("/") // .setHandshakeHandler(new DefaultHandshakeHandler(upgradeStrategy)) // .setAllowedOrigins("*"); } // @Override // public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { // registry.addHandler(this.webSocketHandler(), "/") // 配置处理器 // .addInterceptors(new DemoWebSocketShakeInterceptor()) // 配置拦截器 // .setAllowedOrigins("*"); // 解决跨域问题 // } // @Bean // public DemoWebSocketHandler webSocketHandler() { // return new DemoWebSocketHandler(); // } // @Bean // public DemoWebSocketShakeInterceptor webSocketShakeInterceptor() { // return new DemoWebSocketShakeInterceptor(); // } } ================================================ FILE: lab-25/lab-websocket-25-03/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/controller/SendController.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.controller; import cn.iocoder.springboot.lab25.springwebsocket.message.SendToAllRequest; import cn.iocoder.springboot.lab25.springwebsocket.message.SendToUserRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.handler.annotation.SendTo; import org.springframework.stereotype.Controller; @Controller public class SendController { private Logger logger = LoggerFactory.getLogger(getClass()); @MessageMapping("/send_to_all") @SendTo(value = "/topic/send_to_all") // TODO https://blog.csdn.net/fly_leopard/article/details/78664409 暂时先跳过 public SendToUserRequest sendToAll(SendToAllRequest message) { logger.info("[sendToAll][SendToAllRequest({})]", message); // // 创建转发的消息 // SendToUserRequest sendToUserRequest = new SendToUserRequest().setMsgId(message.getMsgId()) // .setContent(message.getContent()); // // 广播发送 // WebSocketUtil.broadcast(SendToUserRequest.TYPE, sendToUserRequest); // 这里,假装直接成功 // return new SendResponse().setMsgId(message.getMsgId()).setCode(0); return new SendToUserRequest().setMsgId(message.getMsgId()) .setContent(message.getContent()); } // @SubscribeMapping("/topic/send_to_all") // public void subSendToAll(SendToUserRequest message) { // logger.info("[subSendToAll][SendToUserRequest({})]", message); // } } ================================================ FILE: lab-25/lab-websocket-25-03/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/AuthRequest.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.message; /** * 用户认证请求 */ public class AuthRequest implements Message { public static final String TYPE = "AUTH_REQUEST"; /** * 认证 Token */ private String accessToken; public String getAccessToken() { return accessToken; } public AuthRequest setAccessToken(String accessToken) { this.accessToken = accessToken; return this; } } ================================================ FILE: lab-25/lab-websocket-25-03/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/AuthResponse.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.message; /** * 用户认证响应 */ public class AuthResponse implements Message { public static final String TYPE = "AUTH_RESPONSE"; /** * 响应状态码 */ private Integer code; /** * 响应提示 */ private String message; public Integer getCode() { return code; } public AuthResponse setCode(Integer code) { this.code = code; return this; } public String getMessage() { return message; } public AuthResponse setMessage(String message) { this.message = message; return this; } } ================================================ FILE: lab-25/lab-websocket-25-03/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/Message.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.message; /** * 基础消息体 */ public interface Message { } ================================================ FILE: lab-25/lab-websocket-25-03/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/SendResponse.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.message; /** * 发送消息响应结果的 Message */ public class SendResponse implements Message { public static final String TYPE = "SEND_RESPONSE"; /** * 消息编号 */ private String msgId; /** * 响应状态码 */ private Integer code; /** * 响应提示 */ private String message; public String getMsgId() { return msgId; } public SendResponse setMsgId(String msgId) { this.msgId = msgId; return this; } public Integer getCode() { return code; } public SendResponse setCode(Integer code) { this.code = code; return this; } public String getMessage() { return message; } public SendResponse setMessage(String message) { this.message = message; return this; } } ================================================ FILE: lab-25/lab-websocket-25-03/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/SendToAllRequest.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.message; /** * 发送给所有人的群聊消息的 Message */ public class SendToAllRequest implements Message { public static final String TYPE = "SEND_TO_ALL_REQUEST"; /** * 消息编号 */ private String msgId; /** * 内容 */ private String content; public String getContent() { return content; } public SendToAllRequest setContent(String content) { this.content = content; return this; } public String getMsgId() { return msgId; } public SendToAllRequest setMsgId(String msgId) { this.msgId = msgId; return this; } } ================================================ FILE: lab-25/lab-websocket-25-03/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/SendToOneRequest.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.message; /** * 发送给指定人的私聊消息的 Message */ public class SendToOneRequest implements Message { public static final String TYPE = "SEND_TO_ONE_REQUEST"; /** * 发送给的用户 */ private String toUser; /** * 消息编号 */ private String msgId; /** * 内容 */ private String content; public String getToUser() { return toUser; } public SendToOneRequest setToUser(String toUser) { this.toUser = toUser; return this; } public String getMsgId() { return msgId; } public SendToOneRequest setMsgId(String msgId) { this.msgId = msgId; return this; } public String getContent() { return content; } public SendToOneRequest setContent(String content) { this.content = content; return this; } } ================================================ FILE: lab-25/lab-websocket-25-03/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/SendToUserRequest.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.message; /** * 发送消息给一个用户的 Message */ public class SendToUserRequest implements Message { public static final String TYPE = "SEND_TO_USER_REQUEST"; /** * 消息编号 */ private String msgId; /** * 内容 */ private String content; public String getMsgId() { return msgId; } public SendToUserRequest setMsgId(String msgId) { this.msgId = msgId; return this; } public String getContent() { return content; } public SendToUserRequest setContent(String content) { this.content = content; return this; } } ================================================ FILE: lab-25/lab-websocket-25-03/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/UserJoinNoticeRequest.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.message; /** * 用户加入群聊的通知 Message */ public class UserJoinNoticeRequest implements Message { public static final String TYPE = "USER_JOIN_NOTICE_REQUEST"; /** * 昵称 */ private String nickname; public String getNickname() { return nickname; } public UserJoinNoticeRequest setNickname(String nickname) { this.nickname = nickname; return this; } } ================================================ FILE: lab-25/lab-websocket-25-03/src/test/java/cn/iocoder/springboot/lab25/springwebsocket/Main.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket; import cn.iocoder.springboot.lab25.springwebsocket.message.SendToAllRequest; import org.springframework.messaging.converter.MappingJackson2MessageConverter; import org.springframework.messaging.simp.stomp.StompCommand; import org.springframework.messaging.simp.stomp.StompHeaders; import org.springframework.messaging.simp.stomp.StompSession; import org.springframework.messaging.simp.stomp.StompSessionHandler; import org.springframework.web.socket.client.WebSocketClient; import org.springframework.web.socket.client.standard.StandardWebSocketClient; import org.springframework.web.socket.messaging.WebSocketStompClient; import org.springframework.web.socket.sockjs.client.SockJsClient; import org.springframework.web.socket.sockjs.client.Transport; import org.springframework.web.socket.sockjs.client.WebSocketTransport; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; import java.util.Scanner; import java.util.UUID; public class Main { public static void main(String[] args) { List transports = new ArrayList<>(1); transports.add(new WebSocketTransport( new StandardWebSocketClient()) ); WebSocketClient transport = new SockJsClient(transports); // TODO 芋艿,参考 https://codeday.me/bug/20190115/521240.html 文章 // WebSocketClient client = new StandardWebSocketClient(); WebSocketStompClient stompClient = new WebSocketStompClient(transport); stompClient.setMessageConverter(new MappingJackson2MessageConverter()); StompSessionHandler sessionHandler = new StompSessionHandler() { @Override public void afterConnected(StompSession stompSession, StompHeaders stompHeaders) { // stompSession.subscribe("/topic/send_to_all", this); stompSession.send("/app/send_to_all", new SendToAllRequest().setMsgId(UUID.randomUUID().toString()) .setContent("测试消息")); } @Override public void handleException(StompSession stompSession, StompCommand stompCommand, StompHeaders stompHeaders, byte[] bytes, Throwable throwable) { System.out.println(); } @Override public void handleTransportError(StompSession stompSession, Throwable throwable) { System.out.println(); } @Override public Type getPayloadType(StompHeaders stompHeaders) { return null; } @Override public void handleFrame(StompHeaders stompHeaders, Object o) { System.out.println(); } }; stompClient.connect("ws://127.0.0.1:8080/", sessionHandler); new Scanner(System.in).nextLine(); // Don't close immediately. } } ================================================ FILE: lab-25/lab-websocket-25-03/src/test/java/cn/iocoder/springboot/lab25/springwebsocket/client/DemoWebSocketStompClient.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.client; import cn.iocoder.springboot.lab25.springwebsocket.client.handler.ConnectHandler; import cn.iocoder.springboot.lab25.springwebsocket.message.SendToAllRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.messaging.converter.MappingJackson2MessageConverter; import org.springframework.messaging.simp.stomp.StompSession; import org.springframework.util.concurrent.ListenableFuture; import org.springframework.web.socket.client.WebSocketClient; import org.springframework.web.socket.client.standard.StandardWebSocketClient; import org.springframework.web.socket.messaging.WebSocketStompClient; import org.springframework.web.socket.sockjs.client.SockJsClient; import org.springframework.web.socket.sockjs.client.Transport; import org.springframework.web.socket.sockjs.client.WebSocketTransport; import java.util.Collections; import java.util.List; import java.util.Scanner; import java.util.UUID; import java.util.concurrent.ExecutionException; public class DemoWebSocketStompClient { private static final Logger LOGGER = LoggerFactory.getLogger(DemoWebSocketStompClient.class); private WebSocketStompClient stompClient; public DemoWebSocketStompClient() { // 创建 SockJsClient 对象,内嵌 StandardWebSocketClient 对象 List transports = Collections.singletonList(new WebSocketTransport(new StandardWebSocketClient())); WebSocketClient transport = new SockJsClient(transports); // 创建 WebSocketStompClient 对象,内嵌 SockJsClient 对象 this.stompClient = new WebSocketStompClient(transport); // 设置消息转换器 this.stompClient.setMessageConverter(new MappingJackson2MessageConverter()); } public ListenableFuture connect(String url) { return this.stompClient.connect(url, new ConnectHandler()); } public static void main(String[] args) throws ExecutionException, InterruptedException { // 创建 DemoWebSocketStompClient 客户端 DemoWebSocketStompClient demoWebSocketStompClient = new DemoWebSocketStompClient(); // 发起连接 ListenableFuture future = demoWebSocketStompClient.connect("ws://127.0.0.1:8080/"); StompSession session = future.get(); // 发起消息 session.send("/app/send_to_all", new SendToAllRequest().setMsgId(UUID.randomUUID().toString()) .setContent("测试消息")); // 阻塞等待 new Scanner(System.in).nextLine(); // Don't close immediately. } } ================================================ FILE: lab-25/lab-websocket-25-03/src/test/java/cn/iocoder/springboot/lab25/springwebsocket/client/handler/ConnectHandler.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.client.handler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.messaging.simp.stomp.StompCommand; import org.springframework.messaging.simp.stomp.StompHeaders; import org.springframework.messaging.simp.stomp.StompSession; import org.springframework.messaging.simp.stomp.StompSessionHandlerAdapter; public class ConnectHandler extends StompSessionHandlerAdapter { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public void afterConnected(StompSession session, StompHeaders connectedHeaders) { logger.info("[afterConnected][session({}) 连接成功 connectedHeaders({})]", session, connectedHeaders); session.subscribe("/topic/send_to_all", new SendToUserRequestHandler()); } @Override public void handleException(StompSession session, StompCommand command, StompHeaders headers, byte[] payload, Throwable exception) { logger.error("[handleException][session({}) command({}) headers({}) payload({}) 发生异常]", session, command, headers, payload, exception); } @Override public void handleTransportError(StompSession session, Throwable exception) { logger.error("[handleTransportError][session({}) 发生传输错误]", session, exception); } } ================================================ FILE: lab-25/lab-websocket-25-03/src/test/java/cn/iocoder/springboot/lab25/springwebsocket/client/handler/SendToUserRequestHandler.java ================================================ package cn.iocoder.springboot.lab25.springwebsocket.client.handler; import cn.iocoder.springboot.lab25.springwebsocket.message.SendToUserRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.messaging.simp.stomp.StompHeaders; import org.springframework.messaging.simp.stomp.StompSessionHandlerAdapter; import java.lang.reflect.Type; public class SendToUserRequestHandler extends StompSessionHandlerAdapter { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public Type getPayloadType(StompHeaders headers) { return SendToUserRequest.class; } @Override public void handleFrame(StompHeaders headers, Object payload) { SendToUserRequest request = (SendToUserRequest) payload; logger.info("[handleFrame][接收到消息 headers({}) payload({})]", headers, request); } } ================================================ FILE: lab-25/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-25 pom lab-websocket-25-01 lab-websocket-25-02 lab-websocket-25-03 ================================================ FILE: lab-25/《芋道 Spring Boot WebSocket 入门》.md ================================================ ================================================ FILE: lab-26/lab-26-distributed-session-01/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.10.RELEASE 4.0.0 lab-26-distributed-session-01 org.springframework.boot spring-boot-starter-web org.springframework.session spring-session-data-redis org.springframework.boot spring-boot-starter-data-redis io.lettuce lettuce-core redis.clients jedis ================================================ FILE: lab-26/lab-26-distributed-session-01/src/main/java/cn/iocoder/springboot/lab26/distributedsession/Application.java ================================================ package cn.iocoder.springboot.lab26.distributedsession; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-26/lab-26-distributed-session-01/src/main/java/cn/iocoder/springboot/lab26/distributedsession/config/SessionConfiguration.java ================================================ package cn.iocoder.springboot.lab26.distributedsession.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.session.data.redis.RedisOperationsSessionRepository; import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession; import org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration; @Configuration @EnableRedisHttpSession // 自动化配置 Spring Session 使用 Redis 作为数据源 public class SessionConfiguration { /** * 创建 {@link RedisOperationsSessionRepository} 使用的 RedisSerializer Bean 。 * * 具体可以看看 {@link RedisHttpSessionConfiguration#setDefaultRedisSerializer(RedisSerializer)} 方法, * 它会引入名字为 "springSessionDefaultRedisSerializer" 的 Bean 。 * * @return RedisSerializer Bean */ @Bean(name = "springSessionDefaultRedisSerializer") public RedisSerializer springSessionDefaultRedisSerializer() { return RedisSerializer.json(); } // @Bean // public CookieHttpSessionIdResolver sessionIdResolver() { // // 创建 CookieHttpSessionIdResolver 对象 // CookieHttpSessionIdResolver sessionIdResolver = new CookieHttpSessionIdResolver(); // // // 创建 DefaultCookieSerializer 对象 // DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer(); // sessionIdResolver.setCookieSerializer(cookieSerializer); // 设置到 sessionIdResolver 中 // cookieSerializer.setCookieName("JSESSIONID"); // // return sessionIdResolver; // } // @Bean // public HeaderHttpSessionIdResolver sessionIdResolver() { //// return HeaderHttpSessionIdResolver.xAuthToken(); //// return HeaderHttpSessionIdResolver.authenticationInfo(); // return new HeaderHttpSessionIdResolver("token"); // } } ================================================ FILE: lab-26/lab-26-distributed-session-01/src/main/java/cn/iocoder/springboot/lab26/distributedsession/controller/SessionController.java ================================================ package cn.iocoder.springboot.lab26.distributedsession.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpSession; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; @RestController @RequestMapping("/session") public class SessionController { @GetMapping("/set") // 其实 PostMapping 更合适,单纯为了方便 public void set(HttpSession session, @RequestParam("key") String key, @RequestParam("value") String value) { session.setAttribute(key, value); } @GetMapping("/get_all") public Map getAll(HttpSession session) { Map result = new HashMap<>(); // 遍历 for (Enumeration enumeration = session.getAttributeNames(); enumeration.hasMoreElements();) { String key = enumeration.nextElement(); Object value = session.getAttribute(key); result.put(key, value); } // 返回 return result; } } ================================================ FILE: lab-26/lab-26-distributed-session-01/src/main/resources/application.yaml ================================================ spring: # 对应 RedisProperties 类 redis: host: 127.0.0.1 port: 6379 password: # Redis 服务器密码,默认为空。生产中,一定要设置 Redis 密码! database: 0 # Redis 数据库号,默认为 0 。 timeout: 0 # Redis 连接超时时间,单位:毫秒。 # 对应 RedisProperties.Jedis 内部类 jedis: pool: max-active: 8 # 连接池最大连接数,默认为 8 。使用负数表示没有限制。 max-idle: 8 # 默认连接数最大空闲的连接数,默认为 8 。使用负数表示没有限制。 min-idle: 0 # 默认连接池最小空闲的连接数,默认为 0 。允许设置 0 和 正数。 max-wait: -1 # 连接池最大阻塞等待时间,单位:毫秒。默认为 -1 ,表示不限制。 ================================================ FILE: lab-26/lab-26-distributed-session-02/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.10.RELEASE 4.0.0 lab-26-distributed-session-02 org.springframework.boot spring-boot-starter-web org.springframework.session spring-session-data-mongodb org.springframework.boot spring-boot-starter-data-mongodb ================================================ FILE: lab-26/lab-26-distributed-session-02/src/main/java/cn/iocoder/springboot/lab26/distributedsession/Application.java ================================================ package cn.iocoder.springboot.lab26.distributedsession; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-26/lab-26-distributed-session-02/src/main/java/cn/iocoder/springboot/lab26/distributedsession/config/SessionConfiguration.java ================================================ package cn.iocoder.springboot.lab26.distributedsession.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.session.data.mongo.AbstractMongoSessionConverter; import org.springframework.session.data.mongo.JacksonMongoSessionConverter; import org.springframework.session.data.mongo.config.annotation.web.http.EnableMongoHttpSession; @Configuration @EnableMongoHttpSession // 自动化配置 Spring Session 使用 MongoDB 作为数据源 public class SessionConfiguration { @Bean public AbstractMongoSessionConverter mongoSessionConverter() { return new JacksonMongoSessionConverter(); } } ================================================ FILE: lab-26/lab-26-distributed-session-02/src/main/java/cn/iocoder/springboot/lab26/distributedsession/controller/SessionController.java ================================================ package cn.iocoder.springboot.lab26.distributedsession.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpSession; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; @RestController @RequestMapping("/session") public class SessionController { @GetMapping("/set") // 其实 PostMapping 更合适,单纯为了方便 public void set(HttpSession session, @RequestParam("key") String key, @RequestParam("value") String value) { session.setAttribute(key, value); } @GetMapping("/get_all") public Map getAll(HttpSession session) { Map result = new HashMap<>(); // 遍历 for (Enumeration enumeration = session.getAttributeNames(); enumeration.hasMoreElements();) { String key = enumeration.nextElement(); Object value = session.getAttribute(key); result.put(key, value); } // 返回 return result; } } ================================================ FILE: lab-26/lab-26-distributed-session-02/src/main/resources/application.yaml ================================================ spring: data: # MongoDB 配置项,对应 MongoProperties 类 mongodb: host: 127.0.0.1 port: 27017 database: yourdatabase username: test01 password: password01 # 上述属性,也可以只配置 uri logging: level: org: springframework: data: mongodb: core: DEBUG # 打印 mongodb 操作的具体语句。生产环境下,不建议开启。 ================================================ FILE: lab-26/lab-26-distributed-session-springsecurity/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.10.RELEASE 4.0.0 lab-26-distributed-session-springsecurity org.springframework.boot spring-boot-starter-web org.springframework.session spring-session-data-redis org.springframework.boot spring-boot-starter-data-redis io.lettuce lettuce-core redis.clients jedis org.springframework.boot spring-boot-starter-security ================================================ FILE: lab-26/lab-26-distributed-session-springsecurity/src/main/java/cn/iocoder/springboot/lab26/distributedsession/Application.java ================================================ package cn.iocoder.springboot.lab26.distributedsession; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-26/lab-26-distributed-session-springsecurity/src/main/java/cn/iocoder/springboot/lab26/distributedsession/config/SessionConfiguration.java ================================================ package cn.iocoder.springboot.lab26.distributedsession.config; import org.springframework.context.annotation.Configuration; import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession; @Configuration @EnableRedisHttpSession // 自动化配置 Spring Session 使用 Redis 作为数据源 public class SessionConfiguration { } ================================================ FILE: lab-26/lab-26-distributed-session-springsecurity/src/main/java/cn/iocoder/springboot/lab26/distributedsession/controller/SessionController.java ================================================ package cn.iocoder.springboot.lab26.distributedsession.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.session.FindByIndexNameSessionRepository; import org.springframework.session.Session; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.Map; @RestController @RequestMapping("/session") public class SessionController { @Autowired private FindByIndexNameSessionRepository sessionRepository; @GetMapping("/list") public Map list(@RequestParam("username") String username) { return sessionRepository.findByPrincipalName(username); } // @GetMapping("/set") // 其实 PostMapping 更合适,单纯为了方便 // public void set(HttpSession session, // @RequestParam("key") String key, // @RequestParam("value") String value) { // session.setAttribute(key, value); // } // // @GetMapping("/get_all") // public Map getAll(HttpSession session) { // Map result = new HashMap<>(); // // 遍历 // for (Enumeration enumeration = session.getAttributeNames(); // enumeration.hasMoreElements();) { // String key = enumeration.nextElement(); // Object value = session.getAttribute(key); // result.put(key, value); // } // // 返回 // return result; // } } ================================================ FILE: lab-26/lab-26-distributed-session-springsecurity/src/main/resources/application.yaml ================================================ spring: # 对应 RedisProperties 类 redis: host: 127.0.0.1 port: 6379 password: # Redis 服务器密码,默认为空。生产中,一定要设置 Redis 密码! database: 0 # Redis 数据库号,默认为 0 。 timeout: 0 # Redis 连接超时时间,单位:毫秒。 # 对应 RedisProperties.Jedis 内部类 jedis: pool: max-active: 8 # 连接池最大连接数,默认为 8 。使用负数表示没有限制。 max-idle: 8 # 默认连接数最大空闲的连接数,默认为 8 。使用负数表示没有限制。 min-idle: 0 # 默认连接池最小空闲的连接数,默认为 0 。允许设置 0 和 正数。 max-wait: -1 # 连接池最大阻塞等待时间,单位:毫秒。默认为 -1 ,表示不限制。 # 对应 SecurityProperties 类 security: user: # 配置内存中,可登陆的用户名和密码 name: yudaoyuanma password: nicai ================================================ FILE: lab-26/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-26 pom lab-26-distributed-session-01 lab-26-distributed-session-02 lab-26-distributed-session-springsecurity ================================================ FILE: lab-26/《芋道 Spring Boot 分布式 Session 入门》.md ================================================ ================================================ FILE: lab-27/lab-27-webflux-01/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-27-webflux-01 org.springframework.boot spring-boot-starter-webflux 2.2.1.RELEASE org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-27/lab-27-webflux-01/src/main/java/cn/iocoder/springboot/lab27/springwebflux/Application.java ================================================ package cn.iocoder.springboot.lab27.springwebflux; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-27/lab-27-webflux-01/src/main/java/cn/iocoder/springboot/lab27/springwebflux/controller/UserController.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.controller; import cn.iocoder.springboot.lab27.springwebflux.dto.UserAddDTO; import cn.iocoder.springboot.lab27.springwebflux.dto.UserUpdateDTO; import cn.iocoder.springboot.lab27.springwebflux.service.UserService; import cn.iocoder.springboot.lab27.springwebflux.vo.UserVO; import org.reactivestreams.Publisher; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.util.ArrayList; import java.util.List; /** * 用户 Controller */ @RestController @RequestMapping("/users") public class UserController { @Autowired private UserService userService; /** * 查询用户列表 * * @return 用户列表 */ @GetMapping("/list") public Flux list() { // 查询列表 List result = new ArrayList<>(); result.add(new UserVO().setId(1).setUsername("yudaoyuanma")); result.add(new UserVO().setId(2).setUsername("woshiyutou")); result.add(new UserVO().setId(3).setUsername("chifanshuijiao")); // 返回列表 return Flux.fromIterable(result); } /** * 获得指定用户编号的用户 * * @param id 用户编号 * @return 用户 */ @GetMapping("/get") public Mono get(@RequestParam("id") Integer id) { // 查询用户 UserVO user = new UserVO().setId(id).setUsername("username:" + id); // 返回 return Mono.just(user); } /** * 获得指定用户编号的用户 * * @param id 用户编号 * @return 用户 */ @GetMapping("/v2/get") public Mono get2(@RequestParam("id") Integer id) { // 查询用户 UserVO user = userService.get(id); // 返回 return Mono.just(user); } /** * 添加用户 * * @param addDTO 添加用户信息 DTO * @return 添加成功的用户编号 */ @PostMapping("add") public Mono add(@RequestBody Publisher addDTO) { // 插入用户记录,返回编号 Integer returnId = 1; // 返回用户编号 return Mono.just(returnId); } /** * 添加用户 * * @param addDTO 添加用户信息 DTO * @return 添加成功的用户编号 */ @PostMapping("add2") public Mono add2(Mono addDTO) { // 插入用户记录,返回编号 Integer returnId = 1; // 返回用户编号 return Mono.just(returnId); } /** * 更新指定用户编号的用户 * * @param updateDTO 更新用户信息 DTO * @return 是否修改成功 */ @PostMapping("/update") public Mono update(@RequestBody Publisher updateDTO) { // 更新用户记录 Boolean success = true; // 返回更新是否成功 return Mono.just(success); } /** * 删除指定用户编号的用户 * * @param id 用户编号 * @return 是否删除成功 */ @PostMapping("/delete") // URL 修改成 /delete ,RequestMethod 改成 DELETE public Mono delete(@RequestParam("id") Integer id) { // 删除用户记录 Boolean success = true; // 返回是否更新成功 return Mono.just(success); } } ================================================ FILE: lab-27/lab-27-webflux-01/src/main/java/cn/iocoder/springboot/lab27/springwebflux/controller/UserRouter.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.controller; import cn.iocoder.springboot.lab27.springwebflux.vo.UserVO; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.util.StringUtils; import org.springframework.web.reactive.function.server.*; import reactor.core.publisher.Mono; import java.util.ArrayList; import java.util.List; import java.util.UUID; import static org.springframework.web.reactive.function.server.RequestPredicates.*; import static org.springframework.web.reactive.function.server.RouterFunctions.*; import static org.springframework.web.reactive.function.server.ServerResponse.*; /** * 用户 Router */ @Configuration public class UserRouter { @Bean public RouterFunction userListRouterFunction() { return RouterFunctions.route(RequestPredicates.GET("/users2/list"), new HandlerFunction() { @Override public Mono handle(ServerRequest request) { // 查询列表 List result = new ArrayList<>(); result.add(new UserVO().setId(1).setUsername("yudaoyuanma")); result.add(new UserVO().setId(2).setUsername("woshiyutou")); result.add(new UserVO().setId(3).setUsername("chifanshuijiao")); // 返回列表 return ServerResponse.ok().bodyValue(result); } }); } @Bean public RouterFunction userGetRouterFunction() { return RouterFunctions.route(RequestPredicates.GET("/users2/get"), new HandlerFunction() { @Override public Mono handle(ServerRequest request) { // 获得编号 Integer id = request.queryParam("id") .map(s -> StringUtils.isEmpty(s) ? null : Integer.valueOf(s)).get(); // 查询用户 UserVO user = new UserVO().setId(id).setUsername(UUID.randomUUID().toString()); // 返回列表 return ServerResponse.ok().bodyValue(user); } }); } @Bean public RouterFunction demoRouterFunction() { return route(GET("/users2/demo"), request -> ok().bodyValue("demo")); } } ================================================ FILE: lab-27/lab-27-webflux-01/src/main/java/cn/iocoder/springboot/lab27/springwebflux/dto/UserAddDTO.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.dto; /** * 用户添加 DTO */ public class UserAddDTO { /** * 账号 */ private String username; /** * 密码 */ private String password; public String getUsername() { return username; } public UserAddDTO setUsername(String username) { this.username = username; return this; } public String getPassword() { return password; } public UserAddDTO setPassword(String password) { this.password = password; return this; } } ================================================ FILE: lab-27/lab-27-webflux-01/src/main/java/cn/iocoder/springboot/lab27/springwebflux/dto/UserUpdateDTO.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.dto; public class UserUpdateDTO { /** * 编号 */ private Integer id; /** * 账号 */ private String username; /** * 密码 */ private String password; public Integer getId() { return id; } public UserUpdateDTO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserUpdateDTO setUsername(String username) { this.username = username; return this; } public String getPassword() { return password; } public UserUpdateDTO setPassword(String password) { this.password = password; return this; } } ================================================ FILE: lab-27/lab-27-webflux-01/src/main/java/cn/iocoder/springboot/lab27/springwebflux/service/UserService.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.service; import cn.iocoder.springboot.lab27.springwebflux.vo.UserVO; import org.springframework.stereotype.Service; @Service public class UserService { public UserVO get(Integer id) { return new UserVO().setId(id).setUsername("test"); } } ================================================ FILE: lab-27/lab-27-webflux-01/src/main/java/cn/iocoder/springboot/lab27/springwebflux/vo/UserVO.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.vo; /** * 用户 VO */ public class UserVO { /** * 编号 */ private Integer id; /** * 账号 */ private String username; public Integer getId() { return id; } public UserVO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserVO setUsername(String username) { this.username = username; return this; } } ================================================ FILE: lab-27/lab-27-webflux-01/src/test/java/cn/iocoder/springboot/lab27/springwebflux/controller/UserControllerTest.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.controller; import cn.iocoder.springboot.lab27.springwebflux.Application; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.reactive.server.EntityExchangeResult; import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.web.reactive.function.BodyInserters; import java.util.HashMap; import java.util.Map; import java.util.function.Consumer; /** * UserController 集成测试 */ @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) @AutoConfigureWebTestClient public class UserControllerTest { @Autowired private WebTestClient webClient; @Test public void testList() { webClient.get().uri("/users/list") .exchange() // 执行请求 .expectStatus().isOk() // 响应状态码 200 .expectBody().json("[\n" + " {\n" + " \"id\": 1,\n" + " \"username\": \"yudaoyuanma\"\n" + " },\n" + " {\n" + " \"id\": 2,\n" + " \"username\": \"woshiyutou\"\n" + " },\n" + " {\n" + " \"id\": 3,\n" + " \"username\": \"chifanshuijiao\"\n" + " }\n" + "]"); // 响应结果 } @Test public void testGet() { // 获得指定用户编号的用户 webClient.get().uri("/users/get?id=1") .exchange() // 执行请求 .expectStatus().isOk() // 响应状态码 200 .expectBody().json("{\n" + " \"id\": 1,\n" + " \"username\": \"username:1\"\n" + "}"); // 响应结果 } @Test public void testGet2() { // 获得指定用户编号的用户 webClient.get().uri("/users/v2/get?id=1") .exchange() // 执行请求 .expectStatus().isOk() // 响应状态码 200 .expectBody().json("{\n" + " \"id\": 1,\n" + " \"username\": \"test\"\n" + "}"); // 响应结果 } @Test public void testAdd() { Map params = new HashMap<>(); params.put("username", "yudaoyuanma"); params.put("password", "nicai"); // 添加用户 webClient.post().uri("/users/add") .bodyValue(params) .exchange() // 执行请求 .expectStatus().isOk() // 响应状态码 200 .expectBody().json("1"); // 响应结果。因为没有提供 content 的比较,所以只好使用 json 来比较。竟然能通过 } @Test public void testAdd2() { // 发送文件的测试,可以参考 https://dev.to/shavz/sending-multipart-form-data-using-spring-webtestclient-2gb7 文章 BodyInserters.FormInserter formData = // Form Data 数据,需要这么拼凑 BodyInserters.fromFormData("username", "yudaoyuanma") .with("password", "nicai"); // 添加用户 webClient.post().uri("/users/add2") .body(formData) .exchange() // 执行请求 .expectStatus().isOk() // 响应状态码 200 .expectBody().json("1"); // 响应结果。因为没有提供 content 的比较,所以只好使用 json 来比较。竟然能通过 } @Test public void testUpdate() { Map params = new HashMap<>(); params.put("id", 1); params.put("username", "yudaoyuanma"); // 修改用户 webClient.post().uri("/users/update") .bodyValue(params) .exchange() // 执行请求 .expectStatus().isOk() // 响应状态码 200 .expectBody(Boolean.class) // 期望返回值类型是 Boolean .consumeWith((Consumer>) result -> // 通过消费结果,判断符合是 true 。 Assert.assertTrue("返回结果需要为 true", result.getResponseBody())); } @Test public void testDelete() { // 删除用户 webClient.post().uri("/users/delete?id=1") .exchange() // 执行请求 .expectStatus().isOk() // 响应状态码 200 .expectBody(Boolean.class) // 期望返回值类型是 Boolean .isEqualTo(true); // 这样更加简洁一些 // .consumeWith((Consumer>) result -> // 通过消费结果,判断符合是 true 。 // Assert.assertTrue("返回结果需要为 true", result.getResponseBody())); } } ================================================ FILE: lab-27/lab-27-webflux-01/src/test/java/cn/iocoder/springboot/lab27/springwebflux/controller/UserControllerTest2.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.controller; import cn.iocoder.springboot.lab27.springwebflux.service.UserService; import cn.iocoder.springboot.lab27.springwebflux.vo.UserVO; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.reactive.server.WebTestClient; /** * UserController 单元测试 * * 参考 https://howtodoinjava.com/spring-webflux/webfluxtest-with-webtestclient/ 文章 */ @RunWith(SpringRunner.class) @WebFluxTest(UserController.class) public class UserControllerTest2 { @Autowired private WebTestClient webClient; @MockBean private UserService userService; @Test public void testGet2() throws Exception { // Mock UserService 的 get 方法 System.out.println("before mock:" + userService.get(1)); Mockito.when(userService.get(1)).thenReturn( new UserVO().setId(1).setUsername("username:1")); System.out.println("after mock:" + userService.get(1)); // 查询用户列表 webClient.get().uri("/users/v2/get?id=1") .exchange() // 执行请求 .expectStatus().isOk() // 响应状态码 200 .expectBody().json("{\n" + " \"id\": 1,\n" + " \"username\": \"username:1\"\n" + "}"); // 响应结果 } } ================================================ FILE: lab-27/lab-27-webflux-01/src/test/java/cn/iocoder/springboot/lab27/springwebflux/package-info.java ================================================ package cn.iocoder.springboot.lab27.springwebflux; ================================================ FILE: lab-27/lab-27-webflux-02/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-27-webflux-02 org.springframework.boot spring-boot-starter-webflux org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/Application.java ================================================ package cn.iocoder.springboot.lab27.springwebflux; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/config/WebFluxConfiguration.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.config; import cn.iocoder.springboot.lab27.springwebflux.core.web.GlobalResponseBodyHandler; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; import org.springframework.http.codec.ServerCodecConfigurer; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.reactive.CorsWebFilter; import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource; import org.springframework.web.reactive.accept.RequestedContentTypeResolver; import org.springframework.web.reactive.config.WebFluxConfigurer; import java.util.Collections; @Configuration public class WebFluxConfiguration implements WebFluxConfigurer { @Bean public GlobalResponseBodyHandler responseWrapper(ServerCodecConfigurer serverCodecConfigurer, RequestedContentTypeResolver requestedContentTypeResolver) { return new GlobalResponseBodyHandler(serverCodecConfigurer.getWriters(), requestedContentTypeResolver); } // @Override // public void addCorsMappings(CorsRegistry registry) { // // 添加全局的 CORS 配置 // registry.addMapping("/**") // 匹配所有 URL ,相当于全局配置 // .allowedOrigins("*") // 允许所有请求来源 // .allowCredentials(true) // 允许发送 Cookie // .allowedMethods("*") // 允许所有请求 Method // .allowedHeaders("*") // 允许所有请求 Header //// .exposedHeaders("*") // 允许所有响应 Header // .maxAge(1800L); // 有效期 1800 秒,2 小时 // } @Bean @Order(0) // 设置 order 排序。这个顺序很重要哦,为避免麻烦请设置在最前 public CorsWebFilter corsFilter() { // 创建 UrlBasedCorsConfigurationSource 配置源,类似 CorsRegistry 注册表 UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); // 创建 CorsConfiguration 配置,相当于 CorsRegistration 注册信息 CorsConfiguration config = new CorsConfiguration(); config.setAllowedOrigins(Collections.singletonList("*")); // 允许所有请求来源 config.setAllowCredentials(true); // 允许发送 Cookie config.addAllowedMethod("*"); // 允许所有请求 Method config.setAllowedHeaders(Collections.singletonList("*")); // 允许所有请求 Header // config.setExposedHeaders(Collections.singletonList("*")); // 允许所有响应 Header config.setMaxAge(1800L); // 有效期 1800 秒,2 小时 source.registerCorsConfiguration("/**", config); // 创建 CorsWebFilter 对象 return new CorsWebFilter(source); // 创建 CorsFilter 过滤器 } // @Override // public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) { // configurer.registerDefaults(false); // configurer.customCodecs().decoder(new Jaxb2XmlDecoder()); // <- here // configurer.customCodecs().encoder(new Jaxb2XmlEncoder()); // <- here // } } ================================================ FILE: lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/constants/ServiceExceptionEnum.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.constants; /** * 业务异常枚举 */ public enum ServiceExceptionEnum { // ========== 系统级别 ========== SUCCESS(0, "成功"), SYS_ERROR(2001001000, "服务端发生异常"), MISSING_REQUEST_PARAM_ERROR(2001001001, "参数缺失"), // ========== 用户模块 ========== USER_NOT_FOUND(1001002000, "用户不存在"), // ========== 订单模块 ========== // ========== 商品模块 ========== ; /** * 错误码 */ private final int code; /** * 错误提示 */ private final String message; ServiceExceptionEnum(int code, String message) { this.code = code; this.message = message; } public int getCode() { return code; } public String getMessage() { return message; } } ================================================ FILE: lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/controller/UserController.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.controller; import cn.iocoder.springboot.lab27.springwebflux.constants.ServiceExceptionEnum; import cn.iocoder.springboot.lab27.springwebflux.core.exception.ServiceException; import cn.iocoder.springboot.lab27.springwebflux.core.vo.CommonResult; import cn.iocoder.springboot.lab27.springwebflux.vo.UserVO; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.util.ArrayList; import java.util.List; /** * 用户 Controller */ @RestController @RequestMapping("/users") //@CrossOrigin(value = "*") public class UserController { /** * 查询用户列表 * * @return 用户列表 */ @GetMapping("/list") public Flux list() { // 查询列表 List result = new ArrayList<>(); result.add(new UserVO().setId(1).setUsername("yudaoyuanma")); result.add(new UserVO().setId(2).setUsername("woshiyutou")); result.add(new UserVO().setId(3).setUsername("chifanshuijiao")); // 返回列表 return Flux.fromIterable(result); } /** * 获得指定用户编号的用户 * * @param id 用户编号 * @return 用户 */ @GetMapping("/get") // @PostMapping("/get") public Mono get(@RequestParam("id") Integer id) { // 查询用户 UserVO user = new UserVO().setId(id).setUsername("username:" + id); // 返回 return Mono.just(user); } /** * 获得指定用户编号的用户 * * @param id 用户编号 * @return 用户 */ @GetMapping("/get2") public Mono> get2(@RequestParam("id") Integer id) { // 查询用户 UserVO user = new UserVO().setId(id).setUsername("username:" + id); // 返回 return Mono.just(CommonResult.success(user)); } /** * 获得指定用户编号的用户 * * @param id 用户编号 * @return 用户 */ @GetMapping("/get3") public UserVO get3(@RequestParam("id") Integer id) { // 查询用户 UserVO user = new UserVO().setId(id).setUsername("username:" + id); // 返回 return user; } /** * 获得指定用户编号的用户 * * @param id 用户编号 * @return 用户 */ @GetMapping("/get4") public CommonResult get4(@RequestParam("id") Integer id) { // 查询用户 UserVO user = new UserVO().setId(id).setUsername("username:" + id); // 返回 return CommonResult.success(user); } /** * 测试抛出 NullPointerException 异常 */ @GetMapping("/exception-01") public UserVO exception01() { throw new NullPointerException("没有粗面鱼丸"); } /** * 测试抛出 ServiceException 异常 */ @GetMapping("/exception-02") public UserVO exception02() { throw new ServiceException(ServiceExceptionEnum.USER_NOT_FOUND); } // @PostMapping(value = "/add", // // ↓ 增加 "application/xml"、"application/json" ,针对 Content-Type 请求头 // consumes = {MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE}, // // ↓ 增加 "application/xml"、"application/json" ,针对 Accept 请求头 // produces = {MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE} // ) @PostMapping(value = "/add", // ↓ 增加 "application/xml"、"application/json" ,针对 Content-Type 请求头 consumes = {MediaType.APPLICATION_XML_VALUE}, // ↓ 增加 "application/xml"、"application/json" ,针对 Accept 请求头 produces = {MediaType.APPLICATION_XML_VALUE} ) // @PostMapping(value = "/add") public Mono add(@RequestBody Mono user) { return user; } } ================================================ FILE: lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/controller/UserRouter.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.controller; import cn.iocoder.springboot.lab27.springwebflux.vo.UserVO; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.util.StringUtils; import org.springframework.web.reactive.function.server.*; import reactor.core.publisher.Mono; import java.util.ArrayList; import java.util.List; import java.util.UUID; import java.util.function.Consumer; import static org.springframework.web.reactive.function.server.RequestPredicates.GET; import static org.springframework.web.reactive.function.server.RouterFunctions.route; import static org.springframework.web.reactive.function.server.ServerResponse.ok; /** * 用户 Router */ @Configuration public class UserRouter { private Logger logger = LoggerFactory.getLogger(getClass()); @Bean public RouterFunction userListRouterFunction() { return RouterFunctions.route(RequestPredicates.GET("/users2/list"), new HandlerFunction() { @Override public Mono handle(ServerRequest request) { // 查询列表 List result = new ArrayList<>(); result.add(new UserVO().setId(1).setUsername("yudaoyuanma")); result.add(new UserVO().setId(2).setUsername("woshiyutou")); result.add(new UserVO().setId(3).setUsername("chifanshuijiao")); // 返回列表 return ServerResponse.ok().bodyValue(result); } }); } @Bean public RouterFunction userGetRouterFunction() { return RouterFunctions.route(RequestPredicates.GET("/users2/get"), new HandlerFunction() { @Override public Mono handle(ServerRequest request) { // 获得编号 Integer id = request.queryParam("id") .map(s -> StringUtils.isEmpty(s) ? null : Integer.valueOf(s)).get(); // 查询用户 UserVO user = new UserVO().setId(id).setUsername(UUID.randomUUID().toString()); // 返回列表 return ServerResponse.ok().bodyValue(user); } }); } @Bean public RouterFunction demoRouterFunction() { return route(GET("/users2/demo"), request -> ok().bodyValue("demo")); } @Bean public RouterFunction demo2RouterFunction() { return route(GET("/users2/demo2"), request -> ok().bodyValue("demo")) .filter(new HandlerFilterFunction() { @Override public Mono filter(ServerRequest request, HandlerFunction next) { return next.handle(request).doOnSuccess(new Consumer() { // 执行成功后回调 @Override public void accept(ServerResponse serverResponse) { logger.info("[accept][执行成功]"); } }); } }); } } ================================================ FILE: lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/controller2/TestController.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.controller2; import cn.iocoder.springboot.lab27.springwebflux.vo.UserVO; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Mono; import java.util.UUID; /** * 测试 Controller */ @RestController @RequestMapping("/test") //@CrossOrigin(origins = "*", allowCredentials = "true") // 允许所有来源,允许发送 Cookie public class TestController { /** * 获得指定用户编号的用户 * * @return 用户 */ @GetMapping("/get") // @CrossOrigin(allowCredentials = "false") // 允许所有来源,不允许发送 Cookie public Mono get() { // 查询用户 UserVO user = new UserVO().setId(1).setUsername(UUID.randomUUID().toString()); // 返回 return Mono.just(user); } } ================================================ FILE: lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/core/exception/ServiceException.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.core.exception; import cn.iocoder.springboot.lab27.springwebflux.constants.ServiceExceptionEnum; /** * 服务异常 * * 参考 https://www.kancloud.cn/onebase/ob/484204 文章 * * 一共 10 位,分成四段 * * 第一段,1 位,类型 * 1 - 业务级别异常 * 2 - 系统级别异常 * 第二段,3 位,系统类型 * 001 - 用户系统 * 002 - 商品系统 * 003 - 订单系统 * 004 - 支付系统 * 005 - 优惠劵系统 * ... - ... * 第三段,3 位,模块 * 不限制规则。 * 一般建议,每个系统里面,可能有多个模块,可以再去做分段。以用户系统为例子: * 001 - OAuth2 模块 * 002 - User 模块 * 003 - MobileCode 模块 * 第四段,3 位,错误码 * 不限制规则。 * 一般建议,每个模块自增。 */ public final class ServiceException extends RuntimeException { /** * 错误码 */ private final Integer code; public ServiceException(ServiceExceptionEnum serviceExceptionEnum) { // 使用父类的 message 字段 super(serviceExceptionEnum.getMessage()); // 设置错误码 this.code = serviceExceptionEnum.getCode(); } public Integer getCode() { return code; } } ================================================ FILE: lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/core/filter/DemoWebFilter.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.core.filter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.WebFilter; import org.springframework.web.server.WebFilterChain; import reactor.core.publisher.Mono; import java.util.function.Consumer; @Component @Order(1) public class DemoWebFilter implements WebFilter { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public Mono filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) { // 继续执行请求 return webFilterChain.filter(serverWebExchange) .doOnSuccess(new Consumer() { // 执行成功后回调 @Override public void accept(Void aVoid) { logger.info("[accept][执行成功]"); } }); } } ================================================ FILE: lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/core/package-info.java ================================================ /** * 提供核心封装 */ package cn.iocoder.springboot.lab27.springwebflux.core; ================================================ FILE: lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/core/vo/CommonResult.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.core.vo; import com.fasterxml.jackson.annotation.JsonIgnore; import org.springframework.util.Assert; import javax.xml.bind.annotation.XmlRootElement; import java.io.Serializable; /** * 通用返回结果 * * @param 结果泛型 */ @XmlRootElement public class CommonResult implements Serializable { public static Integer CODE_SUCCESS = 0; /** * 错误码 */ private Integer code; /** * 错误提示 */ private String message; /** * 返回数据 */ private T data; /** * 将传入的 result 对象,转换成另外一个泛型结果的对象 * * 因为 A 方法返回的 CommonResult 对象,不满足调用其的 B 方法的返回,所以需要进行转换。 * * @param result 传入的 result 对象 * @param 返回的泛型 * @return 新的 CommonResult 对象 */ public static CommonResult error(CommonResult result) { return error(result.getCode(), result.getMessage()); } public static CommonResult error(Integer code, String message) { Assert.isTrue(!CODE_SUCCESS.equals(code), "code 必须是错误的!"); CommonResult result = new CommonResult<>(); result.code = code; result.message = message; return result; } public static CommonResult success(T data) { CommonResult result = new CommonResult<>(); result.code = CODE_SUCCESS; result.data = data; result.message = ""; return result; } public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public T getData() { return data; } public void setData(T data) { this.data = data; } @JsonIgnore public boolean isSuccess() { return CODE_SUCCESS.equals(code); } @JsonIgnore public boolean isError() { return !isSuccess(); } @Override public String toString() { return "CommonResult{" + "code=" + code + ", message='" + message + '\'' + ", data=" + data + '}'; } } ================================================ FILE: lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/core/web/GlobalExceptionHandler.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.core.web; import cn.iocoder.springboot.lab27.springwebflux.constants.ServiceExceptionEnum; import cn.iocoder.springboot.lab27.springwebflux.core.exception.ServiceException; import cn.iocoder.springboot.lab27.springwebflux.core.vo.CommonResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.server.ServerWebInputException; @ControllerAdvice(basePackages = "cn.iocoder.springboot.lab27.springwebflux.controller") public class GlobalExceptionHandler { private Logger logger = LoggerFactory.getLogger(getClass()); /** * 处理 ServiceException 异常 */ @ResponseBody @ExceptionHandler(value = ServiceException.class) public CommonResult serviceExceptionHandler(ServiceException ex) { logger.debug("[serviceExceptionHandler]", ex); // 包装 CommonResult 结果 return CommonResult.error(ex.getCode(), ex.getMessage()); } /** * 处理 ServerWebInputException 异常 * * WebFlux 参数不正确 */ @ResponseBody @ExceptionHandler(value = ServerWebInputException.class) public CommonResult serverWebInputExceptionHandler(ServerWebInputException ex) { logger.debug("[ServerWebInputExceptionHandler]", ex); // 包装 CommonResult 结果 return CommonResult.error(ServiceExceptionEnum.MISSING_REQUEST_PARAM_ERROR.getCode(), ServiceExceptionEnum.MISSING_REQUEST_PARAM_ERROR.getMessage()); } /** * 处理其它 Exception 异常 */ @ResponseBody @ExceptionHandler(value = Exception.class) public CommonResult exceptionHandler(Exception e) { // 记录异常日志 logger.error("[exceptionHandler]", e); // 返回 ERROR CommonResult return CommonResult.error(ServiceExceptionEnum.SYS_ERROR.getCode(), ServiceExceptionEnum.SYS_ERROR.getMessage()); } } ================================================ FILE: lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/core/web/GlobalResponseBodyHandler.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.core.web; import cn.iocoder.springboot.lab27.springwebflux.core.vo.CommonResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.MethodParameter; import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.http.codec.HttpMessageWriter; import org.springframework.web.reactive.HandlerResult; import org.springframework.web.reactive.accept.RequestedContentTypeResolver; import org.springframework.web.reactive.result.method.annotation.ResponseBodyResultHandler; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.util.List; import java.util.function.Function; public class GlobalResponseBodyHandler extends ResponseBodyResultHandler { private static Logger LOGGER = LoggerFactory.getLogger(GlobalResponseBodyHandler.class); private static MethodParameter METHOD_PARAMETER_MONO_COMMON_RESULT; private static final CommonResult COMMON_RESULT_SUCCESS = CommonResult.success(null); static { try { // 获得 METHOD_PARAMETER_MONO_COMMON_RESULT 。其中 -1 表示 `#methodForParams()` 方法的返回值 METHOD_PARAMETER_MONO_COMMON_RESULT = new MethodParameter( GlobalResponseBodyHandler.class.getDeclaredMethod("methodForParams"), -1); } catch (NoSuchMethodException e) { LOGGER.error("[static][获取 METHOD_PARAMETER_MONO_COMMON_RESULT 时,找不都方法"); throw new RuntimeException(e); } } public GlobalResponseBodyHandler(List> writers, RequestedContentTypeResolver resolver) { super(writers, resolver); } public GlobalResponseBodyHandler(List> writers, RequestedContentTypeResolver resolver, ReactiveAdapterRegistry registry) { super(writers, resolver, registry); } @Override @SuppressWarnings("unchecked") public Mono handleResult(ServerWebExchange exchange, HandlerResult result) { Object returnValue = result.getReturnValue(); Object body; // 处理返回结果为 Mono 的情况 if (returnValue instanceof Mono) { body = ((Mono) result.getReturnValue()) .map((Function) GlobalResponseBodyHandler::wrapCommonResult) .defaultIfEmpty(COMMON_RESULT_SUCCESS); // 处理返回结果为 Flux 的情况 } else if (returnValue instanceof Flux) { body = ((Flux) result.getReturnValue()) .collectList() .map((Function) GlobalResponseBodyHandler::wrapCommonResult) .defaultIfEmpty(COMMON_RESULT_SUCCESS); // 处理结果为其它类型 } else { body = wrapCommonResult(returnValue); } return writeBody(body, METHOD_PARAMETER_MONO_COMMON_RESULT, exchange); } private static Mono methodForParams() { return null; } private static CommonResult wrapCommonResult(Object body) { // 如果已经是 CommonResult 类型,则直接返回 if (body instanceof CommonResult) { return (CommonResult) body; } // 如果不是,则包装成 CommonResult 类型 return CommonResult.success(body); } } ================================================ FILE: lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/vo/UserVO.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.vo; import javax.xml.bind.annotation.XmlRootElement; /** * 用户 VO */ @XmlRootElement public class UserVO { /** * 编号 */ private Integer id; /** * 账号 */ private String username; public Integer getId() { return id; } public UserVO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserVO setUsername(String username) { this.username = username; return this; } } ================================================ FILE: lab-27/lab-27-webflux-03/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-27-webflux-03 测试在 WebFlux 中,使用 Java Servlet 相关的 Servlet、Filter、Listener org.springframework.boot spring-boot-starter-webflux org.springframework.boot spring-boot-starter-netty org.springframework.boot spring-boot-starter-tomcat org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-27/lab-27-webflux-03/src/main/java/cn/iocoder/springboot/lab27/springwebflux/Application.java ================================================ package cn.iocoder.springboot.lab27.springwebflux; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.ServletComponentScan; import org.springframework.boot.web.servlet.ServletListenerRegistrationBean; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import javax.servlet.*; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Collections; @SpringBootApplication @ServletComponentScan public class Application { private Logger logger = LoggerFactory.getLogger(getClass()); @Bean public ServletRegistrationBean testServlet01() { ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean<>(new HttpServlet() { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { logger.info("[doGet][uri: {}]", req.getRequestURI()); } }); servletRegistrationBean.setUrlMappings(Collections.singleton("/test/01")); return servletRegistrationBean; } @Bean public FilterRegistrationBean testFilter01() { FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean<>(new Filter() { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { logger.info("[doFilter]"); filterChain.doFilter(servletRequest, servletResponse); } }); filterRegistrationBean.setUrlPatterns(Collections.singleton("/test/*")); return filterRegistrationBean; } @Bean public ServletListenerRegistrationBean testListener01() { return new ServletListenerRegistrationBean<>(new ServletContextListener() { @Override public void contextInitialized(ServletContextEvent sce) { logger.info("[contextInitialized]"); } @Override public void contextDestroyed(ServletContextEvent sce) { } }); } public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-27/lab-27-webflux-03/src/main/java/cn/iocoder/springboot/lab27/springwebflux/controller/UserController.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.controller; import cn.iocoder.springboot.lab27.springwebflux.vo.UserVO; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Flux; import java.util.ArrayList; import java.util.List; /** * 用户 Controller */ @RestController @RequestMapping("/users") public class UserController { /** * 查询用户列表 * * @return 用户列表 */ @GetMapping("/list") public Flux list() { // 查询列表 List result = new ArrayList<>(); result.add(new UserVO().setId(1).setUsername("yudaoyuanma")); result.add(new UserVO().setId(2).setUsername("woshiyutou")); result.add(new UserVO().setId(3).setUsername("chifanshuijiao")); // 返回列表 return Flux.fromIterable(result); } } ================================================ FILE: lab-27/lab-27-webflux-03/src/main/java/cn/iocoder/springboot/lab27/springwebflux/core/package-info.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.core; ================================================ FILE: lab-27/lab-27-webflux-03/src/main/java/cn/iocoder/springboot/lab27/springwebflux/core/servlet/TestFilter02.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.core.servlet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import java.io.IOException; @WebFilter("/users/*") public class TestFilter02 implements Filter { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { logger.info("[doFilter]"); filterChain.doFilter(servletRequest, servletResponse); } } ================================================ FILE: lab-27/lab-27-webflux-03/src/main/java/cn/iocoder/springboot/lab27/springwebflux/core/servlet/TestServlet02.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.core.servlet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet(urlPatterns = "/test/02") public class TestServlet02 extends HttpServlet { private Logger logger = LoggerFactory.getLogger(getClass()); @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { logger.info("[doGet][uri: {}]", req.getRequestURI()); } } ================================================ FILE: lab-27/lab-27-webflux-03/src/main/java/cn/iocoder/springboot/lab27/springwebflux/core/servlet/TestServletContextListener02.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.core.servlet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.annotation.WebListener; @WebListener public class TestServletContextListener02 implements ServletContextListener { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public void contextInitialized(ServletContextEvent sce) { logger.info("[contextInitialized]"); } @Override public void contextDestroyed(ServletContextEvent sce) { } } ================================================ FILE: lab-27/lab-27-webflux-03/src/main/java/cn/iocoder/springboot/lab27/springwebflux/vo/UserVO.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.vo; /** * 用户 VO */ public class UserVO { /** * 编号 */ private Integer id; /** * 账号 */ private String username; public Integer getId() { return id; } public UserVO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserVO setUsername(String username) { this.username = username; return this; } } ================================================ FILE: lab-27/lab-27-webflux-elasticsearch/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-27-webflux-elasticsearch org.springframework.boot spring-boot-starter-webflux 2.2.1.RELEASE org.springframework.boot spring-boot-starter-data-elasticsearch org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-27/lab-27-webflux-elasticsearch/src/main/java/cn/iocoder/springboot/lab27/springwebflux/Application.java ================================================ package cn.iocoder.springboot.lab27.springwebflux; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { // System.setProperty("es.set.netty.runtime.available.processors", "false"); SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-27/lab-27-webflux-elasticsearch/src/main/java/cn/iocoder/springboot/lab27/springwebflux/config/ElasticsearchConfiguration.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.config; import org.springframework.context.annotation.Configuration; import org.springframework.data.elasticsearch.repository.config.EnableReactiveElasticsearchRepositories; @Configuration @EnableReactiveElasticsearchRepositories // 开启响应式的 Elasticsearch 的 Repository 的自动化配置 public class ElasticsearchConfiguration { } ================================================ FILE: lab-27/lab-27-webflux-elasticsearch/src/main/java/cn/iocoder/springboot/lab27/springwebflux/controller/UserController.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.controller; import cn.iocoder.springboot.lab27.springwebflux.dao.UserRepository; import cn.iocoder.springboot.lab27.springwebflux.dataobject.UserDO; import cn.iocoder.springboot.lab27.springwebflux.dto.UserAddDTO; import cn.iocoder.springboot.lab27.springwebflux.dto.UserUpdateDTO; import cn.iocoder.springboot.lab27.springwebflux.vo.UserVO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.util.Date; import java.util.Objects; import java.util.function.Function; /** * 用户 Controller */ @RestController @RequestMapping("/users") public class UserController { private static final UserDO USER_NULL = new UserDO(); @Autowired private UserRepository userRepository; /** * 查询用户列表 * * @return 用户列表 */ @GetMapping("/list") public Flux list() { // 返回列表 return userRepository.findAll() .map(userDO -> new UserVO().setId(userDO.getId()).setUsername(userDO.getUsername())); } /** * 获得指定用户编号的用户 * * @param id 用户编号 * @return 用户 */ @GetMapping("/get") public Mono get(@RequestParam("id") Integer id) { // 返回 return userRepository.findById(id) .map(userDO -> new UserVO().setId(userDO.getId()).setUsername(userDO.getUsername())); } /** * 添加用户 * * @param addDTO 添加用户信息 DTO * @return 添加成功的用户编号 */ @PostMapping("add") public Mono add(UserAddDTO addDTO) { // 查询用户 Mono user = userRepository.findByUsername(addDTO.getUsername()); // 执行插入 return user.defaultIfEmpty(USER_NULL) // 设置 USER_NULL 作为 null 的情况,否则 flatMap 不会往下走 .flatMap(new Function>() { @Override public Mono apply(UserDO userDO) { if (userDO != USER_NULL) { // 返回 -1 表示插入失败。 // 实际上,一般是抛出 ServiceException 异常。因为这个示例项目里暂时没做全局异常的定义,所以暂时返回 -1 啦 return Mono.just(-1); } // 将 addDTO 转成 UserDO userDO = new UserDO().setId((int) (System.currentTimeMillis() / 1000)) // 使用当前时间戳的描述,作为 ID 。 .setUsername(addDTO.getUsername()) .setPassword(addDTO.getPassword()) .setCreateTime(new Date()); // 插入数据库 return userRepository.save(userDO).map(UserDO::getId); } }); } /** * 更新指定用户编号的用户 * * @param updateDTO 更新用户信息 DTO * @return 是否修改成功 */ @PostMapping("/update") public Mono update(UserUpdateDTO updateDTO) { // 查询用户 Mono user = userRepository.findById(updateDTO.getId()); // 执行更新 return user.defaultIfEmpty(USER_NULL) // 设置 USER_NULL 作为 null 的情况,否则 flatMap 不会往下走 .flatMap(new Function>() { @Override public Mono apply(UserDO userDO) { // 如果不存在该用户,则直接返回 false 失败 if (userDO == USER_NULL) { return Mono.just(false); } // 查询用户是否存在 return userRepository.findByUsername(updateDTO.getUsername()) .defaultIfEmpty(USER_NULL) // 设置 USER_NULL 作为 null 的情况,否则 flatMap 不会往下走 .flatMap(new Function>() { @Override public Mono apply(UserDO usernameUserDO) { // 如果用户名已经使用(该用户名对应的 id 不是自己,说明就已经被使用了) if (usernameUserDO != USER_NULL && !Objects.equals(updateDTO.getId(), usernameUserDO.getId())) { return Mono.just(false); } // 执行更新 userDO.setUsername(updateDTO.getUsername()); userDO.setPassword(updateDTO.getPassword()); return userRepository.save(userDO).map(userDO -> true); // 返回 true 成功 } }); } }); } /** * 删除指定用户编号的用户 * * @param id 用户编号 * @return 是否删除成功 */ @PostMapping("/delete") // URL 修改成 /delete ,RequestMethod 改成 DELETE public Mono delete(@RequestParam("id") Integer id) { // 查询用户 Mono user = userRepository.findById(id); // 执行删除。这里仅仅是示例,项目中不要物理删除,而是标记删除 return user.defaultIfEmpty(USER_NULL) // 设置 USER_NULL 作为 null 的情况,否则 flatMap 不会往下走 .flatMap(new Function>() { @Override public Mono apply(UserDO userDO) { // 如果不存在该用户,则直接返回 false 失败 if (userDO == USER_NULL) { return Mono.just(false); } // 执行删除 return userRepository.deleteById(id).map(aVoid -> true); // 返回 true 成功 } }); } } ================================================ FILE: lab-27/lab-27-webflux-elasticsearch/src/main/java/cn/iocoder/springboot/lab27/springwebflux/dao/UserRepository.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.dao; import cn.iocoder.springboot.lab27.springwebflux.dataobject.UserDO; import org.springframework.data.elasticsearch.repository.ReactiveElasticsearchRepository; import reactor.core.publisher.Mono; public interface UserRepository extends ReactiveElasticsearchRepository { Mono findByUsername(String username); } //public interface UserRepository extends ElasticsearchRepository { // //// Mono findByUsername(String username); // //} ================================================ FILE: lab-27/lab-27-webflux-elasticsearch/src/main/java/cn/iocoder/springboot/lab27/springwebflux/dataobject/UserDO.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.dataobject; import org.springframework.data.annotation.Id; import org.springframework.data.elasticsearch.annotations.Document; import java.util.Date; @Document(indexName = "user", // 索引名 type = "user", // 类型。未来的版本即将废弃 shards = 1, // 默认索引分区数 replicas = 0, // 每个分区的备份数 refreshInterval = "-1" // 刷新间隔 ) public class UserDO { /** * ID 主键 */ @Id private Integer id; /** * 账号 */ private String username; /** * 密码 */ private String password; /** * 创建时间 */ private Date createTime; public Integer getId() { return id; } public UserDO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserDO setUsername(String username) { this.username = username; return this; } public String getPassword() { return password; } public UserDO setPassword(String password) { this.password = password; return this; } public Date getCreateTime() { return createTime; } public UserDO setCreateTime(Date createTime) { this.createTime = createTime; return this; } @Override public String toString() { return "UserDO{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", createTime=" + createTime + '}'; } } ================================================ FILE: lab-27/lab-27-webflux-elasticsearch/src/main/java/cn/iocoder/springboot/lab27/springwebflux/dto/UserAddDTO.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.dto; /** * 用户添加 DTO */ public class UserAddDTO { /** * 账号 */ private String username; /** * 密码 */ private String password; public String getUsername() { return username; } public UserAddDTO setUsername(String username) { this.username = username; return this; } public String getPassword() { return password; } public UserAddDTO setPassword(String password) { this.password = password; return this; } } ================================================ FILE: lab-27/lab-27-webflux-elasticsearch/src/main/java/cn/iocoder/springboot/lab27/springwebflux/dto/UserUpdateDTO.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.dto; public class UserUpdateDTO { /** * 编号 */ private Integer id; /** * 账号 */ private String username; /** * 密码 */ private String password; public Integer getId() { return id; } public UserUpdateDTO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserUpdateDTO setUsername(String username) { this.username = username; return this; } public String getPassword() { return password; } public UserUpdateDTO setPassword(String password) { this.password = password; return this; } } ================================================ FILE: lab-27/lab-27-webflux-elasticsearch/src/main/java/cn/iocoder/springboot/lab27/springwebflux/vo/UserVO.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.vo; /** * 用户 VO */ public class UserVO { /** * 编号 */ private Integer id; /** * 账号 */ private String username; public Integer getId() { return id; } public UserVO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserVO setUsername(String username) { this.username = username; return this; } } ================================================ FILE: lab-27/lab-27-webflux-elasticsearch/src/main/resources/application.yaml ================================================ spring: data: # Elasticsearch 配置项 elasticsearch: client: # 对应 ReactiveRestClientProperties 配置类 reactive: endpoints: 127.0.0.1:9200 # ES Restful API 地址 ================================================ FILE: lab-27/lab-27-webflux-mongodb/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-27-webflux-mongodb org.springframework.boot spring-boot-starter-webflux 2.2.1.RELEASE org.springframework.boot spring-boot-starter-data-mongodb-reactive org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-27/lab-27-webflux-mongodb/src/main/java/cn/iocoder/springboot/lab27/springwebflux/Application.java ================================================ package cn.iocoder.springboot.lab27.springwebflux; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-27/lab-27-webflux-mongodb/src/main/java/cn/iocoder/springboot/lab27/springwebflux/controller/UserController.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.controller; import cn.iocoder.springboot.lab27.springwebflux.dao.UserRepository; import cn.iocoder.springboot.lab27.springwebflux.dataobject.UserDO; import cn.iocoder.springboot.lab27.springwebflux.dto.UserAddDTO; import cn.iocoder.springboot.lab27.springwebflux.dto.UserUpdateDTO; import cn.iocoder.springboot.lab27.springwebflux.vo.UserVO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.util.Date; import java.util.Objects; import java.util.function.Function; /** * 用户 Controller */ @RestController @RequestMapping("/users") public class UserController { private static final UserDO USER_NULL = new UserDO(); @Autowired private UserRepository userRepository; /** * 查询用户列表 * * @return 用户列表 */ @GetMapping("/list") public Flux list() { // 返回列表 return userRepository.findAll() .map(userDO -> new UserVO().setId(userDO.getId()).setUsername(userDO.getUsername())); } /** * 获得指定用户编号的用户 * * @param id 用户编号 * @return 用户 */ @GetMapping("/get") public Mono get(@RequestParam("id") Integer id) { // 返回 return userRepository.findById(id) .map(userDO -> new UserVO().setId(userDO.getId()).setUsername(userDO.getUsername())); } /** * 添加用户 * * @param addDTO 添加用户信息 DTO * @return 添加成功的用户编号 */ @PostMapping("add") public Mono add(UserAddDTO addDTO) { // 查询用户 Mono user = userRepository.findByUsername(addDTO.getUsername()); // 执行插入 return user.defaultIfEmpty(USER_NULL) // 设置 USER_NULL 作为 null 的情况,否则 flatMap 不会往下走 .flatMap(new Function>() { @Override public Mono apply(UserDO userDO) { if (userDO != USER_NULL) { // 返回 -1 表示插入失败。 // 实际上,一般是抛出 ServiceException 异常。因为这个示例项目里暂时没做全局异常的定义,所以暂时返回 -1 啦 return Mono.just(-1); } // 将 addDTO 转成 UserDO userDO = new UserDO().setId((int) (System.currentTimeMillis() / 1000)) // 使用当前时间戳的描述,作为 ID 。 .setUsername(addDTO.getUsername()) .setPassword(addDTO.getPassword()) .setCreateTime(new Date()); // 插入数据库 return userRepository.insert(userDO).map(UserDO::getId); } }); } /** * 更新指定用户编号的用户 * * @param updateDTO 更新用户信息 DTO * @return 是否修改成功 */ @PostMapping("/update") public Mono update(UserUpdateDTO updateDTO) { // 查询用户 Mono user = userRepository.findById(updateDTO.getId()); // 执行更新 return user.defaultIfEmpty(USER_NULL) // 设置 USER_NULL 作为 null 的情况,否则 flatMap 不会往下走 .flatMap(new Function>() { @Override public Mono apply(UserDO userDO) { // 如果不存在该用户,则直接返回 false 失败 if (userDO == USER_NULL) { return Mono.just(false); } // 查询用户是否存在 return userRepository.findByUsername(updateDTO.getUsername()) .defaultIfEmpty(USER_NULL) // 设置 USER_NULL 作为 null 的情况,否则 flatMap 不会往下走 .flatMap(new Function>() { @Override public Mono apply(UserDO usernameUserDO) { // 如果用户名已经使用(该用户名对应的 id 不是自己,说明就已经被使用了) if (usernameUserDO != USER_NULL && !Objects.equals(updateDTO.getId(), usernameUserDO.getId())) { return Mono.just(false); } // 执行更新 userDO.setUsername(updateDTO.getUsername()); userDO.setPassword(updateDTO.getPassword()); return userRepository.save(userDO).map(userDO -> true); // 返回 true 成功 } }); } }); } /** * 删除指定用户编号的用户 * * @param id 用户编号 * @return 是否删除成功 */ @PostMapping("/delete") // URL 修改成 /delete ,RequestMethod 改成 DELETE public Mono delete(@RequestParam("id") Integer id) { // 查询用户 Mono user = userRepository.findById(id); // 执行删除。这里仅仅是示例,项目中不要物理删除,而是标记删除 return user.defaultIfEmpty(USER_NULL) // 设置 USER_NULL 作为 null 的情况,否则 flatMap 不会往下走 .flatMap(new Function>() { @Override public Mono apply(UserDO userDO) { // 如果不存在该用户,则直接返回 false 失败 if (userDO == USER_NULL) { return Mono.just(false); } // 执行删除 return userRepository.deleteById(id).map(aVoid -> true); // 返回 true 成功 } }); } } ================================================ FILE: lab-27/lab-27-webflux-mongodb/src/main/java/cn/iocoder/springboot/lab27/springwebflux/dao/UserRepository.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.dao; import cn.iocoder.springboot.lab27.springwebflux.dataobject.UserDO; import org.springframework.data.mongodb.repository.ReactiveMongoRepository; import reactor.core.publisher.Mono; public interface UserRepository extends ReactiveMongoRepository { Mono findByUsername(String username); } ================================================ FILE: lab-27/lab-27-webflux-mongodb/src/main/java/cn/iocoder/springboot/lab27/springwebflux/dataobject/UserDO.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.dataobject; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.mapping.Document; import java.util.Date; /** * 用户 DO */ @Document(collection = "User") public class UserDO { @Id private Integer id; /** * 账号 */ private String username; /** * 密码 */ private String password; /** * 创建时间 */ private Date createTime; public Integer getId() { return id; } public UserDO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserDO setUsername(String username) { this.username = username; return this; } public String getPassword() { return password; } public UserDO setPassword(String password) { this.password = password; return this; } public Date getCreateTime() { return createTime; } public UserDO setCreateTime(Date createTime) { this.createTime = createTime; return this; } @Override public String toString() { return "UserDO{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", createTime=" + createTime + '}'; } } ================================================ FILE: lab-27/lab-27-webflux-mongodb/src/main/java/cn/iocoder/springboot/lab27/springwebflux/dto/UserAddDTO.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.dto; /** * 用户添加 DTO */ public class UserAddDTO { /** * 账号 */ private String username; /** * 密码 */ private String password; public String getUsername() { return username; } public UserAddDTO setUsername(String username) { this.username = username; return this; } public String getPassword() { return password; } public UserAddDTO setPassword(String password) { this.password = password; return this; } } ================================================ FILE: lab-27/lab-27-webflux-mongodb/src/main/java/cn/iocoder/springboot/lab27/springwebflux/dto/UserUpdateDTO.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.dto; public class UserUpdateDTO { /** * 编号 */ private Integer id; /** * 账号 */ private String username; /** * 密码 */ private String password; public Integer getId() { return id; } public UserUpdateDTO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserUpdateDTO setUsername(String username) { this.username = username; return this; } public String getPassword() { return password; } public UserUpdateDTO setPassword(String password) { this.password = password; return this; } } ================================================ FILE: lab-27/lab-27-webflux-mongodb/src/main/java/cn/iocoder/springboot/lab27/springwebflux/vo/UserVO.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.vo; /** * 用户 VO */ public class UserVO { /** * 编号 */ private Integer id; /** * 账号 */ private String username; public Integer getId() { return id; } public UserVO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserVO setUsername(String username) { this.username = username; return this; } } ================================================ FILE: lab-27/lab-27-webflux-mongodb/src/main/resources/application.yaml ================================================ spring: data: # MongoDB 配置项,对应 MongoProperties 类 mongodb: host: 127.0.0.1 port: 27017 database: yourdatabase username: test01 password: password01 # 上述属性,也可以只配置 uri logging: level: org: springframework: data: mongodb: core: DEBUG # 打印 mongodb 操作的具体语句。生产环境下,不建议开启。 ================================================ FILE: lab-27/lab-27-webflux-r2dbc/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-27-webflux-r2dbc org.springframework.boot spring-boot-starter-webflux 2.2.1.RELEASE org.springframework.boot.experimental spring-boot-starter-data-r2dbc 0.1.0.M2 com.github.jasync-sql jasync-r2dbc-mysql 1.0.11 org.springframework.boot spring-boot-starter-test test spring-libs-snapshot https://repo.spring.io/libs-snapshot jcenter https://jcenter.bintray.com/ ================================================ FILE: lab-27/lab-27-webflux-r2dbc/src/main/java/cn/iocoder/springboot/lab27/springwebflux/Application.java ================================================ package cn.iocoder.springboot.lab27.springwebflux; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-27/lab-27-webflux-r2dbc/src/main/java/cn/iocoder/springboot/lab27/springwebflux/config/DatabaseConfiguration.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.config; import com.github.jasync.r2dbc.mysql.JasyncConnectionFactory; import com.github.jasync.sql.db.mysql.pool.MySQLConnectionFactory; import io.r2dbc.spi.ConnectionFactory; import org.springframework.boot.autoconfigure.r2dbc.R2dbcProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.r2dbc.connectionfactory.R2dbcTransactionManager; import org.springframework.transaction.ReactiveTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; import java.net.URI; import java.net.URISyntaxException; @Configuration @EnableTransactionManagement // 开启事务的支持 public class DatabaseConfiguration { @Bean public ConnectionFactory connectionFactory(R2dbcProperties properties) throws URISyntaxException { // 从 R2dbcProperties 中,解析出 host、port、database URI uri = new URI(properties.getUrl()); String host = uri.getHost(); int port = uri.getPort(); String database = uri.getPath().substring(1); // 去掉首位的 / 斜杠 // 创建 jasync Configuration 配置配置对象 com.github.jasync.sql.db.Configuration configuration = new com.github.jasync.sql.db.Configuration( properties.getUsername(), host, port, properties.getPassword(), database); // 创建 JasyncConnectionFactory 对象 return new JasyncConnectionFactory(new MySQLConnectionFactory(configuration)); } @Bean public ReactiveTransactionManager transactionManager(R2dbcProperties properties) throws URISyntaxException { return new R2dbcTransactionManager(this.connectionFactory(properties)); } } ================================================ FILE: lab-27/lab-27-webflux-r2dbc/src/main/java/cn/iocoder/springboot/lab27/springwebflux/controller/UserController.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.controller; import cn.iocoder.springboot.lab27.springwebflux.dao.UserRepository; import cn.iocoder.springboot.lab27.springwebflux.dataobject.UserDO; import cn.iocoder.springboot.lab27.springwebflux.dto.UserAddDTO; import cn.iocoder.springboot.lab27.springwebflux.dto.UserUpdateDTO; import cn.iocoder.springboot.lab27.springwebflux.vo.UserVO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.util.Date; import java.util.Objects; import java.util.function.Function; /** * 用户 Controller */ @RestController @RequestMapping("/users") public class UserController { private static final UserDO USER_NULL = new UserDO(); @Autowired private UserRepository userRepository; /** * 查询用户列表 * * @return 用户列表 */ @GetMapping("/list") public Flux list() { // 返回列表 return userRepository.findAll() .map(userDO -> new UserVO().setId(userDO.getId()).setUsername(userDO.getUsername())); } /** * 获得指定用户编号的用户 * * @param id 用户编号 * @return 用户 */ @GetMapping("/get") public Mono get(@RequestParam("id") Integer id) { // 返回 return userRepository.findById(id) .map(userDO -> new UserVO().setId(userDO.getId()).setUsername(userDO.getUsername())); } /** * 添加用户 * * @param addDTO 添加用户信息 DTO * @return 添加成功的用户编号 */ @PostMapping("add") @Transactional public Mono add(UserAddDTO addDTO) { // 查询用户 Mono user = userRepository.findByUsername(addDTO.getUsername()); // 执行插入 return user.defaultIfEmpty(USER_NULL) // 设置 USER_NULL 作为 null 的情况,否则 flatMap 不会往下走 .flatMap(new Function>() { @Override public Mono apply(UserDO userDO) { if (userDO != USER_NULL) { // 返回 -1 表示插入失败。 // 实际上,一般是抛出 ServiceException 异常。因为这个示例项目里暂时没做全局异常的定义,所以暂时返回 -1 啦 return Mono.just(-1); } // 将 addDTO 转成 UserDO userDO = new UserDO() .setUsername(addDTO.getUsername()) .setPassword(addDTO.getPassword()) .setCreateTime(new Date()); // 插入数据库 return userRepository.save(userDO).flatMap(new Function>() { @Override public Mono apply(UserDO userDO) { // 如果编号为偶数,抛出异常。 if (userDO.getId() % 2 == 0) { throw new RuntimeException("我就是故意抛出一个异常,测试下事务回滚"); } // 返回编号 return Mono.just(userDO.getId()); } }); } }); } /** * 更新指定用户编号的用户 * * @param updateDTO 更新用户信息 DTO * @return 是否修改成功 */ @PostMapping("/update") public Mono update(UserUpdateDTO updateDTO) { // 查询用户 Mono user = userRepository.findById(updateDTO.getId()); // 执行更新 return user.defaultIfEmpty(USER_NULL) // 设置 USER_NULL 作为 null 的情况,否则 flatMap 不会往下走 .flatMap(new Function>() { @Override public Mono apply(UserDO userDO) { // 如果不存在该用户,则直接返回 false 失败 if (userDO == USER_NULL) { return Mono.just(false); } // 查询用户是否存在 return userRepository.findByUsername(updateDTO.getUsername()) .defaultIfEmpty(USER_NULL) // 设置 USER_NULL 作为 null 的情况,否则 flatMap 不会往下走 .flatMap(new Function>() { @Override public Mono apply(UserDO usernameUserDO) { // 如果用户名已经使用(该用户名对应的 id 不是自己,说明就已经被使用了) if (usernameUserDO != USER_NULL && !Objects.equals(updateDTO.getId(), usernameUserDO.getId())) { return Mono.just(false); } // 执行更新 userDO.setUsername(updateDTO.getUsername()); userDO.setPassword(updateDTO.getPassword()); return userRepository.save(userDO).map(userDO -> true); // 返回 true 成功 } }); } }); } /** * 删除指定用户编号的用户 * * @param id 用户编号 * @return 是否删除成功 */ @PostMapping("/delete") // URL 修改成 /delete ,RequestMethod 改成 DELETE public Mono delete(@RequestParam("id") Integer id) { // 查询用户 Mono user = userRepository.findById(id); // 执行删除。这里仅仅是示例,项目中不要物理删除,而是标记删除 return user.defaultIfEmpty(USER_NULL) // 设置 USER_NULL 作为 null 的情况,否则 flatMap 不会往下走 .flatMap(new Function>() { @Override public Mono apply(UserDO userDO) { // 如果不存在该用户,则直接返回 false 失败 if (userDO == USER_NULL) { return Mono.just(false); } // 执行删除 return userRepository.deleteById(id).map(aVoid -> true); // 返回 true 成功 } }); } } ================================================ FILE: lab-27/lab-27-webflux-r2dbc/src/main/java/cn/iocoder/springboot/lab27/springwebflux/dao/UserRepository.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.dao; import cn.iocoder.springboot.lab27.springwebflux.dataobject.UserDO; import org.springframework.data.r2dbc.repository.query.Query; import org.springframework.data.repository.reactive.ReactiveCrudRepository; import reactor.core.publisher.Mono; public interface UserRepository extends ReactiveCrudRepository { @Query("SELECT id, username, password, create_time FROM users WHERE username = :username") Mono findByUsername(String username); } ================================================ FILE: lab-27/lab-27-webflux-r2dbc/src/main/java/cn/iocoder/springboot/lab27/springwebflux/dataobject/UserDO.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.dataobject; import org.springframework.data.annotation.Id; import org.springframework.data.relational.core.mapping.Table; import java.util.Date; /** * 用户 DO */ @Table(value = "users") public class UserDO { @Id private Integer id; /** * 账号 */ private String username; /** * 密码 */ private String password; /** * 创建时间 */ private Date createTime; public Integer getId() { return id; } public UserDO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserDO setUsername(String username) { this.username = username; return this; } public String getPassword() { return password; } public UserDO setPassword(String password) { this.password = password; return this; } public Date getCreateTime() { return createTime; } public UserDO setCreateTime(Date createTime) { this.createTime = createTime; return this; } @Override public String toString() { return "UserDO{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", createTime=" + createTime + '}'; } } ================================================ FILE: lab-27/lab-27-webflux-r2dbc/src/main/java/cn/iocoder/springboot/lab27/springwebflux/dto/UserAddDTO.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.dto; /** * 用户添加 DTO */ public class UserAddDTO { /** * 账号 */ private String username; /** * 密码 */ private String password; public String getUsername() { return username; } public UserAddDTO setUsername(String username) { this.username = username; return this; } public String getPassword() { return password; } public UserAddDTO setPassword(String password) { this.password = password; return this; } } ================================================ FILE: lab-27/lab-27-webflux-r2dbc/src/main/java/cn/iocoder/springboot/lab27/springwebflux/dto/UserUpdateDTO.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.dto; public class UserUpdateDTO { /** * 编号 */ private Integer id; /** * 账号 */ private String username; /** * 密码 */ private String password; public Integer getId() { return id; } public UserUpdateDTO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserUpdateDTO setUsername(String username) { this.username = username; return this; } public String getPassword() { return password; } public UserUpdateDTO setPassword(String password) { this.password = password; return this; } } ================================================ FILE: lab-27/lab-27-webflux-r2dbc/src/main/java/cn/iocoder/springboot/lab27/springwebflux/vo/UserVO.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.vo; /** * 用户 VO */ public class UserVO { /** * 编号 */ private Integer id; /** * 账号 */ private String username; public Integer getId() { return id; } public UserVO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserVO setUsername(String username) { this.username = username; return this; } } ================================================ FILE: lab-27/lab-27-webflux-r2dbc/src/main/resources/application.yaml ================================================ spring: # R2DBC 配置,对应 R2dbcProperties 配置类 r2dbc: url: mysql://47.112.193.81:3306/lab-27-webflux-r2dbc username: lab-27-webflux-r2dbc password: 0ed86@11-r2Dbc123 # jasync: # r2dbc: # host: 47.112.193.81 # port: 3306 # database: lab-27-webflux-r2dbc # username: lab-27-webflux-r2dbc # password: 0ed86@11-r2Dbc123 ================================================ FILE: lab-27/lab-27-webflux-r2dbc/src/main/resources/sql/users.sql ================================================ CREATE TABLE `users` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号', `username` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号', `password` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '密码', `create_time` datetime DEFAULT NULL COMMENT '创建时间', PRIMARY KEY (`id`), UNIQUE KEY `idx_username` (`username`) ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; ================================================ FILE: lab-27/lab-27-webflux-redis/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-27-webflux-redis org.springframework.boot spring-boot-starter-webflux 2.2.1.RELEASE org.springframework.boot spring-boot-starter-data-redis-reactive org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-27/lab-27-webflux-redis/src/main/java/cn/iocoder/springboot/lab27/springwebflux/Application.java ================================================ package cn.iocoder.springboot.lab27.springwebflux; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-27/lab-27-webflux-redis/src/main/java/cn/iocoder/springboot/lab27/springwebflux/cacheobject/UserCacheObject.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.cacheobject; /** * 用户缓存对象 */ public class UserCacheObject { /** * 用户编号 */ private Integer id; /** * 昵称 */ private String name; /** * 性别 */ private Integer gender; public Integer getId() { return id; } public UserCacheObject setId(Integer id) { this.id = id; return this; } public String getName() { return name; } public UserCacheObject setName(String name) { this.name = name; return this; } public Integer getGender() { return gender; } public UserCacheObject setGender(Integer gender) { this.gender = gender; return this; } @Override public String toString() { return "UserCacheObject{" + "id=" + id + ", name='" + name + '\'' + ", gender=" + gender + '}'; } } ================================================ FILE: lab-27/lab-27-webflux-redis/src/main/java/cn/iocoder/springboot/lab27/springwebflux/config/RedisConfiguration.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.config; import cn.iocoder.springboot.lab27.springwebflux.cacheobject.UserCacheObject; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory; import org.springframework.data.redis.core.ReactiveRedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializationContext; import org.springframework.data.redis.serializer.RedisSerializer; @Configuration public class RedisConfiguration { @Bean public ReactiveRedisTemplate commonRedisTemplate(ReactiveRedisConnectionFactory factory) { RedisSerializationContext serializationContext = RedisSerializationContext.newSerializationContext(RedisSerializer.string()) .value(RedisSerializer.json()) // 创建通用的 GenericJackson2JsonRedisSerializer 作为序列化 .build(); return new ReactiveRedisTemplate<>(factory, serializationContext); } @Bean public ReactiveRedisTemplate userRedisTemplate(ReactiveRedisConnectionFactory factory) { RedisSerializationContext serializationContext = RedisSerializationContext.newSerializationContext(RedisSerializer.string()) .value(new Jackson2JsonRedisSerializer<>(UserCacheObject.class)) // 创建专属 UserCacheObject 的 Jackson2JsonRedisSerializer 作为序列化 .build(); return new ReactiveRedisTemplate<>(factory, serializationContext); } } ================================================ FILE: lab-27/lab-27-webflux-redis/src/main/java/cn/iocoder/springboot/lab27/springwebflux/controller/UserController.java ================================================ package cn.iocoder.springboot.lab27.springwebflux.controller; import cn.iocoder.springboot.lab27.springwebflux.cacheobject.UserCacheObject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.ReactiveRedisTemplate; import org.springframework.web.bind.annotation.*; import reactor.core.publisher.Mono; /** * 用户 Controller */ @RestController @RequestMapping("/users") public class UserController { // ========== 使用通用的 ReactiveRedisTemplate 的方式 ========== @Autowired private ReactiveRedisTemplate commonRedisTemplate; /** * 获得指定用户编号的用户 * * @param id 用户编号 * @return 用户 */ @GetMapping("/get") public Mono get(@RequestParam("id") Integer id) { String key = genKey(id); return commonRedisTemplate.opsForValue().get(key) .map(o -> (UserCacheObject) o); } /** * 设置指定用户的信息 * * @param user 用户 * @return 是否成功 */ @PostMapping("/set") public Mono set(UserCacheObject user) { String key = genKey(user.getId()); return commonRedisTemplate.opsForValue().set(key, user); } private static String genKey(Integer id) { return "user::" + id; } // ========== 使用专属的 ReactiveRedisTemplate 的方式 ========= @Autowired private ReactiveRedisTemplate userRedisTemplate; /** * 获得指定用户编号的用户 * * @param id 用户编号 * @return 用户 */ @GetMapping("/v2/get") public Mono getV2(@RequestParam("id") Integer id) { String key = genKeyV2(id); return userRedisTemplate.opsForValue().get(key); } /** * 设置指定用户的信息 * * @param user 用户 * @return 是否成功 */ @PostMapping("/v2/set") public Mono setV2(UserCacheObject user) { String key = genKeyV2(user.getId()); return userRedisTemplate.opsForValue().set(key, user); } private static String genKeyV2(Integer id) { return "user::v2::" + id; } } ================================================ FILE: lab-27/lab-27-webflux-redis/src/main/resources/application.yaml ================================================ spring: # 对应 RedisProperties 类 redis: host: 127.0.0.1 port: 6379 password: # Redis 服务器密码,默认为空。生产中,一定要设置 Redis 密码! database: 0 # Redis 数据库号,默认为 0 。 timeout: 0 # Redis 连接超时时间,单位:毫秒。 ================================================ FILE: lab-27/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-27 pom lab-27-webflux-01 lab-27-webflux-02 lab-27-webflux-03 lab-27-webflux-mongodb lab-27-webflux-redis lab-27-webflux-elasticsearch lab-27-webflux-r2dbc ================================================ FILE: lab-27/《芋道 Spring Boot 响应式 WebFlux 入门》.md ================================================ ================================================ FILE: lab-28/lab-28-task-demo/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-28-task-demo org.springframework.boot spring-boot-starter-web ================================================ FILE: lab-28/lab-28-task-demo/src/main/java/cn/iocoder/springboot/lab28/task/Application.java ================================================ package cn.iocoder.springboot.lab28.task; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-28/lab-28-task-demo/src/main/java/cn/iocoder/springboot/lab28/task/config/ScheduleConfiguration.java ================================================ package cn.iocoder.springboot.lab28.task.config; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableScheduling; @Configuration @EnableScheduling public class ScheduleConfiguration { } ================================================ FILE: lab-28/lab-28-task-demo/src/main/java/cn/iocoder/springboot/lab28/task/job/DemoJob.java ================================================ package cn.iocoder.springboot.lab28.task.job; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import java.util.concurrent.atomic.AtomicInteger; @Component public class DemoJob { private Logger logger = LoggerFactory.getLogger(getClass()); private final AtomicInteger counts = new AtomicInteger(); @Scheduled(fixedRate = 2000) public void execute() { logger.info("[execute][定时第 ({}) 次执行]", counts.incrementAndGet()); // try { // Thread.sleep(10000L); // } catch (InterruptedException e) { // e.printStackTrace(); // } } } ================================================ FILE: lab-28/lab-28-task-demo/src/main/resources/application.yaml ================================================ spring: task: # Spring Task 调度任务的配置,对应 TaskSchedulingProperties 配置类 scheduling: thread-name-prefix: pikaqiu-demo- # 线程池的线程名的前缀。默认为 scheduling- ,建议根据自己应用来设置 pool: size: 10 # 线程池大小。默认为 1 ,根据自己应用来设置 shutdown: await-termination: true # 应用关闭时,是否等待定时任务执行完成。默认为 false ,建议设置为 true await-termination-period: 60 # 等待任务完成的最大时长,单位为秒。默认为 0 ,根据自己应用来设置 ================================================ FILE: lab-28/lab-28-task-quartz-jdbc/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.10.RELEASE 4.0.0 lab-28-task-quartz-jdbc org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 5.1.48 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-quartz org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-28/lab-28-task-quartz-jdbc/src/main/java/cn/iocoder/springboot/lab28/task/Application.java ================================================ package cn.iocoder.springboot.lab28.task; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-28/lab-28-task-quartz-jdbc/src/main/java/cn/iocoder/springboot/lab28/task/Application02.java ================================================ package cn.iocoder.springboot.lab28.task; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application02 { public static void main(String[] args) { // 设置 Tomcat 随机端口 System.setProperty("server.port", "0"); // 启动 Spring Boot 应用 SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-28/lab-28-task-quartz-jdbc/src/main/java/cn/iocoder/springboot/lab28/task/config/DataSourceConfiguration.java ================================================ package cn.iocoder.springboot.lab28.task.config; import com.zaxxer.hikari.HikariDataSource; import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; import org.springframework.boot.autoconfigure.quartz.QuartzDataSource; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.util.StringUtils; import javax.sql.DataSource; @Configuration public class DataSourceConfiguration { /** * 创建 user 数据源的配置对象 */ @Primary @Bean(name = "userDataSourceProperties") @ConfigurationProperties(prefix = "spring.datasource.user") // 读取 spring.datasource.user 配置到 DataSourceProperties 对象 public DataSourceProperties userDataSourceProperties() { return new DataSourceProperties(); } /** * 创建 user 数据源 */ @Primary @Bean(name = "userDataSource") @ConfigurationProperties(prefix = "spring.datasource.user.hikari") // 读取 spring.datasource.user 配置到 HikariDataSource 对象 public DataSource userDataSource() { // 获得 DataSourceProperties 对象 DataSourceProperties properties = this.userDataSourceProperties(); // 创建 HikariDataSource 对象 return createHikariDataSource(properties); } /** * 创建 quartz 数据源的配置对象 */ @Bean(name = "quartzDataSourceProperties") @ConfigurationProperties(prefix = "spring.datasource.quartz") // 读取 spring.datasource.quartz 配置到 DataSourceProperties 对象 public DataSourceProperties quartzDataSourceProperties() { return new DataSourceProperties(); } /** * 创建 quartz 数据源 */ @Bean(name = "quartzDataSource") @ConfigurationProperties(prefix = "spring.datasource.quartz.hikari") @QuartzDataSource public DataSource quartzDataSource() { // 获得 DataSourceProperties 对象 DataSourceProperties properties = this.quartzDataSourceProperties(); // 创建 HikariDataSource 对象 return createHikariDataSource(properties); } private static HikariDataSource createHikariDataSource(DataSourceProperties properties) { // 创建 HikariDataSource 对象 HikariDataSource dataSource = properties.initializeDataSourceBuilder().type(HikariDataSource.class).build(); // 设置线程池名 if (StringUtils.hasText(properties.getName())) { dataSource.setPoolName(properties.getName()); } return dataSource; } } ================================================ FILE: lab-28/lab-28-task-quartz-jdbc/src/main/java/cn/iocoder/springboot/lab28/task/config/ScheduleConfiguration.java ================================================ package cn.iocoder.springboot.lab28.task.config; import cn.iocoder.springboot.lab28.task.job.DemoJob01; import cn.iocoder.springboot.lab28.task.job.DemoJob02; import org.quartz.*; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class ScheduleConfiguration { public static class DemoJob01Configuration { @Bean public JobDetail demoJob01() { return JobBuilder.newJob(DemoJob01.class) .withIdentity("demoJob01") // 名字为 demoJob01 .storeDurably() // 没有 Trigger 关联的时候任务是否被保留。因为创建 JobDetail 时,还没 Trigger 指向它,所以需要设置为 true ,表示保留。 .build(); } @Bean public Trigger demoJob01Trigger() { // 简单的调度计划的构造器 SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule() .withIntervalInSeconds(5) // 频率。 .repeatForever(); // 次数。 // Trigger 构造器 return TriggerBuilder.newTrigger() .forJob(demoJob01()) // 对应 Job 为 demoJob01 .withIdentity("demoJob01Trigger") // 名字为 demoJob01Trigger .withSchedule(scheduleBuilder) // 对应 Schedule 为 scheduleBuilder .build(); } } public static class DemoJob02Configuration { @Bean public JobDetail demoJob02() { return JobBuilder.newJob(DemoJob02.class) .withIdentity("demoJob02") // 名字为 demoJob02 .storeDurably() // 没有 Trigger 关联的时候任务是否被保留。因为创建 JobDetail 时,还没 Trigger 指向它,所以需要设置为 true ,表示保留。 .build(); } @Bean public Trigger demoJob02Trigger() { // 简单的调度计划的构造器 CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule("0/10 * * * * ? *"); // Trigger 构造器 return TriggerBuilder.newTrigger() .forJob(demoJob02()) // 对应 Job 为 demoJob02 .withIdentity("demoJob02Trigger") // 名字为 demoJob02Trigger .withSchedule(scheduleBuilder) // 对应 Schedule 为 scheduleBuilder .build(); } } } ================================================ FILE: lab-28/lab-28-task-quartz-jdbc/src/main/java/cn/iocoder/springboot/lab28/task/job/DemoJob01.java ================================================ package cn.iocoder.springboot.lab28.task.job; import cn.iocoder.springboot.lab28.task.service.DemoService; import org.quartz.DisallowConcurrentExecution; import org.quartz.JobExecutionContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.quartz.QuartzJobBean; @DisallowConcurrentExecution public class DemoJob01 extends QuartzJobBean { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private DemoService demoService; @Override protected void executeInternal(JobExecutionContext context) { logger.info("[executeInternal][我开始的执行了, demoService 为 ({})]", demoService); } } ================================================ FILE: lab-28/lab-28-task-quartz-jdbc/src/main/java/cn/iocoder/springboot/lab28/task/job/DemoJob02.java ================================================ package cn.iocoder.springboot.lab28.task.job; import org.quartz.DisallowConcurrentExecution; import org.quartz.JobExecutionContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.quartz.QuartzJobBean; @DisallowConcurrentExecution public class DemoJob02 extends QuartzJobBean { private Logger logger = LoggerFactory.getLogger(getClass()); @Override protected void executeInternal(JobExecutionContext context) { logger.info("[executeInternal][我开始的执行了]"); } } ================================================ FILE: lab-28/lab-28-task-quartz-jdbc/src/main/java/cn/iocoder/springboot/lab28/task/service/DemoService.java ================================================ package cn.iocoder.springboot.lab28.task.service; import org.springframework.stereotype.Service; @Service public class DemoService { } ================================================ FILE: lab-28/lab-28-task-quartz-jdbc/src/main/resources/application.yaml ================================================ spring: datasource: user: url: jdbc:mysql://127.0.0.1:3306/lab-28-quartz-jdbc-user?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: quartz: url: jdbc:mysql://127.0.0.1:3306/lab-28-quartz-jdbc-quartz?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: # Quartz 的配置,对应 QuartzProperties 配置类 quartz: scheduler-name: clusteredScheduler # Scheduler 名字。默认为 schedulerName job-store-type: jdbc # Job 存储器类型。默认为 memory 表示内存,可选 jdbc 使用数据库。 auto-startup: true # Quartz 是否自动启动 startup-delay: 0 # 延迟 N 秒启动 wait-for-jobs-to-complete-on-shutdown: true # 应用关闭时,是否等待定时任务执行完成。默认为 false ,建议设置为 true overwrite-existing-jobs: false # 是否覆盖已有 Job 的配置 properties: # 添加 Quartz Scheduler 附加属性,更多可以看 http://www.quartz-scheduler.org/documentation/2.4.0-SNAPSHOT/configuration.html 文档 org: quartz: # JobStore 相关配置 jobStore: # 数据源名称 dataSource: quartzDataSource # 使用的数据源 class: org.quartz.impl.jdbcjobstore.JobStoreTX # JobStore 实现类 driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate tablePrefix: QRTZ_ # Quartz 表前缀 isClustered: true # 是集群模式 clusterCheckinInterval: 1000 useProperties: false # 线程池相关配置 threadPool: threadCount: 25 # 线程池大小。默认为 10 。 threadPriority: 5 # 线程优先级 class: org.quartz.simpl.SimpleThreadPool # 线程池类型 jdbc: # 使用 JDBC 的 JobStore 的时候,JDBC 的配置 initialize-schema: never # 是否自动使用 SQL 初始化 Quartz 表结构。这里设置成 never ,我们手动创建表结构。 ================================================ FILE: lab-28/lab-28-task-quartz-jdbc/src/test/java/cn/iocoder/springboot/lab28/task/QuartzSchedulerTest.java ================================================ package cn.iocoder.springboot.lab28.task; import cn.iocoder.springboot.lab28.task.job.DemoJob01; import cn.iocoder.springboot.lab28.task.job.DemoJob02; import org.junit.Test; import org.junit.runner.RunWith; import org.quartz.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class QuartzSchedulerTest { @Autowired private Scheduler scheduler; @Test public void addDemoJob01Config() throws SchedulerException { // 创建 JobDetail JobDetail jobDetail = JobBuilder.newJob(DemoJob01.class) .withIdentity("demoJob01") // 名字为 demoJob01 .storeDurably() // 没有 Trigger 关联的时候任务是否被保留。因为创建 JobDetail 时,还没 Trigger 指向它,所以需要设置为 true ,表示保留。 .build(); // 创建 Trigger SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule() .withIntervalInSeconds(5) // 频率。 .repeatForever(); // 次数。 Trigger trigger = TriggerBuilder.newTrigger() .forJob(jobDetail) // 对应 Job 为 demoJob01 .withIdentity("demoJob01Trigger") // 名字为 demoJob01Trigger .withSchedule(scheduleBuilder) // 对应 Schedule 为 scheduleBuilder .build(); // 添加调度任务 scheduler.scheduleJob(jobDetail, trigger); } @Test public void addDemoJob02Config() throws SchedulerException { // 创建 JobDetail JobDetail jobDetail = JobBuilder.newJob(DemoJob02.class) .withIdentity("demoJob02") // 名字为 demoJob02 .storeDurably() // 没有 Trigger 关联的时候任务是否被保留。因为创建 JobDetail 时,还没 Trigger 指向它,所以需要设置为 true ,表示保留。 .build(); // 创建 Trigger CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule("0/10 * * * * ? *"); Trigger trigger = TriggerBuilder.newTrigger() .forJob(jobDetail) // 对应 Job 为 demoJob01 .withIdentity("demoJob02Trigger") // 名字为 demoJob01Trigger .withSchedule(scheduleBuilder) // 对应 Schedule 为 scheduleBuilder .build(); // 添加调度任务 scheduler.scheduleJob(jobDetail, trigger); // scheduler.scheduleJob(jobDetail, Sets.newSet(trigger), true); } } ================================================ FILE: lab-28/lab-28-task-quartz-memory/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-28-task-quartz-memory org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-quartz ================================================ FILE: lab-28/lab-28-task-quartz-memory/src/main/java/cn/iocoder/springboot/lab28/task/Application.java ================================================ package cn.iocoder.springboot.lab28.task; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-28/lab-28-task-quartz-memory/src/main/java/cn/iocoder/springboot/lab28/task/config/ScheduleConfiguration.java ================================================ package cn.iocoder.springboot.lab28.task.config; import cn.iocoder.springboot.lab28.task.job.DemoJob01; import cn.iocoder.springboot.lab28.task.job.DemoJob02; import org.quartz.*; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class ScheduleConfiguration { public static class DemoJob01Configuration { @Bean public JobDetail demoJob01() { return JobBuilder.newJob(DemoJob01.class) .withIdentity("demoJob01") // 名字为 demoJob01 .storeDurably() // 没有 Trigger 关联的时候任务是否被保留。因为创建 JobDetail 时,还没 Trigger 指向它,所以需要设置为 true ,表示保留。 .build(); } @Bean public Trigger demoJob01Trigger() { // 简单的调度计划的构造器 SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule() .withIntervalInSeconds(5) // 频率。 .repeatForever(); // 次数。 // Trigger 构造器 return TriggerBuilder.newTrigger() .forJob(demoJob01()) // 对应 Job 为 demoJob01 .withIdentity("demoJob01Trigger") // 名字为 demoJob01Trigger .withSchedule(scheduleBuilder) // 对应 Schedule 为 scheduleBuilder .build(); } } public static class DemoJob02Configuration { @Bean public JobDetail demoJob02() { return JobBuilder.newJob(DemoJob02.class) .withIdentity("demoJob02") // 名字为 demoJob02 .storeDurably() // 没有 Trigger 关联的时候任务是否被保留。因为创建 JobDetail 时,还没 Trigger 指向它,所以需要设置为 true ,表示保留。 .build(); } @Bean public Trigger demoJob02Trigger() { // 基于 Quartz Cron 表达式的调度计划的构造器 CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule("0/10 * * * * ? *"); // Trigger 构造器 return TriggerBuilder.newTrigger() .forJob(demoJob02()) // 对应 Job 为 demoJob02 .withIdentity("demoJob02Trigger") // 名字为 demoJob02Trigger .withSchedule(scheduleBuilder) // 对应 Schedule 为 scheduleBuilder .build(); } } } ================================================ FILE: lab-28/lab-28-task-quartz-memory/src/main/java/cn/iocoder/springboot/lab28/task/job/DemoJob01.java ================================================ package cn.iocoder.springboot.lab28.task.job; import cn.iocoder.springboot.lab28.task.service.DemoService; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.quartz.QuartzJobBean; import java.util.concurrent.atomic.AtomicInteger; public class DemoJob01 extends QuartzJobBean { private Logger logger = LoggerFactory.getLogger(getClass()); private final AtomicInteger counts = new AtomicInteger(); @Autowired private DemoService demoService; @Override protected void executeInternal(JobExecutionContext context) throws JobExecutionException { logger.info("[executeInternal][定时第 ({}) 次执行, demoService 为 ({})]", counts.incrementAndGet(), demoService); } } ================================================ FILE: lab-28/lab-28-task-quartz-memory/src/main/java/cn/iocoder/springboot/lab28/task/job/DemoJob02.java ================================================ package cn.iocoder.springboot.lab28.task.job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.quartz.QuartzJobBean; public class DemoJob02 extends QuartzJobBean { private Logger logger = LoggerFactory.getLogger(getClass()); @Override protected void executeInternal(JobExecutionContext context) throws JobExecutionException { logger.info("[executeInternal][我开始的执行了]"); } } ================================================ FILE: lab-28/lab-28-task-quartz-memory/src/main/java/cn/iocoder/springboot/lab28/task/service/DemoService.java ================================================ package cn.iocoder.springboot.lab28.task.service; import org.springframework.stereotype.Service; @Service public class DemoService { } ================================================ FILE: lab-28/lab-28-task-quartz-memory/src/main/resources/application.yaml ================================================ spring: # Quartz 的配置,对应 QuartzProperties 配置类 quartz: job-store-type: memory # Job 存储器类型。默认为 memory 表示内存,可选 jdbc 使用数据库。 auto-startup: true # Quartz 是否自动启动 startup-delay: 0 # 延迟 N 秒启动 wait-for-jobs-to-complete-on-shutdown: true # 应用关闭时,是否等待定时任务执行完成。默认为 false ,建议设置为 true overwrite-existing-jobs: false # 是否覆盖已有 Job 的配置 properties: # 添加 Quartz Scheduler 附加属性,更多可以看 http://www.quartz-scheduler.org/documentation/2.4.0-SNAPSHOT/configuration.html 文档 org: quartz: threadPool: threadCount: 25 # 线程池大小。默认为 10 。 threadPriority: 5 # 线程优先级 class: org.quartz.simpl.SimpleThreadPool # 线程池类型 # jdbc: # 这里暂时不说明,使用 JDBC 的 JobStore 的时候,才需要配置 ================================================ FILE: lab-28/lab-28-task-xxl-job/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-28-task-xxl-job org.springframework.boot spring-boot-starter-web com.xuxueli xxl-job-core 2.1.1 ================================================ FILE: lab-28/lab-28-task-xxl-job/src/main/java/cn/iocoder/springboot/lab28/task/Application.java ================================================ package cn.iocoder.springboot.lab28.task; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-28/lab-28-task-xxl-job/src/main/java/cn/iocoder/springboot/lab28/task/config/XxlJobConfiguration.java ================================================ package cn.iocoder.springboot.lab28.task.config; import com.xxl.job.core.executor.impl.XxlJobSpringExecutor; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class XxlJobConfiguration { @Value("${xxl.job.admin.addresses}") private String adminAddresses; @Value("${xxl.job.executor.appname}") private String appName; @Value("${xxl.job.executor.ip}") private String ip; @Value("${xxl.job.executor.port}") private int port; @Value("${xxl.job.accessToken}") private String accessToken; @Value("${xxl.job.executor.logpath}") private String logPath; @Value("${xxl.job.executor.logretentiondays}") private int logRetentionDays; @Bean public XxlJobSpringExecutor xxlJobExecutor() { // 创建 XxlJobSpringExecutor 执行器 XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor(); xxlJobSpringExecutor.setAdminAddresses(adminAddresses); xxlJobSpringExecutor.setAppName(appName); xxlJobSpringExecutor.setIp(ip); xxlJobSpringExecutor.setPort(port); xxlJobSpringExecutor.setAccessToken(accessToken); xxlJobSpringExecutor.setLogPath(logPath); xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays); // 返回 return xxlJobSpringExecutor; } } ================================================ FILE: lab-28/lab-28-task-xxl-job/src/main/java/cn/iocoder/springboot/lab28/task/job/DemoJob.java ================================================ package cn.iocoder.springboot.lab28.task.job; import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.core.handler.IJobHandler; import com.xxl.job.core.handler.annotation.JobHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import java.util.concurrent.atomic.AtomicInteger; @Component @JobHandler("demoJob") public class DemoJob extends IJobHandler { private Logger logger = LoggerFactory.getLogger(getClass()); private final AtomicInteger counts = new AtomicInteger(); @Override public ReturnT execute(String param) throws Exception { // 打印日志 logger.info("[execute][定时第 ({}) 次执行]", counts.incrementAndGet()); // 返回执行成功 return ReturnT.SUCCESS; } } ================================================ FILE: lab-28/lab-28-task-xxl-job/src/main/resources/application.yaml ================================================ server: port: 9090 #指定一个端口,避免和 XXL-JOB 调度中心的端口冲突。仅仅测试之用 # xxl-job xxl: job: admin: addresses: http://127.0.0.1:8080/xxl-job-admin # 调度中心部署跟地址 [选填]:如调度中心集群部署存在多个地址则用逗号分隔。执行器将会使用该地址进行"执行器心跳注册"和"任务结果回调";为空则关闭自动注册; executor: appname: lab-28-executor # 执行器 AppName [选填]:执行器心跳注册分组依据;为空则关闭自动注册 ip: # 执行器IP [选填]:默认为空表示自动获取IP,多网卡时可手动设置指定IP,该IP不会绑定Host仅作为通讯实用;地址信息用于 "执行器注册" 和 "调度中心请求并触发任务"; port: 6666 # ### 执行器端口号 [选填]:小于等于0则自动获取;默认端口为9999,单机部署多个执行器时,注意要配置不同执行器端口; logpath: /Users/yunai/logs/xxl-job/lab-28-executor # 执行器运行日志文件存储磁盘路径 [选填] :需要对该路径拥有读写权限;为空则使用默认路径; logretentiondays: 30 # 执行器日志文件保存天数 [选填] : 过期日志自动清理, 限制值大于等于3时生效; 否则, 如-1, 关闭自动清理功能; accessToken: yudaoyuanma # 执行器通讯TOKEN [选填]:非空时启用; ================================================ FILE: lab-28/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-28 pom lab-28-task-demo lab-28-task-quartz-memory lab-28-task-quartz-jdbc lab-28-task-xxl-job ================================================ FILE: lab-28/《芋道 Spring Boot 定时任务入门》.md ================================================ ================================================ FILE: lab-29/lab-29-async-demo/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-29-async-demo org.springframework.boot spring-boot-starter org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-29/lab-29-async-demo/src/main/java/cn/iocoder/springboot/lab29/asynctask/Application.java ================================================ package cn.iocoder.springboot.lab29.asynctask; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableAsync; @SpringBootApplication @EnableAsync // 开启 @Async 的支持 public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-29/lab-29-async-demo/src/main/java/cn/iocoder/springboot/lab29/asynctask/Demo.java ================================================ package cn.iocoder.springboot.lab29.asynctask; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class Demo { public static void main(String[] args) { // 创建线程池。这里只是临时测试,不要扣艿艿遵守阿里 Java 开发规范,YEAH ExecutorService executor = Executors.newFixedThreadPool(10); // 提交任务到线程池中执行。 executor.submit(new Runnable() { @Override public void run() { System.out.println("听说我被异步调用了"); } }); } } ================================================ FILE: lab-29/lab-29-async-demo/src/main/java/cn/iocoder/springboot/lab29/asynctask/config/AsyncConfig.java ================================================ package cn.iocoder.springboot.lab29.asynctask.config; import cn.iocoder.springboot.lab29.asynctask.core.async.GlobalAsyncExceptionHandler; import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.AsyncConfigurer; import org.springframework.scheduling.annotation.EnableAsync; import java.util.concurrent.Executor; @Configuration @EnableAsync // 开启 @Async 的支持 public class AsyncConfig implements AsyncConfigurer { @Autowired private GlobalAsyncExceptionHandler exceptionHandler; @Override public Executor getAsyncExecutor() { return null; } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return exceptionHandler; } } ================================================ FILE: lab-29/lab-29-async-demo/src/main/java/cn/iocoder/springboot/lab29/asynctask/core/async/GlobalAsyncExceptionHandler.java ================================================ package cn.iocoder.springboot.lab29.asynctask.core.async; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; import org.springframework.stereotype.Component; import java.lang.reflect.Method; @Component public class GlobalAsyncExceptionHandler implements AsyncUncaughtExceptionHandler { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public void handleUncaughtException(Throwable ex, Method method, Object... params) { logger.error("[handleUncaughtException][method({}) params({}) 发生异常]", method, params, ex); } } ================================================ FILE: lab-29/lab-29-async-demo/src/main/java/cn/iocoder/springboot/lab29/asynctask/core/package-info.java ================================================ /** * 核心封装 */ package cn.iocoder.springboot.lab29.asynctask.core; ================================================ FILE: lab-29/lab-29-async-demo/src/main/java/cn/iocoder/springboot/lab29/asynctask/service/DemoService.java ================================================ package cn.iocoder.springboot.lab29.asynctask.service; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.AsyncResult; import org.springframework.stereotype.Service; import org.springframework.util.concurrent.ListenableFuture; import java.util.concurrent.Future; @Service public class DemoService { private Logger logger = LoggerFactory.getLogger(getClass()); // public void task01() { // long now = System.currentTimeMillis(); // logger.info("[task01][开始执行]"); // // execute01(); // execute02(); // // logger.info("[task01][结束执行,消耗时长 {} 毫秒]", System.currentTimeMillis() - now); // } // // public void task02() { // long now = System.currentTimeMillis(); // logger.info("[task02][开始执行]"); // // execute01Async(); // execute02Async(); // // logger.info("[task02][结束执行,消耗时长 {} 毫秒]", System.currentTimeMillis() - now); // } // // public void task03() throws ExecutionException, InterruptedException { // long now = System.currentTimeMillis(); // logger.info("[task03][开始执行]"); // // // 执行任务 // Future execute01Result = execute01AsyncWithFuture(); // Future execute02Result = execute02AsyncWithFuture(); // // 阻塞等待结果 // execute01Result.get(); // execute02Result.get(); // // logger.info("[task03][结束执行,消耗时长 {} 毫秒]", System.currentTimeMillis() - now); // } @Async public Integer execute01Async() { return this.execute01(); } @Async public Integer execute02Async() { return this.execute02(); } @Async public Future execute01AsyncWithFuture() { return AsyncResult.forValue(this.execute01()); } @Async public Future execute02AsyncWithFuture() { return AsyncResult.forValue(this.execute02()); } @Async public ListenableFuture execute01AsyncWithListenableFuture() { try { return AsyncResult.forValue(this.execute02()); } catch (Throwable ex) { return AsyncResult.forExecutionException(ex); } } public Integer execute01() { logger.info("[execute01]"); sleep(10); return 1; } public Integer execute02() { logger.info("[execute02]"); sleep(5); return 2; } private static void sleep(int seconds) { try { Thread.sleep(seconds * 1000); } catch (InterruptedException e) { throw new RuntimeException(e); } } @Async public Integer zhaoDaoNvPengYou(Integer a, Integer b) { throw new RuntimeException("程序员不需要女朋友"); } } ================================================ FILE: lab-29/lab-29-async-demo/src/main/resources/application.yaml ================================================ spring: task: # Spring 执行器配置,对应 TaskExecutionProperties 配置类。对于 Spring 异步任务,会使用该执行器。 execution: thread-name-prefix: task- # 线程池的线程名的前缀。默认为 task- ,建议根据自己应用来设置 pool: # 线程池相关 core-size: 8 # 核心线程数,线程池创建时候初始化的线程数。默认为 8 。 max-size: 20 # 最大线程数,线程池最大的线程数,只有在缓冲队列满了之后,才会申请超过核心线程数的线程。默认为 Integer.MAX_VALUE keep-alive: 60s # 允许线程的空闲时间,当超过了核心线程之外的线程,在空闲时间到达之后会被销毁。默认为 60 秒 queue-capacity: 200 # 缓冲队列大小,用来缓冲执行任务的队列的大小。默认为 Integer.MAX_VALUE 。 allow-core-thread-timeout: true # 是否允许核心线程超时,即开启线程池的动态增长和缩小。默认为 true 。 shutdown: await-termination: true # 应用关闭时,是否等待定时任务执行完成。默认为 false ,建议设置为 true await-termination-period: 60 # 等待任务完成的最大时长,单位为秒。默认为 0 ,根据自己应用来设置 ================================================ FILE: lab-29/lab-29-async-demo/src/test/java/cn/iocoder/springboot/lab29/asynctask/package-info.java ================================================ package cn.iocoder.springboot.lab29.asynctask; ================================================ FILE: lab-29/lab-29-async-demo/src/test/java/cn/iocoder/springboot/lab29/asynctask/service/DemoServiceTest.java ================================================ package cn.iocoder.springboot.lab29.asynctask.service; import cn.iocoder.springboot.lab29.asynctask.Application; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.util.concurrent.FailureCallback; import org.springframework.util.concurrent.ListenableFuture; import org.springframework.util.concurrent.ListenableFutureCallback; import org.springframework.util.concurrent.SuccessCallback; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class DemoServiceTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private DemoService demoService; @Test public void task01() { long now = System.currentTimeMillis(); logger.info("[task01][开始执行]"); demoService.execute01(); demoService.execute02(); logger.info("[task01][结束执行,消耗时长 {} 毫秒]", System.currentTimeMillis() - now); } @Test public void task02() { long now = System.currentTimeMillis(); logger.info("[task02][开始执行]"); demoService.execute01Async(); demoService.execute02Async(); logger.info("[task02][结束执行,消耗时长 {} 毫秒]", System.currentTimeMillis() - now); } @Test public void task03() throws ExecutionException, InterruptedException { long now = System.currentTimeMillis(); logger.info("[task03][开始执行]"); // 执行任务 Future execute01Result = demoService.execute01AsyncWithFuture(); Future execute02Result = demoService.execute02AsyncWithFuture(); // 阻塞等待结果 execute01Result.get(); execute02Result.get(); logger.info("[task03][结束执行,消耗时长 {} 毫秒]", System.currentTimeMillis() - now); } @Test public void task04() throws ExecutionException, InterruptedException { long now = System.currentTimeMillis(); logger.info("[task04][开始执行]"); // 执行任务 ListenableFuture execute01Result = demoService.execute01AsyncWithListenableFuture(); logger.info("[task04][execute01Result 的类型是:({})]",execute01Result.getClass().getSimpleName()); execute01Result.addCallback(new SuccessCallback() { // 增加成功的回调 @Override public void onSuccess(Integer result) { logger.info("[onSuccess][result: {}]", result); } }, new FailureCallback() { // 增加失败的回调 @Override public void onFailure(Throwable ex) { logger.info("[onFailure][发生异常]", ex); } }); execute01Result.addCallback(new ListenableFutureCallback() { // 增加成功和失败的统一回调 @Override public void onSuccess(Integer result) { logger.info("[onSuccess][result: {}]", result); } @Override public void onFailure(Throwable ex) { logger.info("[onFailure][发生异常]", ex); } }); // 阻塞等待结果 execute01Result.get(); logger.info("[task04][结束执行,消耗时长 {} 毫秒]", System.currentTimeMillis() - now); } @Test public void testZhaoDaoNvPengYou() throws InterruptedException { demoService.zhaoDaoNvPengYou(1, 2); // sleep 1 秒,保证异步调用的执行 Thread.sleep(1000); } } ================================================ FILE: lab-29/lab-29-async-two/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-29-async-two org.springframework.boot spring-boot-starter org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-29/lab-29-async-two/src/main/java/cn/iocoder/springboot/lab29/asynctask/Application.java ================================================ package cn.iocoder.springboot.lab29.asynctask; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-29/lab-29-async-two/src/main/java/cn/iocoder/springboot/lab29/asynctask/config/AsyncConfig.java ================================================ package cn.iocoder.springboot.lab29.asynctask.config; import org.springframework.boot.autoconfigure.task.TaskExecutionProperties; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.task.TaskExecutorBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; @Configuration @EnableAsync // 开启 @Async 的支持 public class AsyncConfig { public static final String EXECUTOR_ONE_BEAN_NAME = "executor-one"; public static final String EXECUTOR_TWO_BEAN_NAME = "executor-two"; @Configuration public static class ExecutorOneConfiguration { @Bean(name = EXECUTOR_ONE_BEAN_NAME + "-properties") @Primary @ConfigurationProperties(prefix = "spring.task.execution-one") // 读取 spring.task.execution-one 配置到 TaskExecutionProperties 对象 public TaskExecutionProperties taskExecutionProperties() { return new TaskExecutionProperties(); } @Bean(name = EXECUTOR_ONE_BEAN_NAME) public ThreadPoolTaskExecutor threadPoolTaskExecutor() { // 创建 TaskExecutorBuilder 对象 TaskExecutorBuilder builder = createTskExecutorBuilder(this.taskExecutionProperties()); // 创建 ThreadPoolTaskExecutor 对象 return builder.build(); } } @Configuration public static class ExecutorTwoConfiguration { @Bean(name = EXECUTOR_TWO_BEAN_NAME + "-properties") @ConfigurationProperties(prefix = "spring.task.execution-two") // 读取 spring.task.execution-two 配置到 TaskExecutionProperties 对象 public TaskExecutionProperties taskExecutionProperties() { return new TaskExecutionProperties(); } @Bean(name = EXECUTOR_TWO_BEAN_NAME) public ThreadPoolTaskExecutor threadPoolTaskExecutor() { // 创建 TaskExecutorBuilder 对象 TaskExecutorBuilder builder = createTskExecutorBuilder(this.taskExecutionProperties()); // 创建 ThreadPoolTaskExecutor 对象 return builder.build(); } } private static TaskExecutorBuilder createTskExecutorBuilder(TaskExecutionProperties properties) { // Pool 属性 TaskExecutionProperties.Pool pool = properties.getPool(); TaskExecutorBuilder builder = new TaskExecutorBuilder(); builder = builder.queueCapacity(pool.getQueueCapacity()); builder = builder.corePoolSize(pool.getCoreSize()); builder = builder.maxPoolSize(pool.getMaxSize()); builder = builder.allowCoreThreadTimeOut(pool.isAllowCoreThreadTimeout()); builder = builder.keepAlive(pool.getKeepAlive()); // Shutdown 属性 TaskExecutionProperties.Shutdown shutdown = properties.getShutdown(); builder = builder.awaitTermination(shutdown.isAwaitTermination()); builder = builder.awaitTerminationPeriod(shutdown.getAwaitTerminationPeriod()); // 其它基本属性 builder = builder.threadNamePrefix(properties.getThreadNamePrefix()); // builder = builder.customizers(taskExecutorCustomizers.orderedStream()::iterator); // builder = builder.taskDecorator(taskDecorator.getIfUnique()); return builder; } } ================================================ FILE: lab-29/lab-29-async-two/src/main/java/cn/iocoder/springboot/lab29/asynctask/service/DemoService.java ================================================ package cn.iocoder.springboot.lab29.asynctask.service; import cn.iocoder.springboot.lab29.asynctask.config.AsyncConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; @Service public class DemoService { private Logger logger = LoggerFactory.getLogger(getClass()); @Async(AsyncConfig.EXECUTOR_ONE_BEAN_NAME) public Integer execute01() { logger.info("[execute01]"); return 1; } @Async(AsyncConfig.EXECUTOR_TWO_BEAN_NAME) public Integer execute02() { logger.info("[execute02]"); return 2; } } ================================================ FILE: lab-29/lab-29-async-two/src/main/resources/application.yaml ================================================ spring: task: # Spring 执行器配置,对应 TaskExecutionProperties 配置类。对于 Spring 异步任务,会使用该执行器。 execution-one: thread-name-prefix: task-one- # 线程池的线程名的前缀。默认为 task- ,建议根据自己应用来设置 pool: # 线程池相关 core-size: 8 # 核心线程数,线程池创建时候初始化的线程数。默认为 8 。 max-size: 20 # 最大线程数,线程池最大的线程数,只有在缓冲队列满了之后,才会申请超过核心线程数的线程。默认为 Integer.MAX_VALUE keep-alive: 60s # 允许线程的空闲时间,当超过了核心线程之外的线程,在空闲时间到达之后会被销毁。默认为 60 秒 queue-capacity: 200 # 缓冲队列大小,用来缓冲执行任务的队列的大小。默认为 Integer.MAX_VALUE 。 allow-core-thread-timeout: true # 是否允许核心线程超时,即开启线程池的动态增长和缩小。默认为 true 。 shutdown: await-termination: true # 应用关闭时,是否等待定时任务执行完成。默认为 false ,建议设置为 true await-termination-period: 60 # 等待任务完成的最大时长,单位为秒。默认为 0 ,根据自己应用来设置 # Spring 执行器配置,对应 TaskExecutionProperties 配置类。对于 Spring 异步任务,会使用该执行器。 execution-two: thread-name-prefix: task-two- # 线程池的线程名的前缀。默认为 task- ,建议根据自己应用来设置 pool: # 线程池相关 core-size: 8 # 核心线程数,线程池创建时候初始化的线程数。默认为 8 。 max-size: 20 # 最大线程数,线程池最大的线程数,只有在缓冲队列满了之后,才会申请超过核心线程数的线程。默认为 Integer.MAX_VALUE keep-alive: 60s # 允许线程的空闲时间,当超过了核心线程之外的线程,在空闲时间到达之后会被销毁。默认为 60 秒 queue-capacity: 200 # 缓冲队列大小,用来缓冲执行任务的队列的大小。默认为 Integer.MAX_VALUE 。 allow-core-thread-timeout: true # 是否允许核心线程超时,即开启线程池的动态增长和缩小。默认为 true 。 shutdown: await-termination: true # 应用关闭时,是否等待定时任务执行完成。默认为 false ,建议设置为 true await-termination-period: 60 # 等待任务完成的最大时长,单位为秒。默认为 0 ,根据自己应用来设置 ================================================ FILE: lab-29/lab-29-async-two/src/test/java/cn/iocoder/springboot/lab29/asynctask/package-info.java ================================================ package cn.iocoder.springboot.lab29.asynctask; ================================================ FILE: lab-29/lab-29-async-two/src/test/java/cn/iocoder/springboot/lab29/asynctask/service/DemoServiceTest.java ================================================ package cn.iocoder.springboot.lab29.asynctask.service; import cn.iocoder.springboot.lab29.asynctask.Application; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class DemoServiceTest { @Autowired private DemoService demoService; @Test public void testExecute() throws InterruptedException { demoService.execute01(); demoService.execute02(); // sleep 1 秒,保证异步调用的执行 Thread.sleep(1000); } } ================================================ FILE: lab-29/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-29 pom lab-29-async-demo lab-29-async-two ================================================ FILE: lab-29/《芋道 Spring Boot 异步任务入门》.md ================================================ ================================================ FILE: lab-30/lab-30-dubbo-annotations-demo/pom.xml ================================================ lab-30 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-30-dubbo-annotations-demo pom user-rpc-service-api-02 user-rpc-service-provider-02 user-rpc-service-consumer-02 ================================================ FILE: lab-30/lab-30-dubbo-annotations-demo/user-rpc-service-api-02/pom.xml ================================================ lab-30-dubbo-annotations-demo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 user-rpc-service-api-02 ================================================ FILE: lab-30/lab-30-dubbo-annotations-demo/user-rpc-service-api-02/src/main/java/cn/iocoder/springboot/lab30/rpc/api/UserRpcService.java ================================================ package cn.iocoder.springboot.lab30.rpc.api; import cn.iocoder.springboot.lab30.rpc.dto.UserDTO; /** * 用户服务 RPC Service 接口 */ public interface UserRpcService { /** * 根据指定用户编号,获得用户信息 * * @param id 用户编号 * @return 用户信息 */ UserDTO get(Integer id); } ================================================ FILE: lab-30/lab-30-dubbo-annotations-demo/user-rpc-service-api-02/src/main/java/cn/iocoder/springboot/lab30/rpc/dto/UserDTO.java ================================================ package cn.iocoder.springboot.lab30.rpc.dto; import java.io.Serializable; /** * 用户信息 DTO */ public class UserDTO implements Serializable { /** * 用户编号 */ private Integer id; /** * 昵称 */ private String name; /** * 性别 */ private Integer gender; public Integer getId() { return id; } public UserDTO setId(Integer id) { this.id = id; return this; } public String getName() { return name; } public UserDTO setName(String name) { this.name = name; return this; } public Integer getGender() { return gender; } public UserDTO setGender(Integer gender) { this.gender = gender; return this; } } ================================================ FILE: lab-30/lab-30-dubbo-annotations-demo/user-rpc-service-consumer-02/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 user-rpc-service-consumer-02 cn.iocoder.springboot.labs user-rpc-service-api-02 1.0-SNAPSHOT org.springframework.boot spring-boot-starter org.apache.dubbo dubbo 2.7.4.1 org.apache.dubbo dubbo-spring-boot-starter 2.7.4.1 org.apache.curator curator-framework 2.13.0 org.apache.curator curator-recipes 2.13.0 ================================================ FILE: lab-30/lab-30-dubbo-annotations-demo/user-rpc-service-consumer-02/src/main/java/cn/iocoder/springboot/lab30/rpc/ConsumerApplication.java ================================================ package cn.iocoder.springboot.lab30.rpc; import cn.iocoder.springboot.lab30.rpc.api.UserRpcService; import cn.iocoder.springboot.lab30.rpc.dto.UserDTO; import org.apache.dubbo.config.annotation.Reference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.stereotype.Component; @SpringBootApplication public class ConsumerApplication { public static void main(String[] args) { // 启动 Spring Boot 应用 ConfigurableApplicationContext context = SpringApplication.run(ConsumerApplication.class, args); } @Component public class UserRpcServiceTest implements CommandLineRunner { private final Logger logger = LoggerFactory.getLogger(getClass()); @Reference(version = "${dubbo.consumer.UserRpcService.version}") private UserRpcService userRpcService; @Override public void run(String... args) throws Exception { UserDTO user = userRpcService.get(1); logger.info("[run][发起一次 Dubbo RPC 请求,获得用户为({})", user); } } } ================================================ FILE: lab-30/lab-30-dubbo-annotations-demo/user-rpc-service-consumer-02/src/main/resources/application.yaml ================================================ # dubbo 配置项,对应 DubboConfigurationProperties 配置类 dubbo: # Dubbo 应用配置 application: name: user-service-consumer # 应用名 # Dubbo 注册中心配置 registry: address: zookeeper://127.0.0.1:2181 # 注册中心地址。个鞥多注册中心,可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。 # Dubbo 消费者配置 consumer: timeout: 1000 # 【重要】远程服务调用超时时间,单位:毫秒。默认为 1000 毫秒,胖友可以根据自己业务修改 UserRpcService: version: 1.0.0 ================================================ FILE: lab-30/lab-30-dubbo-annotations-demo/user-rpc-service-provider-02/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 user-rpc-service-provider-02 cn.iocoder.springboot.labs user-rpc-service-api-02 1.0-SNAPSHOT org.springframework.boot spring-boot-starter org.apache.dubbo dubbo 2.7.4.1 org.apache.dubbo dubbo-spring-boot-starter 2.7.4.1 org.apache.curator curator-framework 2.13.0 org.apache.curator curator-recipes 2.13.0 ================================================ FILE: lab-30/lab-30-dubbo-annotations-demo/user-rpc-service-provider-02/src/main/java/cn/iocoder/springboot/lab30/rpc/ProviderApplication.java ================================================ package cn.iocoder.springboot.lab30.rpc; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ProviderApplication { public static void main(String[] args) { // 启动 Spring Boot 应用 SpringApplication.run(ProviderApplication.class, args); } } ================================================ FILE: lab-30/lab-30-dubbo-annotations-demo/user-rpc-service-provider-02/src/main/java/cn/iocoder/springboot/lab30/rpc/service/UserRpcServiceImpl.java ================================================ package cn.iocoder.springboot.lab30.rpc.service; import cn.iocoder.springboot.lab30.rpc.api.UserRpcService; import cn.iocoder.springboot.lab30.rpc.dto.UserDTO; import org.apache.dubbo.config.annotation.Service; @Service(version = "${dubbo.provider.UserRpcService.version}") public class UserRpcServiceImpl implements UserRpcService { @Override public UserDTO get(Integer id) { return new UserDTO().setId(id) .setName("没有昵称:" + id) .setGender(id % 2 + 1); // 1 - 男;2 - 女 } } ================================================ FILE: lab-30/lab-30-dubbo-annotations-demo/user-rpc-service-provider-02/src/main/resources/application.yaml ================================================ # dubbo 配置项,对应 DubboConfigurationProperties 配置类 dubbo: # Dubbo 应用配置 application: name: user-service-provider # 应用名 # Dubbo 注册中心配置 registry: address: zookeeper://127.0.0.1:2181 # 注册中心地址。配置多注册中心,可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。 # Dubbo 元数据中心配置 metadata-report: address: zookeeper://127.0.0.1:2181 # 元数据中心地址。元数据中心的说明,可见 http://dubbo.apache.org/zh-cn/docs/user/references/metadata/introduction.html # Dubbo 服务提供者协议配置 protocol: port: -1 # 协议端口。使用 -1 表示随机端口。 name: dubbo # 使用 `dubbo://` 协议。更多协议,可见 http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html 文档 # Dubbo 服务提供者配置 provider: timeout: 1000 # 【重要】远程服务调用超时时间,单位:毫秒。默认为 1000 毫秒,胖友可以根据自己业务修改 UserRpcService: version: 1.0.0 # 配置扫描 Dubbo 自定义的 @Service 注解,暴露成 Dubbo 服务提供者 scan: base-packages: cn.iocoder.springboot.lab30.rpc.service ================================================ FILE: lab-30/lab-30-dubbo-annotations-nacos/pom.xml ================================================ lab-30 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-30-dubbo-annotations-nacos pom user-rpc-service-api-03 user-rpc-service-provider-03 user-rpc-service-consumer-03 ================================================ FILE: lab-30/lab-30-dubbo-annotations-nacos/user-rpc-service-api-03/pom.xml ================================================ lab-30-dubbo-annotations-demo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 user-rpc-service-api-03 ================================================ FILE: lab-30/lab-30-dubbo-annotations-nacos/user-rpc-service-api-03/src/main/java/cn/iocoder/springboot/lab30/rpc/api/UserRpcService.java ================================================ package cn.iocoder.springboot.lab30.rpc.api; import cn.iocoder.springboot.lab30.rpc.dto.UserDTO; /** * 用户服务 RPC Service 接口 */ public interface UserRpcService { /** * 根据指定用户编号,获得用户信息 * * @param id 用户编号 * @return 用户信息 */ UserDTO get(Integer id); } ================================================ FILE: lab-30/lab-30-dubbo-annotations-nacos/user-rpc-service-api-03/src/main/java/cn/iocoder/springboot/lab30/rpc/dto/UserDTO.java ================================================ package cn.iocoder.springboot.lab30.rpc.dto; import java.io.Serializable; /** * 用户信息 DTO */ public class UserDTO implements Serializable { /** * 用户编号 */ private Integer id; /** * 昵称 */ private String name; /** * 性别 */ private Integer gender; public Integer getId() { return id; } public UserDTO setId(Integer id) { this.id = id; return this; } public String getName() { return name; } public UserDTO setName(String name) { this.name = name; return this; } public Integer getGender() { return gender; } public UserDTO setGender(Integer gender) { this.gender = gender; return this; } } ================================================ FILE: lab-30/lab-30-dubbo-annotations-nacos/user-rpc-service-consumer-03/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 user-rpc-service-consumer-03 cn.iocoder.springboot.labs user-rpc-service-api-03 1.0-SNAPSHOT org.springframework.boot spring-boot-starter org.apache.dubbo dubbo 2.7.4.1 org.apache.dubbo dubbo-spring-boot-starter 2.7.4.1 com.alibaba.nacos nacos-client 1.2.1 org.apache.dubbo dubbo-registry-nacos 2.7.4.1 ================================================ FILE: lab-30/lab-30-dubbo-annotations-nacos/user-rpc-service-consumer-03/src/main/java/cn/iocoder/springboot/lab30/rpc/ConsumerApplication.java ================================================ package cn.iocoder.springboot.lab30.rpc; import cn.iocoder.springboot.lab30.rpc.api.UserRpcService; import cn.iocoder.springboot.lab30.rpc.dto.UserDTO; import org.apache.dubbo.config.annotation.Reference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.stereotype.Component; @SpringBootApplication public class ConsumerApplication { public static void main(String[] args) { // 启动 Spring Boot 应用 ConfigurableApplicationContext context = SpringApplication.run(ConsumerApplication.class, args); } @Component public class UserRpcServiceTest implements CommandLineRunner { private final Logger logger = LoggerFactory.getLogger(getClass()); @Reference(version = "${dubbo.consumer.UserRpcService.version}") private UserRpcService userRpcService; @Override public void run(String... args) throws Exception { UserDTO user = userRpcService.get(1); logger.info("[run][发起一次 Dubbo RPC 请求,获得用户为({})", user); } } } ================================================ FILE: lab-30/lab-30-dubbo-annotations-nacos/user-rpc-service-consumer-03/src/main/resources/application.yaml ================================================ # dubbo 配置项,对应 DubboConfigurationProperties 配置类 dubbo: # Dubbo 应用配置 application: name: user-service-consumer # 应用名 # Dubbo 注册中心配置 registry: address: nacos://127.0.0.1:8848 # 注册中心地址。个鞥多注册中心,可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。 # Dubbo 消费者配置 consumer: timeout: 1000 # 【重要】远程服务调用超时时间,单位:毫秒。默认为 1000 毫秒,胖友可以根据自己业务修改 UserRpcService: version: 1.0.0 ================================================ FILE: lab-30/lab-30-dubbo-annotations-nacos/user-rpc-service-provider-03/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 user-rpc-service-provider-03 cn.iocoder.springboot.labs user-rpc-service-api-03 1.0-SNAPSHOT org.springframework.boot spring-boot-starter org.apache.dubbo dubbo 2.7.4.1 org.apache.dubbo dubbo-spring-boot-starter 2.7.4.1 com.alibaba.nacos nacos-client 1.2.1 org.apache.dubbo dubbo-registry-nacos 2.7.4.1 ================================================ FILE: lab-30/lab-30-dubbo-annotations-nacos/user-rpc-service-provider-03/src/main/java/cn/iocoder/springboot/lab30/rpc/ProviderApplication.java ================================================ package cn.iocoder.springboot.lab30.rpc; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ProviderApplication { public static void main(String[] args) { // 启动 Spring Boot 应用 SpringApplication.run(ProviderApplication.class, args); } } ================================================ FILE: lab-30/lab-30-dubbo-annotations-nacos/user-rpc-service-provider-03/src/main/java/cn/iocoder/springboot/lab30/rpc/service/UserRpcServiceImpl.java ================================================ package cn.iocoder.springboot.lab30.rpc.service; import cn.iocoder.springboot.lab30.rpc.api.UserRpcService; import cn.iocoder.springboot.lab30.rpc.dto.UserDTO; import org.apache.dubbo.config.annotation.Service; @Service(version = "${dubbo.provider.UserRpcService.version}") public class UserRpcServiceImpl implements UserRpcService { @Override public UserDTO get(Integer id) { return new UserDTO().setId(id) .setName("没有昵称:" + id) .setGender(id % 2 + 1); // 1 - 男;2 - 女 } } ================================================ FILE: lab-30/lab-30-dubbo-annotations-nacos/user-rpc-service-provider-03/src/main/resources/application.yaml ================================================ # dubbo 配置项,对应 DubboConfigurationProperties 配置类 dubbo: # Dubbo 应用配置 application: name: user-service-provider # 应用名 # Dubbo 注册中心配 registry: address: nacos://127.0.0.1:8848 # 注册中心地址。个鞥多注册中心,可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。 # Dubbo 服务提供者协议配置 protocol: port: -1 # 协议端口。使用 -1 表示随机端口。 name: dubbo # 使用 `dubbo://` 协议。更多协议,可见 http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html 文档 # Dubbo 服务提供者配置 provider: timeout: 1000 # 【重要】远程服务调用超时时间,单位:毫秒。默认为 1000 毫秒,胖友可以根据自己业务修改 UserRpcService: version: 1.0.0 # 配置扫描 Dubbo 自定义的 @Service 注解,暴露成 Dubbo 服务提供者 scan: base-packages: cn.iocoder.springboot.lab30.rpc.service ================================================ FILE: lab-30/lab-30-dubbo-annotations-sentinel/pom.xml ================================================ lab-30 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-30-dubbo-annotations-sentinel pom user-rpc-service-api-04 user-rpc-service-provider-04 user-rpc-service-consumer-04 ================================================ FILE: lab-30/lab-30-dubbo-annotations-sentinel/user-rpc-service-api-04/pom.xml ================================================ lab-30-dubbo-annotations-demo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 user-rpc-service-api-04 ================================================ FILE: lab-30/lab-30-dubbo-annotations-sentinel/user-rpc-service-api-04/src/main/java/cn/iocoder/springboot/lab30/rpc/api/UserRpcService.java ================================================ package cn.iocoder.springboot.lab30.rpc.api; import cn.iocoder.springboot.lab30.rpc.dto.UserDTO; /** * 用户服务 RPC Service 接口 */ public interface UserRpcService { /** * 根据指定用户编号,获得用户信息 * * @param id 用户编号 * @return 用户信息 */ UserDTO get(Integer id); } ================================================ FILE: lab-30/lab-30-dubbo-annotations-sentinel/user-rpc-service-api-04/src/main/java/cn/iocoder/springboot/lab30/rpc/dto/UserDTO.java ================================================ package cn.iocoder.springboot.lab30.rpc.dto; import java.io.Serializable; /** * 用户信息 DTO */ public class UserDTO implements Serializable { /** * 用户编号 */ private Integer id; /** * 昵称 */ private String name; /** * 性别 */ private Integer gender; public Integer getId() { return id; } public UserDTO setId(Integer id) { this.id = id; return this; } public String getName() { return name; } public UserDTO setName(String name) { this.name = name; return this; } public Integer getGender() { return gender; } public UserDTO setGender(Integer gender) { this.gender = gender; return this; } } ================================================ FILE: lab-30/lab-30-dubbo-annotations-sentinel/user-rpc-service-consumer-04/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 user-rpc-service-consumer-04 cn.iocoder.springboot.labs user-rpc-service-api-04 1.0-SNAPSHOT org.springframework.boot spring-boot-starter-web org.apache.dubbo dubbo 2.7.4.1 org.apache.dubbo dubbo-spring-boot-starter 2.7.4.1 org.apache.curator curator-framework 2.13.0 org.apache.curator curator-recipes 2.13.0 com.alibaba.csp sentinel-core 1.7.1 com.alibaba.csp sentinel-transport-simple-http 1.7.1 com.alibaba.csp sentinel-apache-dubbo-adapter 1.7.1 ================================================ FILE: lab-30/lab-30-dubbo-annotations-sentinel/user-rpc-service-consumer-04/src/main/java/cn/iocoder/springboot/lab30/rpc/ConsumerApplication.java ================================================ package cn.iocoder.springboot.lab30.rpc; import cn.iocoder.springboot.lab30.rpc.api.UserRpcService; import cn.iocoder.springboot.lab30.rpc.dto.UserDTO; import org.apache.dubbo.config.annotation.Reference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.stereotype.Component; @SpringBootApplication public class ConsumerApplication { public static void main(String[] args) { // 启动 Spring Boot 应用 ConfigurableApplicationContext context = SpringApplication.run(ConsumerApplication.class, args); } @Component public class UserRpcServiceTest implements CommandLineRunner { private final Logger logger = LoggerFactory.getLogger(getClass()); @Reference(version = "${dubbo.consumer.UserRpcService.version}") private UserRpcService userRpcService; @Override public void run(String... args) throws Exception { UserDTO user = userRpcService.get(1); logger.info("[run][发起一次 Dubbo RPC 请求,获得用户为({})", user); } } } ================================================ FILE: lab-30/lab-30-dubbo-annotations-sentinel/user-rpc-service-consumer-04/src/main/java/cn/iocoder/springboot/lab30/rpc/controller/UserController.java ================================================ package cn.iocoder.springboot.lab30.rpc.controller; import cn.iocoder.springboot.lab30.rpc.api.UserRpcService; import cn.iocoder.springboot.lab30.rpc.dto.UserDTO; import org.apache.dubbo.config.annotation.Reference; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/user") public class UserController { @Reference(version = "${dubbo.consumer.UserRpcService.version}") private UserRpcService userRpcService; @GetMapping("/get") public UserDTO get(@RequestParam("id") Integer id) { return userRpcService.get(id); } } ================================================ FILE: lab-30/lab-30-dubbo-annotations-sentinel/user-rpc-service-consumer-04/src/main/resources/application.yaml ================================================ # dubbo 配置项,对应 DubboConfigurationProperties 配置类 dubbo: # Dubbo 应用配置 application: name: user-service-consumer # 应用名 # Dubbo 注册中心配置 registry: address: zookeeper://127.0.0.1:2181 # 注册中心地址。个鞥多注册中心,可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。 # Dubbo 消费者配置 consumer: timeout: 1000 # 【重要】远程服务调用超时时间,单位:毫秒。默认为 1000 毫秒,胖友可以根据自己业务修改 UserRpcService: version: 1.0.0 ================================================ FILE: lab-30/lab-30-dubbo-annotations-sentinel/user-rpc-service-consumer-04/src/main/resources/sentinel.properties ================================================ csp.sentinel.dashboard.server=127.0.0.1:7070 ================================================ FILE: lab-30/lab-30-dubbo-annotations-sentinel/user-rpc-service-provider-04/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 user-rpc-service-provider-04 cn.iocoder.springboot.labs user-rpc-service-api-04 1.0-SNAPSHOT org.springframework.boot spring-boot-starter org.apache.dubbo dubbo 2.7.4.1 org.apache.dubbo dubbo-spring-boot-starter 2.7.4.1 org.apache.curator curator-framework 2.13.0 org.apache.curator curator-recipes 2.13.0 com.alibaba.csp sentinel-core 1.7.1 com.alibaba.csp sentinel-transport-simple-http 1.7.1 com.alibaba.csp sentinel-apache-dubbo-adapter 1.7.1 ================================================ FILE: lab-30/lab-30-dubbo-annotations-sentinel/user-rpc-service-provider-04/src/main/java/cn/iocoder/springboot/lab30/rpc/ProviderApplication.java ================================================ package cn.iocoder.springboot.lab30.rpc; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ProviderApplication { public static void main(String[] args) { // 启动 Spring Boot 应用 SpringApplication.run(ProviderApplication.class, args); } } ================================================ FILE: lab-30/lab-30-dubbo-annotations-sentinel/user-rpc-service-provider-04/src/main/java/cn/iocoder/springboot/lab30/rpc/service/UserRpcServiceImpl.java ================================================ package cn.iocoder.springboot.lab30.rpc.service; import cn.iocoder.springboot.lab30.rpc.api.UserRpcService; import cn.iocoder.springboot.lab30.rpc.dto.UserDTO; import org.apache.dubbo.config.annotation.Service; @Service(version = "${dubbo.provider.UserRpcService.version}") public class UserRpcServiceImpl implements UserRpcService { @Override public UserDTO get(Integer id) { return new UserDTO().setId(id) .setName("没有昵称:" + id) .setGender(id % 2 + 1); // 1 - 男;2 - 女 } } ================================================ FILE: lab-30/lab-30-dubbo-annotations-sentinel/user-rpc-service-provider-04/src/main/resources/application.yaml ================================================ # dubbo 配置项,对应 DubboConfigurationProperties 配置类 dubbo: # Dubbo 应用配置 application: name: user-service-provider # 应用名 # Dubbo 注册中心配 registry: address: zookeeper://127.0.0.1:2181 # 注册中心地址。个鞥多注册中心,可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。 # Dubbo 服务提供者协议配置 protocol: port: -1 # 协议端口。使用 -1 表示随机端口。 name: dubbo # 使用 `dubbo://` 协议。更多协议,可见 http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html 文档 # Dubbo 服务提供者配置 provider: timeout: 1000 # 【重要】远程服务调用超时时间,单位:毫秒。默认为 1000 毫秒,胖友可以根据自己业务修改 UserRpcService: version: 1.0.0 # 配置扫描 Dubbo 自定义的 @Service 注解,暴露成 Dubbo 服务提供者 scan: base-packages: cn.iocoder.springboot.lab30.rpc.service ================================================ FILE: lab-30/lab-30-dubbo-annotations-sentinel/user-rpc-service-provider-04/src/main/resources/sentinel.properties ================================================ csp.sentinel.dashboard.server=127.0.0.1:7070 ================================================ FILE: lab-30/lab-30-dubbo-xml-demo/pom.xml ================================================ lab-30 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-30-dubbo-xml-demo pom user-rpc-service-api user-rpc-service-provider user-rpc-service-consumer ================================================ FILE: lab-30/lab-30-dubbo-xml-demo/user-rpc-service-api/pom.xml ================================================ lab-30-dubbo-xml-demo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 user-rpc-service-api javax.validation validation-api 2.0.1.Final org.hibernate.validator hibernate-validator 6.0.18.Final org.glassfish javax.el 3.0.1-b11 ================================================ FILE: lab-30/lab-30-dubbo-xml-demo/user-rpc-service-api/src/main/java/cn/iocoder/springboot/lab30/rpc/api/UserRpcService.java ================================================ package cn.iocoder.springboot.lab30.rpc.api; import cn.iocoder.springboot.lab30.rpc.dto.UserAddDTO; import cn.iocoder.springboot.lab30.rpc.dto.UserDTO; import javax.validation.ConstraintViolationException; import javax.validation.constraints.NotNull; /** * 用户服务 RPC Service 接口 */ public interface UserRpcService { /** * 根据指定用户编号,获得用户信息 * * @param id 用户编号 * @return 用户信息 */ UserDTO get(@NotNull(message = "用户编号不能为空") Integer id) throws ConstraintViolationException; /** * 添加新用户,返回新添加的用户编号 * * @param addDTO 添加的用户信息 * @return 用户编号 */ Integer add(UserAddDTO addDTO) throws ConstraintViolationException; } ================================================ FILE: lab-30/lab-30-dubbo-xml-demo/user-rpc-service-api/src/main/java/cn/iocoder/springboot/lab30/rpc/core/ServiceException.java ================================================ package cn.iocoder.springboot.lab30.rpc.core; /** * 服务异常 * * 参考 https://www.kancloud.cn/onebase/ob/484204 文章 * * 一共 10 位,分成四段 * * 第一段,1 位,类型 * 1 - 业务级别异常 * 2 - 系统级别异常 * 第二段,3 位,系统类型 * 001 - 用户系统 * 002 - 商品系统 * 003 - 订单系统 * 004 - 支付系统 * 005 - 优惠劵系统 * ... - ... * 第三段,3 位,模块 * 不限制规则。 * 一般建议,每个系统里面,可能有多个模块,可以再去做分段。以用户系统为例子: * 001 - OAuth2 模块 * 002 - User 模块 * 003 - MobileCode 模块 * 第四段,3 位,错误码 * 不限制规则。 * 一般建议,每个模块自增。 */ public final class ServiceException extends RuntimeException { /** * 错误码 */ private Integer code; public ServiceException() { // 创建默认构造方法,用于反序列化的场景。 } public ServiceException(ServiceExceptionEnum serviceExceptionEnum) { // 使用父类的 message 字段 super(serviceExceptionEnum.getMessage()); // 设置错误码 this.code = serviceExceptionEnum.getCode(); } public ServiceException(ServiceExceptionEnum serviceExceptionEnum, String message) { // 使用父类的 message 字段 super(message); // 设置错误码 this.code = serviceExceptionEnum.getCode(); } public Integer getCode() { return code; } } ================================================ FILE: lab-30/lab-30-dubbo-xml-demo/user-rpc-service-api/src/main/java/cn/iocoder/springboot/lab30/rpc/core/ServiceExceptionEnum.java ================================================ package cn.iocoder.springboot.lab30.rpc.core; /** * 业务异常枚举 */ public enum ServiceExceptionEnum { // ========== 系统级别 ========== SUCCESS(0, "成功"), SYS_ERROR(2001001000, "服务端发生异常"), MISSING_REQUEST_PARAM_ERROR(2001001001, "参数缺失"), INVALID_REQUEST_PARAM_ERROR(2001001002, "请求参数不合法"), // ========== 用户模块 ========== USER_NOT_FOUND(1001002000, "用户不存在"), USER_EXISTS(1001002001, "用户已存在"), // ========== 订单模块 ========== // ========== 商品模块 ========== ; /** * 错误码 */ private final int code; /** * 错误提示 */ private final String message; ServiceExceptionEnum(int code, String message) { this.code = code; this.message = message; } public int getCode() { return code; } public String getMessage() { return message; } } ================================================ FILE: lab-30/lab-30-dubbo-xml-demo/user-rpc-service-api/src/main/java/cn/iocoder/springboot/lab30/rpc/dto/UserAddDTO.java ================================================ package cn.iocoder.springboot.lab30.rpc.dto; import org.hibernate.validator.constraints.Length; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; import java.io.Serializable; /** * 用户添加 DTO */ public class UserAddDTO implements Serializable { /** * 昵称 */ @NotEmpty(message = "昵称不能为空") @Length(min = 5, max = 16, message = "账号长度为 5-16 位") private String name; /** * 性别 */ @NotNull(message = "性别不能为空") private Integer gender; public String getName() { return name; } public UserAddDTO setName(String name) { this.name = name; return this; } public Integer getGender() { return gender; } public UserAddDTO setGender(Integer gender) { this.gender = gender; return this; } } ================================================ FILE: lab-30/lab-30-dubbo-xml-demo/user-rpc-service-api/src/main/java/cn/iocoder/springboot/lab30/rpc/dto/UserDTO.java ================================================ package cn.iocoder.springboot.lab30.rpc.dto; import java.io.Serializable; /** * 用户信息 DTO */ public class UserDTO implements Serializable { /** * 用户编号 */ private Integer id; /** * 昵称 */ private String name; /** * 性别 */ private Integer gender; public Integer getId() { return id; } public UserDTO setId(Integer id) { this.id = id; return this; } public String getName() { return name; } public UserDTO setName(String name) { this.name = name; return this; } public Integer getGender() { return gender; } public UserDTO setGender(Integer gender) { this.gender = gender; return this; } } ================================================ FILE: lab-30/lab-30-dubbo-xml-demo/user-rpc-service-consumer/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 user-rpc-service-consumer cn.iocoder.springboot.labs user-rpc-service-api 1.0-SNAPSHOT org.springframework.boot spring-boot-starter org.apache.dubbo dubbo 2.7.4.1 org.apache.dubbo dubbo-spring-boot-starter 2.7.4.1 org.apache.curator curator-framework 2.13.0 org.apache.curator curator-recipes 2.13.0 ================================================ FILE: lab-30/lab-30-dubbo-xml-demo/user-rpc-service-consumer/src/main/java/cn/iocoder/springboot/lab30/rpc/ConsumerApplication.java ================================================ package cn.iocoder.springboot.lab30.rpc; import cn.iocoder.springboot.lab30.rpc.api.UserRpcService; import cn.iocoder.springboot.lab30.rpc.dto.UserAddDTO; import cn.iocoder.springboot.lab30.rpc.dto.UserDTO; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.ImportResource; import org.springframework.stereotype.Component; import javax.annotation.Resource; @SpringBootApplication @ImportResource("classpath:dubbo.xml") public class ConsumerApplication { public static void main(String[] args) { // 启动 Spring Boot 应用 ConfigurableApplicationContext context = SpringApplication.run(ConsumerApplication.class, args); } @Component public class UserRpcServiceTest implements CommandLineRunner { private final Logger logger = LoggerFactory.getLogger(getClass()); @Resource private UserRpcService userRpcService; @Override public void run(String... args) throws Exception { UserDTO user = userRpcService.get(1); logger.info("[run][发起一次 Dubbo RPC 请求,获得用户为({})]", user); } } @Component public class UserRpcServiceTest02 implements CommandLineRunner { private final Logger logger = LoggerFactory.getLogger(getClass()); @Resource private UserRpcService userRpcService; @Override public void run(String... args) throws Exception { // 获得用户 try { // 发起调用 UserDTO user = userRpcService.get(null); // 故意传入空的编号,为了校验编号不通过 logger.info("[run][发起一次 Dubbo RPC 请求,获得用户为({})]", user); } catch (Exception e) { logger.error("[run][获得用户发生异常,信息为:[{}]", e.getMessage()); } // 添加用户 try { // 创建 UserAddDTO UserAddDTO addDTO = new UserAddDTO(); addDTO.setName("yudaoyuanmayudaoyuanma"); // 故意把名字打的特别长,为了校验名字不通过 addDTO.setGender(null); // 不传递性别,为了校验性别不通过 // 发起调用 userRpcService.add(addDTO); logger.info("[run][发起一次 Dubbo RPC 请求,添加用户为({})]", addDTO); } catch (Exception e) { logger.error("[run][添加用户发生异常,信息为:[{}]", e.getMessage()); } } } @Component public class UserRpcServiceTest03 implements CommandLineRunner { private final Logger logger = LoggerFactory.getLogger(getClass()); @Resource private UserRpcService userRpcService; @Override public void run(String... args) { // 添加用户 try { // 创建 UserAddDTO UserAddDTO addDTO = new UserAddDTO(); addDTO.setName("yudaoyuanma"); // 设置为 yudaoyuanma ,触发 ServiceException 异常 addDTO.setGender(1); // 发起调用 userRpcService.add(addDTO); logger.info("[run][发起一次 Dubbo RPC 请求,添加用户为({})]", addDTO); } catch (Exception e) { logger.error("[run][添加用户发生异常({}),信息为:[{}]", e.getClass().getSimpleName(), e.getMessage()); } } } } ================================================ FILE: lab-30/lab-30-dubbo-xml-demo/user-rpc-service-consumer/src/main/resources/application.yaml ================================================ # dubbo 配置项,对应 DubboConfigurationProperties 配置类 dubbo: # Dubbo 应用配置 application: name: user-service-consumer # 应用名 # Dubbo 注册中心配置 registry: address: zookeeper://127.0.0.1:2181 # 注册中心地址。个鞥多注册中心,可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。 # Dubbo 消费者配置 consumer: timeout: 1000 # 【重要】远程服务调用超时时间,单位:毫秒。默认为 1000 毫秒,胖友可以根据自己业务修改 UserRpcService: version: 1.0.0 ================================================ FILE: lab-30/lab-30-dubbo-xml-demo/user-rpc-service-consumer/src/main/resources/dubbo.xml ================================================ ================================================ FILE: lab-30/lab-30-dubbo-xml-demo/user-rpc-service-provider/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 user-rpc-service-provider cn.iocoder.springboot.labs user-rpc-service-api 1.0-SNAPSHOT org.springframework.boot spring-boot-starter org.apache.dubbo dubbo 2.7.4.1 org.apache.dubbo dubbo-spring-boot-starter 2.7.4.1 org.apache.curator curator-framework 2.13.0 org.apache.curator curator-recipes 2.13.0 ================================================ FILE: lab-30/lab-30-dubbo-xml-demo/user-rpc-service-provider/src/main/java/cn/iocoder/springboot/lab30/rpc/ProviderApplication.java ================================================ package cn.iocoder.springboot.lab30.rpc; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.ImportResource; @SpringBootApplication @ImportResource("classpath:dubbo.xml") public class ProviderApplication { public static void main(String[] args) { // 启动 Spring Boot 应用 SpringApplication.run(ProviderApplication.class, args); } } ================================================ FILE: lab-30/lab-30-dubbo-xml-demo/user-rpc-service-provider/src/main/java/cn/iocoder/springboot/lab30/rpc/filter/DubboExceptionFilter.java ================================================ package cn.iocoder.springboot.lab30.rpc.filter; import cn.iocoder.springboot.lab30.rpc.core.ServiceException; import cn.iocoder.springboot.lab30.rpc.core.ServiceExceptionEnum; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.ReflectUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.*; import org.apache.dubbo.rpc.service.GenericService; import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolationException; import java.lang.reflect.Method; @Activate(group = CommonConstants.PROVIDER) public class DubboExceptionFilter extends ListenableFilter { public DubboExceptionFilter() { super.listener = new ExceptionListenerX(); } @Override public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { return invoker.invoke(invocation); } static class ExceptionListenerX extends ExceptionListener { @Override public void onResponse(Result appResponse, Invoker invoker, Invocation invocation) { // 发生异常,并且非泛化调用 if (appResponse.hasException() && GenericService.class != invoker.getInterface()) { Throwable exception = appResponse.getException(); // 如果是 ServiceException 异常,直接返回 if (exception instanceof ServiceException) { return; } // 如果是参数校验的 ConstraintViolationException 异常,则封装返回 if (exception instanceof ConstraintViolationException) { appResponse.setException(this.handleConstraintViolationException((ConstraintViolationException) exception)); return; } } // 其它情况,继续使用父类处理 super.onResponse(appResponse, invoker, invocation); } private ServiceException handleConstraintViolationException(ConstraintViolationException ex) { // 拼接错误 StringBuilder detailMessage = new StringBuilder(); for (ConstraintViolation constraintViolation : ex.getConstraintViolations()) { // 使用 ; 分隔多个错误 if (detailMessage.length() > 0) { detailMessage.append(";"); } // 拼接内容到其中 detailMessage.append(constraintViolation.getMessage()); } // 返回异常 return new ServiceException(ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR, detailMessage.toString()); } } static class ExceptionListener implements Listener { private Logger logger = LoggerFactory.getLogger(ExceptionListener.class); @Override public void onResponse(Result appResponse, Invoker invoker, Invocation invocation) { if (appResponse.hasException() && GenericService.class != invoker.getInterface()) { try { Throwable exception = appResponse.getException(); // directly throw if it's checked exception if (!(exception instanceof RuntimeException) && (exception instanceof Exception)) { return; } // directly throw if the exception appears in the signature try { Method method = invoker.getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes()); Class[] exceptionClassses = method.getExceptionTypes(); for (Class exceptionClass : exceptionClassses) { if (exception.getClass().equals(exceptionClass)) { return; } } } catch (NoSuchMethodException e) { return; } // for the exception not found in method's signature, print ERROR message in server's log. 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); // directly throw if exception class and interface class are in the same jar file. String serviceFile = ReflectUtils.getCodeBase(invoker.getInterface()); String exceptionFile = ReflectUtils.getCodeBase(exception.getClass()); if (serviceFile == null || exceptionFile == null || serviceFile.equals(exceptionFile)) { return; } // directly throw if it's JDK exception String className = exception.getClass().getName(); if (className.startsWith("java.") || className.startsWith("javax.")) { return; } // directly throw if it's dubbo exception if (exception instanceof RpcException) { return; } // otherwise, wrap with RuntimeException and throw back to the client appResponse.setException(new RuntimeException(StringUtils.toString(exception))); return; } catch (Throwable e) { 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); return; } } } @Override public void onError(Throwable e, Invoker invoker, Invocation invocation) { 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); } // For test purpose public void setLogger(Logger logger) { this.logger = logger; } } } ================================================ FILE: lab-30/lab-30-dubbo-xml-demo/user-rpc-service-provider/src/main/java/cn/iocoder/springboot/lab30/rpc/service/UserRpcServiceImpl.java ================================================ package cn.iocoder.springboot.lab30.rpc.service; import cn.iocoder.springboot.lab30.rpc.api.UserRpcService; import cn.iocoder.springboot.lab30.rpc.core.ServiceException; import cn.iocoder.springboot.lab30.rpc.core.ServiceExceptionEnum; import cn.iocoder.springboot.lab30.rpc.dto.UserAddDTO; import cn.iocoder.springboot.lab30.rpc.dto.UserDTO; import org.springframework.stereotype.Service; @Service public class UserRpcServiceImpl implements UserRpcService { @Override public UserDTO get(Integer id) { return new UserDTO().setId(id) .setName("没有昵称:" + id) .setGender(id % 2 + 1); // 1 - 男;2 - 女 } @Override public Integer add(UserAddDTO addDTO) { // 这里,模拟用户已经存在的情况 if ("yudaoyuanma".equals(addDTO.getName())) { throw new ServiceException(ServiceExceptionEnum.USER_EXISTS); } return (int) (System.currentTimeMillis() / 1000); // 嘿嘿,随便返回一个 id } } ================================================ FILE: lab-30/lab-30-dubbo-xml-demo/user-rpc-service-provider/src/main/resources/META-INF/dubbo/org.apache.dubbo.rpc.Filter ================================================ dubboExceptionFilter=cn.iocoder.springboot.lab30.rpc.filter.DubboExceptionFilter ================================================ FILE: lab-30/lab-30-dubbo-xml-demo/user-rpc-service-provider/src/main/resources/application.yaml ================================================ # dubbo 配置项,对应 DubboConfigurationProperties 配置类 dubbo: # Dubbo 应用配置 application: name: user-service-provider # 应用名 # Dubbo 注册中心配 registry: address: zookeeper://127.0.0.1:2181 # 注册中心地址。个鞥多注册中心,可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。 # Dubbo 服务提供者协议配置 protocol: port: -1 # 协议端口。使用 -1 表示随机端口。 name: dubbo # 使用 `dubbo://` 协议。更多协议,可见 http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html 文档 # Dubbo 服务提供者配置 provider: timeout: 1000 # 【重要】远程服务调用超时时间,单位:毫秒。默认为 1000 毫秒,胖友可以根据自己业务修改 filter: -exception # 去掉 ExceptionFilter UserRpcService: version: 1.0.0 ================================================ FILE: lab-30/lab-30-dubbo-xml-demo/user-rpc-service-provider/src/main/resources/dubbo.xml ================================================ ================================================ FILE: lab-30/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-30 pom lab-30-dubbo-xml-demo lab-30-dubbo-annotations-demo lab-30-dubbo-annotations-nacos lab-30-dubbo-annotations-sentinel ================================================ FILE: lab-30/《芋道 Dubbo Admin 快速入门》.md ================================================ ================================================ FILE: lab-30/《芋道 Dubbo Swagger 快速入门》.md ================================================ ================================================ FILE: lab-30/《芋道 Spring Boot Dubbo 入门》.md ================================================ ================================================ FILE: lab-31/lab-31-rocketmq-demo/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-31-rocketmq-demo org.apache.rocketmq rocketmq-spring-boot-starter 2.0.4 org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/Application.java ================================================ package cn.iocoder.springboot.lab31.rocketmqdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/consumer/Demo01AConsumer.java ================================================ package cn.iocoder.springboot.lab31.rocketmqdemo.consumer; import cn.iocoder.springboot.lab31.rocketmqdemo.message.Demo01Message; import org.apache.rocketmq.common.message.MessageExt; import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; import org.apache.rocketmq.spring.core.RocketMQListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component @RocketMQMessageListener( topic = Demo01Message.TOPIC, consumerGroup = "demo01-A-consumer-group-" + Demo01Message.TOPIC ) public class Demo01AConsumer implements RocketMQListener { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public void onMessage(MessageExt message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/consumer/Demo01Consumer.java ================================================ package cn.iocoder.springboot.lab31.rocketmqdemo.consumer; import cn.iocoder.springboot.lab31.rocketmqdemo.message.Demo01Message; import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; import org.apache.rocketmq.spring.core.RocketMQListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component @RocketMQMessageListener( topic = Demo01Message.TOPIC, consumerGroup = "demo01-consumer-group-" + Demo01Message.TOPIC ) public class Demo01Consumer implements RocketMQListener { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public void onMessage(Demo01Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/consumer/Demo02Consumer.java ================================================ package cn.iocoder.springboot.lab31.rocketmqdemo.consumer; import cn.iocoder.springboot.lab31.rocketmqdemo.message.Demo02Message; import org.apache.rocketmq.spring.core.RocketMQListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component //@RocketMQMessageListener( // topic = Demo02Message.TOPIC, // consumerGroup = "demo02-consumer-group-" + Demo02Message.TOPIC //) public class Demo02Consumer implements RocketMQListener { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public void onMessage(Demo02Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/consumer/Demo03Consumer.java ================================================ package cn.iocoder.springboot.lab31.rocketmqdemo.consumer; import cn.iocoder.springboot.lab31.rocketmqdemo.message.Demo03Message; import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; import org.apache.rocketmq.spring.core.RocketMQListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component @RocketMQMessageListener( topic = Demo03Message.TOPIC, consumerGroup = "demo03-consumer-group-" + Demo03Message.TOPIC ) public class Demo03Consumer implements RocketMQListener { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public void onMessage(Demo03Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/consumer/Demo04Consumer.java ================================================ package cn.iocoder.springboot.lab31.rocketmqdemo.consumer; import cn.iocoder.springboot.lab31.rocketmqdemo.message.Demo04Message; import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; import org.apache.rocketmq.spring.core.RocketMQListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component @RocketMQMessageListener( topic = Demo04Message.TOPIC, consumerGroup = "demo04-consumer-group-" + Demo04Message.TOPIC ) public class Demo04Consumer implements RocketMQListener { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public void onMessage(Demo04Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); // 注意,此处抛出一个 RuntimeException 异常,模拟消费失败 throw new RuntimeException("我就是故意抛出一个异常"); } } ================================================ FILE: lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/consumer/Demo05Consumer.java ================================================ package cn.iocoder.springboot.lab31.rocketmqdemo.consumer; import cn.iocoder.springboot.lab31.rocketmqdemo.message.Demo05Message; import org.apache.rocketmq.spring.annotation.MessageModel; import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; import org.apache.rocketmq.spring.core.RocketMQListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component @RocketMQMessageListener( topic = Demo05Message.TOPIC, consumerGroup = "demo05-consumer-group-" + Demo05Message.TOPIC, messageModel = MessageModel.BROADCASTING // 设置为广播消费 ) public class Demo05Consumer implements RocketMQListener { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public void onMessage(Demo05Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/consumer/Demo06Consumer.java ================================================ package cn.iocoder.springboot.lab31.rocketmqdemo.consumer; import cn.iocoder.springboot.lab31.rocketmqdemo.message.Demo06Message; import org.apache.rocketmq.spring.annotation.ConsumeMode; import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; import org.apache.rocketmq.spring.core.RocketMQListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component @RocketMQMessageListener( topic = Demo06Message.TOPIC, consumerGroup = "demo06-consumer-group-" + Demo06Message.TOPIC, consumeMode = ConsumeMode.ORDERLY // 设置为顺序消费 ) public class Demo06Consumer implements RocketMQListener { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public void onMessage(Demo06Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); // sleep 2 秒,用于查看顺序消费的效果 try { Thread.sleep(2 * 1000L); } catch (InterruptedException ignore) { } } } ================================================ FILE: lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/consumer/Demo07Consumer.java ================================================ package cn.iocoder.springboot.lab31.rocketmqdemo.consumer; import cn.iocoder.springboot.lab31.rocketmqdemo.message.Demo07Message; import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; import org.apache.rocketmq.spring.core.RocketMQListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component @RocketMQMessageListener( topic = Demo07Message.TOPIC, consumerGroup = "demo07-consumer-group-" + Demo07Message.TOPIC ) public class Demo07Consumer implements RocketMQListener { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public void onMessage(Demo07Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/core/ExtRocketMQTemplate.java ================================================ package cn.iocoder.springboot.lab31.rocketmqdemo.core; import org.apache.rocketmq.spring.annotation.ExtRocketMQTemplateConfiguration; import org.apache.rocketmq.spring.core.RocketMQTemplate; @ExtRocketMQTemplateConfiguration(nameServer = "${demo.rocketmq.extNameServer:demo.rocketmq.name-server}") public class ExtRocketMQTemplate extends RocketMQTemplate { } ================================================ FILE: lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/message/Demo01Message.java ================================================ package cn.iocoder.springboot.lab31.rocketmqdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { public static final String TOPIC = "DEMO_01"; /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/message/Demo02Message.java ================================================ package cn.iocoder.springboot.lab31.rocketmqdemo.message; /** * 示例 02 的 Message 消息 */ public class Demo02Message { public static final String TOPIC = "DEMO_02"; /** * 编号 */ private Integer id; public Demo02Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo02Message{" + "id=" + id + '}'; } } ================================================ FILE: lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/message/Demo03Message.java ================================================ package cn.iocoder.springboot.lab31.rocketmqdemo.message; /** * 示例 03 的 Message 消息 */ public class Demo03Message { public static final String TOPIC = "DEMO_03"; /** * 编号 */ private Integer id; public Demo03Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo03Message{" + "id=" + id + '}'; } } ================================================ FILE: lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/message/Demo04Message.java ================================================ package cn.iocoder.springboot.lab31.rocketmqdemo.message; /** * 示例 04 的 Message 消息 */ public class Demo04Message { public static final String TOPIC = "DEMO_04"; /** * 编号 */ private Integer id; public Demo04Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo04Message{" + "id=" + id + '}'; } } ================================================ FILE: lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/message/Demo05Message.java ================================================ package cn.iocoder.springboot.lab31.rocketmqdemo.message; /** * 示例 05 的 Message 消息 */ public class Demo05Message { public static final String TOPIC = "DEMO_05"; /** * 编号 */ private Integer id; public Demo05Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo05Message{" + "id=" + id + '}'; } } ================================================ FILE: lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/message/Demo06Message.java ================================================ package cn.iocoder.springboot.lab31.rocketmqdemo.message; /** * 示例 06 的 Message 消息 */ public class Demo06Message { public static final String TOPIC = "DEMO_06"; /** * 编号 */ private Integer id; public Demo06Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo06Message{" + "id=" + id + '}'; } } ================================================ FILE: lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/message/Demo07Message.java ================================================ package cn.iocoder.springboot.lab31.rocketmqdemo.message; /** * 示例 07 的 Message 消息 */ public class Demo07Message { public static final String TOPIC = "DEMO_07"; /** * 编号 */ private Integer id; public Demo07Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo07Message{" + "id=" + id + '}'; } } ================================================ FILE: lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/producer/Demo01Producer.java ================================================ package cn.iocoder.springboot.lab31.rocketmqdemo.producer; import cn.iocoder.springboot.lab31.rocketmqdemo.message.Demo01Message; import org.apache.rocketmq.client.producer.SendCallback; import org.apache.rocketmq.client.producer.SendResult; import org.apache.rocketmq.spring.core.RocketMQTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class Demo01Producer { @Autowired private RocketMQTemplate rocketMQTemplate; public SendResult syncSend(Integer id) { // 创建 Demo01Message 消息 Demo01Message message = new Demo01Message(); message.setId(id); // 同步发送消息 return rocketMQTemplate.syncSend(Demo01Message.TOPIC, message); } public void asyncSend(Integer id, SendCallback callback) { // 创建 Demo01Message 消息 Demo01Message message = new Demo01Message(); message.setId(id); // 异步发送消息 rocketMQTemplate.asyncSend(Demo01Message.TOPIC, message, callback); } public void onewaySend(Integer id) { // 创建 Demo01Message 消息 Demo01Message message = new Demo01Message(); message.setId(id); // oneway 发送消息 rocketMQTemplate.sendOneWay(Demo01Message.TOPIC, message); } } ================================================ FILE: lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/producer/Demo02Producer.java ================================================ package cn.iocoder.springboot.lab31.rocketmqdemo.producer; import cn.iocoder.springboot.lab31.rocketmqdemo.message.Demo02Message; import org.apache.rocketmq.client.producer.SendResult; import org.apache.rocketmq.spring.core.RocketMQTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.Message; import org.springframework.messaging.support.MessageBuilder; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.Collection; import java.util.List; @Component public class Demo02Producer { @Autowired private RocketMQTemplate rocketMQTemplate; public SendResult sendBatch(Collection ids) { // 创建多条 Demo02Message 消息 List messages = new ArrayList<>(ids.size()); for (Integer id : ids) { // 创建 Demo02Message 消息 Demo02Message message = new Demo02Message().setId(id); // 构建 Spring Messaging 定义的 Message 消息 messages.add(MessageBuilder.withPayload(message).build()); } // 同步批量发送消息 return rocketMQTemplate.syncSend(Demo02Message.TOPIC, messages, 30 * 1000L); } } ================================================ FILE: lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/producer/Demo03Producer.java ================================================ package cn.iocoder.springboot.lab31.rocketmqdemo.producer; import cn.iocoder.springboot.lab31.rocketmqdemo.message.Demo03Message; import org.apache.rocketmq.client.producer.SendCallback; import org.apache.rocketmq.client.producer.SendResult; import org.apache.rocketmq.spring.core.RocketMQTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.Message; import org.springframework.messaging.support.MessageBuilder; import org.springframework.stereotype.Component; @Component public class Demo03Producer { @Autowired private RocketMQTemplate rocketMQTemplate; public SendResult syncSendDelay(Integer id, int delayLevel) { // 创建 Demo03Message 消息 Message message = MessageBuilder.withPayload(new Demo03Message().setId(id)) .build(); // 同步发送消息 return rocketMQTemplate.syncSend(Demo03Message.TOPIC, message, 30 * 1000, delayLevel); } public void asyncSendDelay(Integer id, int delayLevel, SendCallback callback) { // 创建 Demo03Message 消息 Message message = MessageBuilder.withPayload(new Demo03Message().setId(id)) .build(); // 同步发送消息 rocketMQTemplate.asyncSend(Demo03Message.TOPIC, message, callback, 30 * 1000, delayLevel); } } ================================================ FILE: lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/producer/Demo04Producer.java ================================================ package cn.iocoder.springboot.lab31.rocketmqdemo.producer; import cn.iocoder.springboot.lab31.rocketmqdemo.message.Demo04Message; import org.apache.rocketmq.client.producer.SendResult; import org.apache.rocketmq.spring.core.RocketMQTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class Demo04Producer { @Autowired private RocketMQTemplate rocketMQTemplate; public SendResult syncSend(Integer id) { // 创建 Demo04Message 消息 Demo04Message message = new Demo04Message(); message.setId(id); // 同步发送消息 return rocketMQTemplate.syncSend(Demo04Message.TOPIC, message); } } ================================================ FILE: lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/producer/Demo05Producer.java ================================================ package cn.iocoder.springboot.lab31.rocketmqdemo.producer; import cn.iocoder.springboot.lab31.rocketmqdemo.message.Demo05Message; import org.apache.rocketmq.client.producer.SendResult; import org.apache.rocketmq.spring.core.RocketMQTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class Demo05Producer { @Autowired private RocketMQTemplate rocketMQTemplate; public SendResult syncSend(Integer id) { // 创建 Demo05Message 消息 Demo05Message message = new Demo05Message(); message.setId(id); // 同步发送消息 return rocketMQTemplate.syncSend(Demo05Message.TOPIC, message); } } ================================================ FILE: lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/producer/Demo06Producer.java ================================================ package cn.iocoder.springboot.lab31.rocketmqdemo.producer; import cn.iocoder.springboot.lab31.rocketmqdemo.message.Demo06Message; import org.apache.rocketmq.client.producer.SendCallback; import org.apache.rocketmq.client.producer.SendResult; import org.apache.rocketmq.spring.core.RocketMQTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class Demo06Producer { @Autowired private RocketMQTemplate rocketMQTemplate; public SendResult syncSendOrderly(Integer id) { // 创建 Demo06Message 消息 Demo06Message message = new Demo06Message(); message.setId(id); // 同步发送消息 return rocketMQTemplate.syncSendOrderly(Demo06Message.TOPIC, message, String.valueOf(id)); } public void asyncSendOrderly(Integer id, SendCallback callback) { // 创建 Demo06Message 消息 Demo06Message message = new Demo06Message(); message.setId(id); // 异步发送消息 rocketMQTemplate.asyncSendOrderly(Demo06Message.TOPIC, message, String.valueOf(id), callback); } public void onewaySendOrderly(Integer id) { // 创建 Demo06Message 消息 Demo06Message message = new Demo06Message(); message.setId(id); // 异步发送消息 rocketMQTemplate.sendOneWayOrderly(Demo06Message.TOPIC, message, String.valueOf(id)); } } ================================================ FILE: lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/producer/Demo07Producer.java ================================================ package cn.iocoder.springboot.lab31.rocketmqdemo.producer; import cn.iocoder.springboot.lab31.rocketmqdemo.message.Demo07Message; import org.apache.rocketmq.client.producer.TransactionSendResult; import org.apache.rocketmq.spring.annotation.RocketMQTransactionListener; import org.apache.rocketmq.spring.core.RocketMQLocalTransactionListener; import org.apache.rocketmq.spring.core.RocketMQLocalTransactionState; import org.apache.rocketmq.spring.core.RocketMQTemplate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.Message; import org.springframework.messaging.support.MessageBuilder; import org.springframework.stereotype.Component; @Component public class Demo07Producer { private static final String TX_PRODUCER_GROUP = "demo07-producer-group"; @Autowired private RocketMQTemplate rocketMQTemplate; public TransactionSendResult sendMessageInTransaction(Integer id) { // 创建 Demo07Message 消息 Message message = MessageBuilder.withPayload(new Demo07Message().setId(id)) .build(); // 发送事务消息 return rocketMQTemplate.sendMessageInTransaction(TX_PRODUCER_GROUP, Demo07Message.TOPIC, message, id); } @RocketMQTransactionListener(txProducerGroup = TX_PRODUCER_GROUP) public class TransactionListenerImpl implements RocketMQLocalTransactionListener { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) { // ... local transaction process, return rollback, commit or unknown logger.info("[executeLocalTransaction][执行本地事务,消息:{} arg:{}]", msg, arg); return RocketMQLocalTransactionState.UNKNOWN; } @Override public RocketMQLocalTransactionState checkLocalTransaction(Message msg) { // ... check transaction status and return rollback, commit or unknown logger.info("[checkLocalTransaction][回查消息:{}]", msg); return RocketMQLocalTransactionState.COMMIT; } } } ================================================ FILE: lab-31/lab-31-rocketmq-demo/src/main/resources/application.yaml ================================================ # rocketmq 配置项,对应 RocketMQProperties 配置类 rocketmq: name-server: 127.0.0.1:9876 # RocketMQ Namesrv # Producer 配置项 producer: group: demo-producer-group # 生产者分组 send-message-timeout: 3000 # 发送消息超时时间,单位:毫秒。默认为 3000 。 compress-message-body-threshold: 4096 # 消息压缩阀值,当消息体的大小超过该阀值后,进行消息压缩。默认为 4 * 1024B max-message-size: 4194304 # 消息体的最大允许大小。。默认为 4 * 1024 * 1024B retry-times-when-send-failed: 2 # 同步发送消息时,失败重试次数。默认为 2 次。 retry-times-when-send-async-failed: 2 # 异步发送消息时,失败重试次数。默认为 2 次。 retry-next-server: false # 发送消息给 Broker 时,如果发送失败,是否重试另外一台 Broker 。默认为 false access-key: # Access Key ,可阅读 https://github.com/apache/rocketmq/blob/master/docs/cn/acl/user_guide.md 文档 secret-key: # Secret Key enable-msg-trace: true # 是否开启消息轨迹功能。默认为 true 开启。可阅读 https://github.com/apache/rocketmq/blob/master/docs/cn/msg_trace/user_guide.md 文档 customized-trace-topic: RMQ_SYS_TRACE_TOPIC # 自定义消息轨迹的 Topic 。默认为 RMQ_SYS_TRACE_TOPIC 。 # Consumer 配置项 consumer: listeners: # 配置某个消费分组,是否监听指定 Topic 。结构为 Map<消费者分组, > 。默认情况下,不配置表示监听。 test-consumer-group: topic1: false # 关闭 test-consumer-group 对 topic1 的监听消费 ================================================ FILE: lab-31/lab-31-rocketmq-demo/src/test/java/cn/iocoder/springboot/lab31/rocketmqdemo/package-info.java ================================================ package cn.iocoder.springboot.lab31.rocketmqdemo; ================================================ FILE: lab-31/lab-31-rocketmq-demo/src/test/java/cn/iocoder/springboot/lab31/rocketmqdemo/producer/Demo01ProducerTest.java ================================================ package cn.iocoder.springboot.lab31.rocketmqdemo.producer; import cn.iocoder.springboot.lab31.rocketmqdemo.Application; import org.apache.rocketmq.client.producer.SendCallback; import org.apache.rocketmq.client.producer.SendResult; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.concurrent.CountDownLatch; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class Demo01ProducerTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private Demo01Producer producer; @Test public void testSyncSend() throws InterruptedException { int id = (int) (System.currentTimeMillis() / 1000); SendResult result = producer.syncSend(id); logger.info("[testSyncSend][发送编号:[{}] 发送结果:[{}]]", id, result); // 阻塞等待,保证消费 new CountDownLatch(1).await(); } @Test public void testASyncSend() throws InterruptedException { int id = (int) (System.currentTimeMillis() / 1000); producer.asyncSend(id, new SendCallback() { @Override public void onSuccess(SendResult result) { logger.info("[testASyncSend][发送编号:[{}] 发送成功,结果为:[{}]]", id, result); } @Override public void onException(Throwable e) { logger.info("[testASyncSend][发送编号:[{}] 发送异常]]", id, e); } }); // 阻塞等待,保证消费 new CountDownLatch(1).await(); } @Test public void testOnewaySend() throws InterruptedException { int id = (int) (System.currentTimeMillis() / 1000); producer.onewaySend(id); logger.info("[testOnewaySend][发送编号:[{}] 发送完成]", id); // 阻塞等待,保证消费 new CountDownLatch(1).await(); } } ================================================ FILE: lab-31/lab-31-rocketmq-demo/src/test/java/cn/iocoder/springboot/lab31/rocketmqdemo/producer/Demo02ProducerTest.java ================================================ package cn.iocoder.springboot.lab31.rocketmqdemo.producer; import cn.iocoder.springboot.lab31.rocketmqdemo.Application; import org.apache.rocketmq.client.producer.SendResult; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.Arrays; import java.util.List; import java.util.concurrent.CountDownLatch; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class Demo02ProducerTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private Demo02Producer producer; @Test public void testSendBatch() throws InterruptedException { List ids = Arrays.asList(1, 2, 3); SendResult result = producer.sendBatch(ids); logger.info("[testSendBatch][发送编号:[{}] 发送结果:[{}]]", ids, result); // 阻塞等待,保证消费 new CountDownLatch(1).await(); } } ================================================ FILE: lab-31/lab-31-rocketmq-demo/src/test/java/cn/iocoder/springboot/lab31/rocketmqdemo/producer/Demo03ProducerTest.java ================================================ package cn.iocoder.springboot.lab31.rocketmqdemo.producer; import cn.iocoder.springboot.lab31.rocketmqdemo.Application; import org.apache.rocketmq.client.producer.SendResult; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.concurrent.CountDownLatch; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class Demo03ProducerTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private Demo03Producer producer; @Test public void testSyncSendDelay() throws InterruptedException { int id = (int) (System.currentTimeMillis() / 1000); SendResult result = producer.syncSendDelay(id, 3); // 延迟级别 3 ,即 10 秒后消费 logger.info("[testSyncSendDelay][发送编号:[{}] 发送结果:[{}]]", id, result); // 阻塞等待,保证消费 new CountDownLatch(1).await(); } } ================================================ FILE: lab-31/lab-31-rocketmq-demo/src/test/java/cn/iocoder/springboot/lab31/rocketmqdemo/producer/Demo04ProducerTest.java ================================================ package cn.iocoder.springboot.lab31.rocketmqdemo.producer; import cn.iocoder.springboot.lab31.rocketmqdemo.Application; import org.apache.rocketmq.client.producer.SendResult; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.concurrent.CountDownLatch; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class Demo04ProducerTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private Demo04Producer producer; @Test public void testSyncSend() throws InterruptedException { int id = (int) (System.currentTimeMillis() / 1000); SendResult result = producer.syncSend(id); logger.info("[testSyncSend][发送编号:[{}] 发送结果:[{}]]", id, result); // 阻塞等待,保证消费 new CountDownLatch(1).await(); } } ================================================ FILE: lab-31/lab-31-rocketmq-demo/src/test/java/cn/iocoder/springboot/lab31/rocketmqdemo/producer/Demo05ProducerTest.java ================================================ package cn.iocoder.springboot.lab31.rocketmqdemo.producer; import cn.iocoder.springboot.lab31.rocketmqdemo.Application; import org.apache.rocketmq.client.producer.SendResult; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.concurrent.CountDownLatch; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class Demo05ProducerTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private Demo05Producer producer; @Test public void test() throws InterruptedException { // 阻塞等待,保证消费 new CountDownLatch(1).await(); } @Test public void testSyncSend() throws InterruptedException { int id = (int) (System.currentTimeMillis() / 1000); SendResult result = producer.syncSend(id); logger.info("[testSyncSend][发送编号:[{}] 发送结果:[{}]]", id, result); // 阻塞等待,保证消费 new CountDownLatch(1).await(); } } ================================================ FILE: lab-31/lab-31-rocketmq-demo/src/test/java/cn/iocoder/springboot/lab31/rocketmqdemo/producer/Demo06ProducerTest.java ================================================ package cn.iocoder.springboot.lab31.rocketmqdemo.producer; import cn.iocoder.springboot.lab31.rocketmqdemo.Application; import org.apache.rocketmq.client.producer.SendCallback; import org.apache.rocketmq.client.producer.SendResult; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.concurrent.CountDownLatch; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class Demo06ProducerTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private Demo06Producer producer; @Test public void testSyncSendOrderly() throws InterruptedException { // 发送多条消息 for (int i = 0; i < 3; i++) { int id = 1024; // 固定成 1024 ,方便我们测试是否发送到相同消息队列 SendResult result = producer.syncSendOrderly(id); logger.info("[testSyncSendOrderly][发送编号:[{}] 发送结果:[{}]]", id, result); } // 阻塞等待,保证消费 new CountDownLatch(1).await(); } @Test public void testASyncSendOrderly() throws InterruptedException { for (int i = 0; i < 3; i++) { int id = 1024; // 固定成 1024 ,方便我们测试是否发送到相同消息队列 producer.asyncSendOrderly(id, new SendCallback() { @Override public void onSuccess(SendResult result) { logger.info("[testASyncSendOrderly][发送编号:[{}] 发送成功,结果为:[{}]]", id, result); } @Override public void onException(Throwable e) { logger.info("[testASyncSendOrderly][发送编号:[{}] 发送异常]]", id, e); } }); } // 阻塞等待,保证消费 new CountDownLatch(1).await(); } @Test public void testOnewaySendOrderly() throws InterruptedException { for (int i = 0; i < 3; i++) { int id = 1024; // 固定成 1024 ,方便我们测试是否发送到相同消息队列 producer.onewaySendOrderly(id); logger.info("[testOnewaySendOrderly][发送编号:[{}] 发送完成]", id); } // 阻塞等待,保证消费 new CountDownLatch(1).await(); } } ================================================ FILE: lab-31/lab-31-rocketmq-demo/src/test/java/cn/iocoder/springboot/lab31/rocketmqdemo/producer/Demo07ProducerTest.java ================================================ package cn.iocoder.springboot.lab31.rocketmqdemo.producer; import cn.iocoder.springboot.lab31.rocketmqdemo.Application; import org.apache.rocketmq.client.producer.SendResult; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.concurrent.CountDownLatch; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class Demo07ProducerTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private Demo07Producer producer; @Test public void testSendMessageInTransaction() throws InterruptedException { int id = (int) (System.currentTimeMillis() / 1000); SendResult result = producer.sendMessageInTransaction(id); logger.info("[testSendMessageInTransaction][发送编号:[{}] 发送结果:[{}]]", id, result); // 阻塞等待,保证消费 new CountDownLatch(1).await(); } } ================================================ FILE: lab-31/lab-31-rocketmq-ons/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-31-rocketmq-ons org.apache.rocketmq rocketmq-spring-boot-starter 2.0.4 org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-31/lab-31-rocketmq-ons/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/Application.java ================================================ package cn.iocoder.springboot.lab31.rocketmqdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-31/lab-31-rocketmq-ons/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/consumer/Demo01Consumer.java ================================================ package cn.iocoder.springboot.lab31.rocketmqdemo.consumer; import cn.iocoder.springboot.lab31.rocketmqdemo.message.Demo01Message; import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; import org.apache.rocketmq.spring.core.RocketMQListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component @RocketMQMessageListener( topic = Demo01Message.TOPIC, consumerGroup = "GID_CONSUMER_GROUP_YUNAI_TEST" ) public class Demo01Consumer implements RocketMQListener { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public void onMessage(Demo01Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: lab-31/lab-31-rocketmq-ons/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/message/Demo01Message.java ================================================ package cn.iocoder.springboot.lab31.rocketmqdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { public static final String TOPIC = "TOPIC_YUNAI_TEST"; /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: lab-31/lab-31-rocketmq-ons/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/producer/Demo01Producer.java ================================================ package cn.iocoder.springboot.lab31.rocketmqdemo.producer; import cn.iocoder.springboot.lab31.rocketmqdemo.message.Demo01Message; import org.apache.rocketmq.client.producer.SendCallback; import org.apache.rocketmq.client.producer.SendResult; import org.apache.rocketmq.spring.core.RocketMQTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class Demo01Producer { @Autowired private RocketMQTemplate rocketMQTemplate; public SendResult syncSend(Integer id) { // 创建 Demo01Message 消息 Demo01Message message = new Demo01Message(); message.setId(id); // 同步发送消息 return rocketMQTemplate.syncSend(Demo01Message.TOPIC, message); } public void asyncSend(Integer id, SendCallback callback) { // 创建 Demo01Message 消息 Demo01Message message = new Demo01Message(); message.setId(id); // 异步发送消息 rocketMQTemplate.asyncSend(Demo01Message.TOPIC, message, callback); } public void onewaySend(Integer id) { // 创建 Demo01Message 消息 Demo01Message message = new Demo01Message(); message.setId(id); // oneway 发送消息 rocketMQTemplate.sendOneWay(Demo01Message.TOPIC, message); } } ================================================ FILE: lab-31/lab-31-rocketmq-ons/src/main/resources/application.yaml ================================================ # rocketmq 配置项,对应 RocketMQProperties 配置类 rocketmq: name-server: http://onsaddr.mq-internet-access.mq-internet.aliyuncs.com:80 # 阿里云 RocketMQ Namesrv access-channel: CLOUD # 设置使用阿里云 # Producer 配置项 producer: group: GID_PRODUCER_GROUP_YUNAI_TEST # 生产者分组 access-key: # 设置阿里云的 RocketMQ 的 access key !!!这里涉及到隐私,所以这里艿艿没有提供 secret-key: # 设置阿里云的 RocketMQ 的 secret key !!!这里涉及到隐私,所以这里艿艿没有提供 ================================================ FILE: lab-31/lab-31-rocketmq-ons/src/test/java/cn/iocoder/springboot/lab31/rocketmqdemo/package-info.java ================================================ package cn.iocoder.springboot.lab31.rocketmqdemo; ================================================ FILE: lab-31/lab-31-rocketmq-ons/src/test/java/cn/iocoder/springboot/lab31/rocketmqdemo/producer/Demo01ProducerTest.java ================================================ package cn.iocoder.springboot.lab31.rocketmqdemo.producer; import cn.iocoder.springboot.lab31.rocketmqdemo.Application; import org.apache.rocketmq.client.producer.SendCallback; import org.apache.rocketmq.client.producer.SendResult; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.concurrent.CountDownLatch; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class Demo01ProducerTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private Demo01Producer producer; @Test public void testSyncSend() throws InterruptedException { int id = (int) (System.currentTimeMillis() / 1000); SendResult result = producer.syncSend(id); logger.info("[testSyncSend][发送编号:[{}] 发送结果:[{}]]", id, result); // 阻塞等待,保证消费 new CountDownLatch(1).await(); } @Test public void testASyncSend() throws InterruptedException { int id = (int) (System.currentTimeMillis() / 1000); producer.asyncSend(id, new SendCallback() { @Override public void onSuccess(SendResult result) { logger.info("[testASyncSend][发送编号:[{}] 发送成功,结果为:[{}]]", id, result); } @Override public void onException(Throwable e) { logger.info("[testASyncSend][发送编号:[{}] 发送异常]]", id, e); } }); // 阻塞等待,保证消费 new CountDownLatch(1).await(); } @Test public void testOnewaySend() throws InterruptedException { int id = (int) (System.currentTimeMillis() / 1000); producer.onewaySend(id); logger.info("[testOnewaySend][发送编号:[{}] 发送完成]", id); // 阻塞等待,保证消费 new CountDownLatch(1).await(); } } ================================================ FILE: lab-31/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-31 pom lab-31-rocketmq-demo lab-31-rocketmq-ons ================================================ FILE: lab-31/《芋道 Spring Boot 消息队列 RocketMQ 入门》.md ================================================ ================================================ FILE: lab-32/lab-32-activemq-demo/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-32-activemq-demo org.springframework.boot spring-boot-starter-activemq org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-32/lab-32-activemq-demo/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/Application.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableAsync; @SpringBootApplication @EnableAsync // 开启异步 public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-32/lab-32-activemq-demo/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/consumer/Demo01Consumer.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo.consumer; import cn.iocoder.springboot.lab32.activemqdemo.message.Demo01Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jms.annotation.JmsListener; import org.springframework.stereotype.Component; @Component public class Demo01Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @JmsListener(destination = Demo01Message.QUEUE) public void onMessage(Demo01Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } // @JmsListener(destination = Demo01Message.QUEUE) // public void onMessage(javax.jms.Message message) { // logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); // } } ================================================ FILE: lab-32/lab-32-activemq-demo/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/message/Demo01Message.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo.message; import java.io.Serializable; public class Demo01Message implements Serializable { public static final String QUEUE = "QUEUE_DEMO_01"; /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: lab-32/lab-32-activemq-demo/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/producer/Demo01Producer.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo.producer; import cn.iocoder.springboot.lab32.activemqdemo.message.Demo01Message; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jms.core.JmsMessagingTemplate; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.AsyncResult; import org.springframework.stereotype.Component; import org.springframework.util.concurrent.ListenableFuture; @Component public class Demo01Producer { @Autowired private JmsMessagingTemplate jmsTemplate; public void syncSend(Integer id) { // 创建 ClusteringMessage 消息 Demo01Message message = new Demo01Message(); message.setId(id); // 同步发送消息 jmsTemplate.convertAndSend(Demo01Message.QUEUE, message); } @Async public ListenableFuture asyncSend(Integer id) { try { // 发送消息 this.syncSend(id); // 返回成功的 Future return AsyncResult.forValue(null); } catch (Throwable ex) { // 返回异常的 Future return AsyncResult.forExecutionException(ex); } } } ================================================ FILE: lab-32/lab-32-activemq-demo/src/main/resources/application.yaml ================================================ spring: # ActiveMQ 配置项,对应 ActiveMQProperties 配置类 activemq: broker-url: tcp://127.0.0.1:61616 # ActiveMQ Broker 的地址 user: admin # 账号 password: admin # 密码 packages: trust-all: true # 可信任的反序列化包 ================================================ FILE: lab-32/lab-32-activemq-demo/src/test/java/cn/iocoder/springboot/lab32/activemqdemo/package-info.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo; ================================================ FILE: lab-32/lab-32-activemq-demo/src/test/java/cn/iocoder/springboot/lab32/activemqdemo/producer/Demo01ProducerTest.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo.producer; import cn.iocoder.springboot.lab32.activemqdemo.Application; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.util.concurrent.ListenableFutureCallback; import java.util.concurrent.CountDownLatch; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class Demo01ProducerTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private Demo01Producer producer; @Test public void testSyncSend() throws InterruptedException { // 发送消息 int id = (int) (System.currentTimeMillis() / 1000); producer.syncSend(id); logger.info("[testSyncSend][发送编号:[{}] 发送成功]", id); // 阻塞等待,保证消费 new CountDownLatch(1).await(); } @Test public void testAsyncSend() throws InterruptedException { int id = (int) (System.currentTimeMillis() / 1000); producer.asyncSend(id).addCallback(new ListenableFutureCallback() { @Override public void onFailure(Throwable e) { logger.info("[testASyncSend][发送编号:[{}] 发送异常]]", id, e); } @Override public void onSuccess(Void aVoid) { logger.info("[testASyncSend][发送编号:[{}] 发送成功,发送成功]", id); } }); logger.info("[testASyncSend][发送编号:[{}] 调用完成]", id); // 阻塞等待,保证消费 new CountDownLatch(1).await(); } } ================================================ FILE: lab-32/lab-32-activemq-demo-concurrency/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-32-activemq-demo-concurrency org.springframework.boot spring-boot-starter-activemq org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-32/lab-32-activemq-demo-concurrency/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/Application.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-32/lab-32-activemq-demo-concurrency/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/consumer/Demo03Consumer.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo.consumer; import cn.iocoder.springboot.lab32.activemqdemo.message.Demo03Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jms.annotation.JmsListener; import org.springframework.stereotype.Component; @Component public class Demo03Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @JmsListener(destination = Demo03Message.QUEUE, concurrency = "2") public void onMessage(Demo03Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: lab-32/lab-32-activemq-demo-concurrency/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/message/Demo03Message.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo.message; import java.io.Serializable; public class Demo03Message implements Serializable { public static final String QUEUE = "QUEUE_DEMO_03"; /** * 编号 */ private Integer id; public Demo03Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo03Message{" + "id=" + id + '}'; } } ================================================ FILE: lab-32/lab-32-activemq-demo-concurrency/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/producer/Demo03Producer.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo.producer; import cn.iocoder.springboot.lab32.activemqdemo.message.Demo03Message; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jms.core.JmsMessagingTemplate; import org.springframework.stereotype.Component; @Component public class Demo03Producer { @Autowired private JmsMessagingTemplate jmsTemplate; public void syncSend(Integer id) { // 创建 Demo03Message 消息 Demo03Message message = new Demo03Message(); message.setId(id); // 同步发送消息 jmsTemplate.convertAndSend(Demo03Message.QUEUE, message); } } ================================================ FILE: lab-32/lab-32-activemq-demo-concurrency/src/main/resources/application.yaml ================================================ spring: # ActiveMQ 配置项,对应 ActiveMQProperties 配置类 activemq: broker-url: tcp://127.0.0.1:61616 # ActiveMQ Broker 的地址 user: admin # 账号 password: admin # 密码 packages: trust-all: true # 可信任的反序列化包 ================================================ FILE: lab-32/lab-32-activemq-demo-concurrency/src/test/java/cn/iocoder/springboot/lab32/activemqdemo/package-info.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo; ================================================ FILE: lab-32/lab-32-activemq-demo-concurrency/src/test/java/cn/iocoder/springboot/lab32/activemqdemo/producer/Demo03ProducerTest.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo.producer; import cn.iocoder.springboot.lab32.activemqdemo.Application; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.concurrent.CountDownLatch; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class Demo03ProducerTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private Demo03Producer producer; @Test public void testSyncSend() throws InterruptedException { for (int i = 0; i < 10; i++) { int id = (int) (System.currentTimeMillis() / 1000); producer.syncSend(id); // logger.info("[testSyncSend][发送编号:[{}] 发送成功]", id); } // 阻塞等待,保证消费 new CountDownLatch(1).await(); } } ================================================ FILE: lab-32/lab-32-activemq-demo-consume-retry/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-32-activemq-demo-consume-retry org.springframework.boot spring-boot-starter-activemq org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-32/lab-32-activemq-demo-consume-retry/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/Application.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-32/lab-32-activemq-demo-consume-retry/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/config/ActiveMQConfig.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo.config; import org.apache.activemq.ActiveMQConnectionFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.jms.activemq.ActiveMQConnectionFactoryCustomizer; import org.springframework.context.annotation.Configuration; @Configuration public class ActiveMQConfig implements ActiveMQConnectionFactoryCustomizer { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public void customize(ActiveMQConnectionFactory factory) { logger.info("[customize][默认重试策略: {}]", factory.getRedeliveryPolicy()); } } ================================================ FILE: lab-32/lab-32-activemq-demo-consume-retry/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/consumer/Demo05Consumer.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo.consumer; import cn.iocoder.springboot.lab32.activemqdemo.message.Demo05Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jms.annotation.JmsListener; import org.springframework.stereotype.Component; @Component public class Demo05Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @JmsListener(destination = Demo05Message.QUEUE) public void onMessage(Demo05Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); // 注意,此处抛出一个 RuntimeException 异常,模拟消费失败 throw new RuntimeException("我就是故意抛出一个异常"); } } ================================================ FILE: lab-32/lab-32-activemq-demo-consume-retry/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/message/Demo05Message.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo.message; import java.io.Serializable; public class Demo05Message implements Serializable { public static final String QUEUE = "QUEUE_DEMO_05"; /** * 编号 */ private Integer id; public Demo05Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo05Message{" + "id=" + id + '}'; } } ================================================ FILE: lab-32/lab-32-activemq-demo-consume-retry/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/producer/Demo05Producer.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo.producer; import cn.iocoder.springboot.lab32.activemqdemo.message.Demo05Message; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jms.core.JmsMessagingTemplate; import org.springframework.stereotype.Component; @Component public class Demo05Producer { @Autowired private JmsMessagingTemplate jmsTemplate; public void syncSend(Integer id) { // 创建 ClusteringMessage 消息 Demo05Message message = new Demo05Message(); message.setId(id); // 同步发送消息 jmsTemplate.convertAndSend(Demo05Message.QUEUE, message); } } ================================================ FILE: lab-32/lab-32-activemq-demo-consume-retry/src/main/resources/application.yaml ================================================ spring: # ActiveMQ 配置项,对应 ActiveMQProperties 配置类 activemq: broker-url: tcp://127.0.0.1:61616 # ActiveMQ Broker 的地址 user: admin # 账号 password: admin # 密码 packages: trust-all: true # 可信任的反序列化包 ================================================ FILE: lab-32/lab-32-activemq-demo-consume-retry/src/test/java/cn/iocoder/springboot/lab32/activemqdemo/package-info.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo; ================================================ FILE: lab-32/lab-32-activemq-demo-consume-retry/src/test/java/cn/iocoder/springboot/lab32/activemqdemo/producer/Demo05ProducerTest.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo.producer; import cn.iocoder.springboot.lab32.activemqdemo.Application; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.concurrent.CountDownLatch; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class Demo05ProducerTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private Demo05Producer producer; @Test public void testSyncSend() throws InterruptedException { // 发送消息 int id = (int) (System.currentTimeMillis() / 1000); producer.syncSend(id); logger.info("[testSyncSend][发送编号:[{}] 发送成功]", id); // 阻塞等待,保证消费 new CountDownLatch(1).await(); } } ================================================ FILE: lab-32/lab-32-activemq-demo-delay/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-32-activemq-demo-delay org.springframework.boot spring-boot-starter-activemq org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-32/lab-32-activemq-demo-delay/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/Application.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-32/lab-32-activemq-demo-delay/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/consumer/Demo02Consumer.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo.consumer; import cn.iocoder.springboot.lab32.activemqdemo.message.Demo02Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jms.annotation.JmsListener; import org.springframework.stereotype.Component; @Component public class Demo02Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @JmsListener(destination = Demo02Message.QUEUE) public void onMessage(Demo02Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: lab-32/lab-32-activemq-demo-delay/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/message/Demo02Message.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo.message; import java.io.Serializable; public class Demo02Message implements Serializable { public static final String QUEUE = "QUEUE_DEMO_02"; /** * 编号 */ private Integer id; public Demo02Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo02Message{" + "id=" + id + '}'; } } ================================================ FILE: lab-32/lab-32-activemq-demo-delay/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/producer/Demo02Producer.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo.producer; import cn.iocoder.springboot.lab32.activemqdemo.message.Demo02Message; import org.apache.activemq.ScheduledMessage; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jms.core.JmsMessagingTemplate; import org.springframework.stereotype.Component; import java.util.HashMap; import java.util.Map; @Component public class Demo02Producer { @Autowired private JmsMessagingTemplate jmsTemplate; public void syncSend(Integer id, Integer delay) { // 创建 Demo02Message 消息 Demo02Message message = new Demo02Message(); message.setId(id); // 创建 Header Map headers = null; if (delay != null && delay > 0) { headers = new HashMap<>(); headers.put(ScheduledMessage.AMQ_SCHEDULED_DELAY, delay); } // 同步发送消息 jmsTemplate.convertAndSend(Demo02Message.QUEUE, message, headers); } } ================================================ FILE: lab-32/lab-32-activemq-demo-delay/src/main/resources/application.yaml ================================================ spring: # ActiveMQ 配置项,对应 ActiveMQProperties 配置类 activemq: broker-url: tcp://127.0.0.1:61616 # ActiveMQ Broker 的地址 user: admin # 账号 password: admin # 密码 packages: trust-all: true # 可信任的反序列化包 ================================================ FILE: lab-32/lab-32-activemq-demo-delay/src/test/java/cn/iocoder/springboot/lab32/activemqdemo/package-info.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo; ================================================ FILE: lab-32/lab-32-activemq-demo-delay/src/test/java/cn/iocoder/springboot/lab32/activemqdemo/producer/Demo02ProducerTest.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo.producer; import cn.iocoder.springboot.lab32.activemqdemo.Application; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.concurrent.CountDownLatch; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class Demo02ProducerTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private Demo02Producer producer; @Test public void testSyncSend01() throws InterruptedException { // 不设置消息的过期时间 this.testSyncSendDelay(null); } @Test public void testSyncSend02() throws InterruptedException { // 设置发送消息的过期时间为 5000 毫秒 this.testSyncSendDelay(5000); } private void testSyncSendDelay(Integer delay) throws InterruptedException { int id = (int) (System.currentTimeMillis() / 1000); producer.syncSend(id, delay); logger.info("[testSyncSendDelay][发送编号:[{}] 发送成功]", id); // 阻塞等待,保证消费 new CountDownLatch(1).await(); } } ================================================ FILE: lab-32/lab-32-activemq-demo-message-model/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-32-activemq-demo-message-model org.springframework.boot spring-boot-starter-activemq org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-32/lab-32-activemq-demo-message-model/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/Application.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-32/lab-32-activemq-demo-message-model/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/config/ActiveMQConfig.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo.config; import org.springframework.boot.autoconfigure.jms.DefaultJmsListenerContainerFactoryConfigurer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jms.config.DefaultJmsListenerContainerFactory; import org.springframework.jms.core.JmsMessagingTemplate; import org.springframework.jms.core.JmsTemplate; import javax.jms.ConnectionFactory; @Configuration public class ActiveMQConfig { public static final String CLUSTERING_JMS_LISTENER_CONTAINER_FACTORY_BEAN_NAME = "clusteringJmsListenerContainerFactory"; public static final String BROADCAST_JMS_LISTENER_CONTAINER_FACTORY_BEAN_NAME = "broadcastJmsListenerContainerFactory"; public static final String CLUSTERING_JMS_TEMPLATE_BEAN_NAME = "clusteringJmsTemplate"; public static final String BROADCAST_JMS_TEMPLATE_BEAN_NAME = "broadcastJmsTemplate"; // ========== 集群消费 ========= @Bean(CLUSTERING_JMS_LISTENER_CONTAINER_FACTORY_BEAN_NAME) public DefaultJmsListenerContainerFactory clusteringJmsListenerContainerFactory( DefaultJmsListenerContainerFactoryConfigurer configurer, ConnectionFactory connectionFactory) { DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory(); configurer.configure(factory, connectionFactory); factory.setPubSubDomain(false); return factory; } @Bean(CLUSTERING_JMS_TEMPLATE_BEAN_NAME) public JmsMessagingTemplate clusteringJmsTemplate(ConnectionFactory connectionFactory) { // 创建 JmsTemplate 对象 JmsTemplate template = new JmsTemplate(connectionFactory); template.setPubSubDomain(false); // 创建 JmsMessageTemplate return new JmsMessagingTemplate(template); } // ========== 广播消费 ========== @Bean(BROADCAST_JMS_LISTENER_CONTAINER_FACTORY_BEAN_NAME) public DefaultJmsListenerContainerFactory broadcastJmsListenerContainerFactory( DefaultJmsListenerContainerFactoryConfigurer configurer, ConnectionFactory connectionFactory) { DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory(); configurer.configure(factory, connectionFactory); factory.setPubSubDomain(true); return factory; } @Bean(BROADCAST_JMS_TEMPLATE_BEAN_NAME) public JmsMessagingTemplate broadcastJmsTemplate(ConnectionFactory connectionFactory) { // 创建 JmsTemplate 对象 JmsTemplate template = new JmsTemplate(connectionFactory); template.setPubSubDomain(true); // 创建 JmsMessageTemplate return new JmsMessagingTemplate(template); } } ================================================ FILE: lab-32/lab-32-activemq-demo-message-model/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/consumer/BroadcastConsumer.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo.consumer; import cn.iocoder.springboot.lab32.activemqdemo.config.ActiveMQConfig; import cn.iocoder.springboot.lab32.activemqdemo.message.BroadcastMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jms.annotation.JmsListener; import org.springframework.stereotype.Component; @Component public class BroadcastConsumer { private Logger logger = LoggerFactory.getLogger(getClass()); @JmsListener(destination = BroadcastMessage.TOPIC, containerFactory = ActiveMQConfig.BROADCAST_JMS_LISTENER_CONTAINER_FACTORY_BEAN_NAME) public void onMessage(BroadcastMessage message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: lab-32/lab-32-activemq-demo-message-model/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/consumer/ClusteringConsumer.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo.consumer; import cn.iocoder.springboot.lab32.activemqdemo.config.ActiveMQConfig; import cn.iocoder.springboot.lab32.activemqdemo.message.ClusteringMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jms.annotation.JmsListener; import org.springframework.stereotype.Component; @Component public class ClusteringConsumer { private Logger logger = LoggerFactory.getLogger(getClass()); @JmsListener(destination = ClusteringMessage.QUEUE, containerFactory = ActiveMQConfig.CLUSTERING_JMS_LISTENER_CONTAINER_FACTORY_BEAN_NAME) public void onMessage(ClusteringMessage message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: lab-32/lab-32-activemq-demo-message-model/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/message/BroadcastMessage.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo.message; import java.io.Serializable; /** * 广播消费的消息示例 */ public class BroadcastMessage implements Serializable { public static final String TOPIC = "TOPIC_BROADCASTING"; /** * 编号 */ private Integer id; public BroadcastMessage setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "BroadcastMessage{" + "id=" + id + '}'; } } ================================================ FILE: lab-32/lab-32-activemq-demo-message-model/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/message/ClusteringMessage.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo.message; import java.io.Serializable; /** * 广播消费的消息示例 */ public class ClusteringMessage implements Serializable { public static final String QUEUE = "QUEUE_CLUSTERING"; /** * 编号 */ private Integer id; public ClusteringMessage setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "ClusteringtMessage{" + "id=" + id + '}'; } } ================================================ FILE: lab-32/lab-32-activemq-demo-message-model/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/producer/BroadcastProducer.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo.producer; import cn.iocoder.springboot.lab32.activemqdemo.config.ActiveMQConfig; import cn.iocoder.springboot.lab32.activemqdemo.message.BroadcastMessage; import org.springframework.jms.core.JmsMessagingTemplate; import org.springframework.stereotype.Component; import javax.annotation.Resource; @Component public class BroadcastProducer { @Resource(name = ActiveMQConfig.BROADCAST_JMS_TEMPLATE_BEAN_NAME) private JmsMessagingTemplate jmsMessagingTemplate; public void syncSend(Integer id) { // 创建 BroadcastMessage 消息 BroadcastMessage message = new BroadcastMessage(); message.setId(id); // 同步发送消息 jmsMessagingTemplate.convertAndSend(BroadcastMessage.TOPIC, message); } } ================================================ FILE: lab-32/lab-32-activemq-demo-message-model/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/producer/ClusteringProducer.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo.producer; import cn.iocoder.springboot.lab32.activemqdemo.config.ActiveMQConfig; import cn.iocoder.springboot.lab32.activemqdemo.message.ClusteringMessage; import org.springframework.jms.core.JmsMessagingTemplate; import org.springframework.stereotype.Component; import javax.annotation.Resource; @Component public class ClusteringProducer { @Resource(name = ActiveMQConfig.CLUSTERING_JMS_TEMPLATE_BEAN_NAME) private JmsMessagingTemplate jmsTemplate; public void syncSend(Integer id) { // 创建 ClusteringMessage 消息 ClusteringMessage message = new ClusteringMessage(); message.setId(id); // 同步发送消息 jmsTemplate.convertAndSend(ClusteringMessage.QUEUE, message); } } ================================================ FILE: lab-32/lab-32-activemq-demo-message-model/src/main/resources/application.yaml ================================================ spring: # ActiveMQ 配置项,对应 ActiveMQProperties 配置类 activemq: broker-url: tcp://127.0.0.1:61616 # ActiveMQ Broker 的地址 user: admin # 账号 password: admin # 密码 packages: trust-all: true ================================================ FILE: lab-32/lab-32-activemq-demo-message-model/src/test/java/cn/iocoder/springboot/lab32/activemqdemo/package-info.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo; ================================================ FILE: lab-32/lab-32-activemq-demo-message-model/src/test/java/cn/iocoder/springboot/lab32/activemqdemo/producer/BroadcastProducerTest.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo.producer; import cn.iocoder.springboot.lab32.activemqdemo.Application; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.concurrent.CountDownLatch; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class BroadcastProducerTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private BroadcastProducer producer; @Test public void mock() throws InterruptedException { // 阻塞等待,保证消费 new CountDownLatch(1).await(); } @Test public void testSyncSend() throws InterruptedException { for (int i = 0; i < 3; i++) { int id = (int) (System.currentTimeMillis() / 1000); producer.syncSend(id); logger.info("[testSyncSend][发送编号:[{}] 发送成功]", id); } // 阻塞等待,保证消费 new CountDownLatch(1).await(); } } ================================================ FILE: lab-32/lab-32-activemq-demo-message-model/src/test/java/cn/iocoder/springboot/lab32/activemqdemo/producer/ClusteringProducerTest.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo.producer; import cn.iocoder.springboot.lab32.activemqdemo.Application; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.concurrent.CountDownLatch; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class ClusteringProducerTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private ClusteringProducer producer; @Test public void mock() throws InterruptedException { // 阻塞等待,保证消费 new CountDownLatch(1).await(); } @Test public void testSyncSend() throws InterruptedException { // 发送 3 条消息 for (int i = 0; i < 3; i++) { int id = (int) (System.currentTimeMillis() / 1000); producer.syncSend(id); logger.info("[testSyncSend][发送编号:[{}] 发送成功]", id); } // 阻塞等待,保证消费 new CountDownLatch(1).await(); } } ================================================ FILE: lab-32/lab-32-activemq-demo-orderly/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-32-activemq-demo-orderly org.springframework.boot spring-boot-starter-activemq org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-32/lab-32-activemq-demo-orderly/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/Application.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-32/lab-32-activemq-demo-orderly/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/consumer/Demo04Consumer.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo.consumer; import cn.iocoder.springboot.lab32.activemqdemo.message.Demo04Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jms.annotation.JmsListener; import org.springframework.stereotype.Component; @Component public class Demo04Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @JmsListener(destination = Demo04Message.QUEUE_0) @JmsListener(destination = Demo04Message.QUEUE_1) @JmsListener(destination = Demo04Message.QUEUE_2) @JmsListener(destination = Demo04Message.QUEUE_3) public void onMessage(Demo04Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: lab-32/lab-32-activemq-demo-orderly/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/message/Demo04Message.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo.message; import java.io.Serializable; public class Demo04Message implements Serializable { public static final String QUEUE_BASE = "QUEUE_DEMO_04-"; public static final String QUEUE_0 = QUEUE_BASE + "0"; public static final String QUEUE_1 = QUEUE_BASE + "1"; public static final String QUEUE_2 = QUEUE_BASE + "2"; public static final String QUEUE_3 = QUEUE_BASE + "3"; public static final int QUEUE_COUNT = 4; /** * 编号 */ private Integer id; public Demo04Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo04Message{" + "id=" + id + '}'; } } ================================================ FILE: lab-32/lab-32-activemq-demo-orderly/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/producer/Demo04Producer.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo.producer; import cn.iocoder.springboot.lab32.activemqdemo.message.Demo04Message; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jms.core.JmsMessagingTemplate; import org.springframework.stereotype.Component; @Component public class Demo04Producer { @Autowired private JmsMessagingTemplate jmsTemplate; public void syncSend(Integer id) { // 创建 Demo04Message 消息 Demo04Message message = new Demo04Message(); message.setId(id); // 同步发送消息 jmsTemplate.convertAndSend(this.getQueue(id), message); } private String getQueue(Integer id) { return Demo04Message.QUEUE_BASE + (id % Demo04Message.QUEUE_COUNT); } } ================================================ FILE: lab-32/lab-32-activemq-demo-orderly/src/main/resources/application.yaml ================================================ spring: # ActiveMQ 配置项,对应 ActiveMQProperties 配置类 activemq: broker-url: tcp://127.0.0.1:61616 # ActiveMQ Broker 的地址 user: admin # 账号 password: admin # 密码 packages: trust-all: true # 可信任的反序列化包 ================================================ FILE: lab-32/lab-32-activemq-demo-orderly/src/test/java/cn/iocoder/springboot/lab32/activemqdemo/package-info.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo; ================================================ FILE: lab-32/lab-32-activemq-demo-orderly/src/test/java/cn/iocoder/springboot/lab32/activemqdemo/producer/Demo04ProducerTest.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo.producer; import cn.iocoder.springboot.lab32.activemqdemo.Application; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.concurrent.CountDownLatch; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class Demo04ProducerTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private Demo04Producer producer; @Test public void testSyncSend() throws InterruptedException { for (int i = 0; i < 2; i++) { for (int id = 0; id < 4; id++) { producer.syncSend(id); // logger.info("[testSyncSend][发送编号:[{}] 发送成功]", id); } } // 阻塞等待,保证消费 new CountDownLatch(1).await(); } } ================================================ FILE: lab-32/lab-32-activemq-native/pom.xml ================================================ lab-32 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-32-activemq-native org.apache.activemq activemq-client 5.15.10 ================================================ FILE: lab-32/lab-32-activemq-native/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/ActiveMQConsumer.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo; import javax.jms.*; // 使用 JMS API import java.util.concurrent.TimeUnit; public class ActiveMQConsumer { public static void main(String[] args) throws JMSException { // 创建连接 Connection connection = ActiveMQProducer.getConnection(); // 创建会话 final Session session = ActiveMQProducer.getSession(connection); // 创建队列 Queue queue = ActiveMQProducer.getQueue(session); // 创建 Consumer MessageConsumer consumer = session.createConsumer(queue); consumer.setMessageListener(new MessageListener() { public void onMessage(Message message) { TextMessage textMessage = (TextMessage) message; try { System.out.println(String.format("[线程:%s][消息编号:%s][消息内容:%s]", Thread.currentThread(), textMessage.getJMSMessageID(), textMessage.getText())); } catch (JMSException e) { throw new RuntimeException(e); } } }); // 关闭 try { TimeUnit.HOURS.sleep(1); } catch (InterruptedException ignore) { } session.close(); connection.close(); } } ================================================ FILE: lab-32/lab-32-activemq-native/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/ActiveMQProducer.java ================================================ package cn.iocoder.springboot.lab32.activemqdemo; import org.apache.activemq.ActiveMQConnectionFactory; // 使用 ActiveMQ 的客户端实现 import javax.jms.*; // 使用 JMS API public class ActiveMQProducer { private static final String BROKER_URL = "tcp://127.0.0.1:61616"; private static final String USERNAME = "admin"; private static final String PASSWORD = "admin"; private static final String QUEUE_NAME = "queue_demo"; // 只有 QUEUE_NAME 需要共享给 ActiveMQConsumer public static void main(String[] args) throws JMSException { // 创建连接 Connection connection = getConnection(); // 创建会话 Session session = getSession(connection); // 创建队列 Queue queue = getQueue(session); // 创建 Producer MessageProducer producer = session.createProducer(queue); // 发送 3 条消息 for (int i = 0; i < 3; i++) { Message message = session.createTextMessage("Hello World" + i); producer.send(message); } // 关闭 session.close(); connection.close(); } public static Connection getConnection() throws JMSException { // 创建连接 ConnectionFactory factory = new ActiveMQConnectionFactory(USERNAME, PASSWORD, BROKER_URL); Connection connection = factory.createConnection(); // 启动连接 connection.start(); return connection; } public static Session getSession(Connection connection) throws JMSException { // 第一个方法参数 transacted ,是否开启事务。这里设置为 false ,无需开启 // 第二个方法参数 acknowledgeMode ,确认模式。这里设置为 AUTO_ACKNOWLEDGE ,自动确认。推荐阅读 https://my.oschina.net/thinwonton/blog/995291 return connection.createSession(false, Session.AUTO_ACKNOWLEDGE); } public static Queue getQueue(Session session) throws JMSException { return session.createQueue(QUEUE_NAME); } } ================================================ FILE: lab-32/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-32 pom lab-32-activemq-native lab-32-activemq-demo lab-32-activemq-demo-message-model lab-32-activemq-demo-delay lab-32-activemq-demo-concurrency lab-32-activemq-demo-orderly lab-32-activemq-demo-consume-retry ================================================ FILE: lab-32/《芋道 Spring Boot 消息队列 ActiveMQ 入门》.md ================================================ ================================================ FILE: lab-33/lab-33-shiro-demo/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.10.RELEASE 4.0.0 lab-33-shiro-demo org.springframework.boot spring-boot-starter-web org.apache.shiro shiro-spring-boot-starter 1.4.2 ================================================ FILE: lab-33/lab-33-shiro-demo/src/main/java/cn/iocoder/springboot/lab01/shirodemo/Application.java ================================================ package cn.iocoder.springboot.lab01.shirodemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-33/lab-33-shiro-demo/src/main/java/cn/iocoder/springboot/lab01/shirodemo/config/ShiroConfig.java ================================================ package cn.iocoder.springboot.lab01.shirodemo.config; import org.apache.shiro.realm.Realm; import org.apache.shiro.realm.SimpleAccountRealm; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.LinkedHashMap; import java.util.Map; @Configuration public class ShiroConfig { @Bean public Realm realm() { // 创建 SimpleAccountRealm 对象 SimpleAccountRealm realm = new SimpleAccountRealm(); // 添加两个用户。参数分别是 username、password、roles 。 realm.addAccount("admin", "admin", "ADMIN"); realm.addAccount("normal", "normal", "NORMAL"); return realm; } @Bean public DefaultWebSecurityManager securityManager() { // 创建 DefaultWebSecurityManager 对象 DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); // 设置其使用的 Realm securityManager.setRealm(this.realm()); return securityManager; } @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean() { // 创建 ShiroFilterFactoryBean 对象,用于创建 ShiroFilter 过滤器 ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean(); // 设置 SecurityManager filterFactoryBean.setSecurityManager(this.securityManager()); // 设置 URL 们 filterFactoryBean.setLoginUrl("/login"); // 登陆 URL filterFactoryBean.setSuccessUrl("/login_success"); // 登陆成功 URL filterFactoryBean.setUnauthorizedUrl("/unauthorized"); // 无权限 URL // 设置 URL 的权限配置 filterFactoryBean.setFilterChainDefinitionMap(this.filterChainDefinitionMap()); return filterFactoryBean; } private Map filterChainDefinitionMap() { Map filterMap = new LinkedHashMap<>(); // 注意要使用有序的 LinkedHashMap ,顺序匹配 filterMap.put("/test/echo", "anon"); // 允许匿名访问 filterMap.put("/test/admin", "roles[ADMIN]"); // 需要 ADMIN 角色 filterMap.put("/test/normal", "roles[NORMAL]"); // 需要 NORMAL 角色 filterMap.put("/logout", "logout"); // 退出 filterMap.put("/**", "authc"); // 默认剩余的 URL ,需要经过认证 return filterMap; } } ================================================ FILE: lab-33/lab-33-shiro-demo/src/main/java/cn/iocoder/springboot/lab01/shirodemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab01.shirodemo.controller; import org.apache.shiro.authz.annotation.RequiresGuest; import org.apache.shiro.authz.annotation.RequiresRoles; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @RequiresGuest @GetMapping("/echo") public String demo() { return "示例返回"; } @GetMapping("/home") public String home() { return "我是首页"; } @RequiresRoles("ADMIN") @GetMapping("/admin") public String admin() { return "我是管理员"; } @RequiresRoles("NORMAL") @GetMapping("/normal") public String normal() { return "我是普通用户"; } } ================================================ FILE: lab-33/lab-33-shiro-demo/src/main/java/cn/iocoder/springboot/lab01/shirodemo/controller/SecurityController.java ================================================ package cn.iocoder.springboot.lab01.shirodemo.controller; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.ExpiredCredentialsException; import org.apache.shiro.authc.IncorrectCredentialsException; import org.apache.shiro.authc.LockedAccountException; import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.subject.Subject; import org.apache.shiro.web.filter.authc.FormAuthenticationFilter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletRequest; @Controller @RequestMapping("/") public class SecurityController { private Logger logger = LoggerFactory.getLogger(getClass()); @GetMapping("/login") public String loginPage() { return "login.html"; } @ResponseBody @PostMapping("/login") public String login(HttpServletRequest request) { // 判断是否已经登陆 Subject subject = SecurityUtils.getSubject(); if (subject.getPrincipal() != null) { return "你已经登陆账号:" + subject.getPrincipal(); } // 获得登陆失败的原因 String shiroLoginFailure = (String) request.getAttribute(FormAuthenticationFilter.DEFAULT_ERROR_KEY_ATTRIBUTE_NAME); // 翻译成人类看的懂的提示 String msg = ""; if (UnknownAccountException.class.getName().equals(shiroLoginFailure)) { msg = "账号不存在"; } else if (IncorrectCredentialsException.class.getName().equals(shiroLoginFailure)) { msg = "密码不正确"; } else if (LockedAccountException.class.getName().equals(shiroLoginFailure)) { msg = "账号被锁定"; } else if (ExpiredCredentialsException.class.getName().equals(shiroLoginFailure)) { msg = "账号已过期"; } else { msg = "未知"; logger.error("[login][未知登陆错误:{}]", shiroLoginFailure); } return "登陆失败,原因:" + msg; } @ResponseBody @GetMapping("/login_success") public String loginSuccess() { return "登陆成功"; } @ResponseBody @GetMapping("/unauthorized") public String unauthorized() { return "你没有权限"; } } ================================================ FILE: lab-33/lab-33-shiro-demo/src/main/java/cn/iocoder/springboot/lab01/shirodemo/controller/TestController.java ================================================ package cn.iocoder.springboot.lab01.shirodemo.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/test") public class TestController { @GetMapping("/demo") public String demo() { return "示例返回"; } @GetMapping("/home") public String home() { return "我是首页"; } @GetMapping("/admin") public String admin() { return "我是管理员"; } @GetMapping("/normal") public String normal() { return "我是普通用户"; } } ================================================ FILE: lab-33/lab-33-shiro-demo/src/main/resources/static/login.html ================================================ 登陆页面
用户名:
密码:
================================================ FILE: lab-33/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-33 pom lab-33-shiro-demo ================================================ FILE: lab-33/《芋道 Spring Boot 安全框架 Shiro 入门》.md ================================================ ================================================ FILE: lab-34/lab-34-actuator-demo/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-34-actuator-demo org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator ================================================ FILE: lab-34/lab-34-actuator-demo/src/main/java/cn/iocoder/springboot/lab34/actuatordemo/Application.java ================================================ package cn.iocoder.springboot.lab34.actuatordemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-34/lab-34-actuator-demo/src/main/resources/application.yaml ================================================ management: endpoints: # Actuator HTTP 配置项,对应 WebEndpointProperties 配置类 web: base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 exclude: # 在 include 的基础上,需要排除的端点。通过设置 * ,可以排除所有端点。 # server: # port: 8081 # endpoint: # shutdown: # enabled: true ================================================ FILE: lab-34/lab-34-actuator-demo-auditevents/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-34-actuator-demo-auditevents org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator org.springframework.boot spring-boot-starter-security 2.2.0.RELEASE ================================================ FILE: lab-34/lab-34-actuator-demo-auditevents/src/main/java/cn/iocoder/springboot/lab34/actuatordemo/Application.java ================================================ package cn.iocoder.springboot.lab34.actuatordemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-34/lab-34-actuator-demo-auditevents/src/main/java/cn/iocoder/springboot/lab34/actuatordemo/config/ActuateConfig.java ================================================ package cn.iocoder.springboot.lab34.actuatordemo.config; import org.springframework.boot.actuate.audit.AuditEventRepository; import org.springframework.boot.actuate.audit.InMemoryAuditEventRepository; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class ActuateConfig { @Bean public AuditEventRepository auditEventRepository() { return new InMemoryAuditEventRepository(); } } ================================================ FILE: lab-34/lab-34-actuator-demo-auditevents/src/main/resources/application.yaml ================================================ management: endpoint: # AuditEventsEndpoint 端点配置项 auditevents: enabled: true # 是否开启。默认为 true 开启 endpoints: # Actuator HTTP 配置项,对应 WebEndpointProperties 配置类 web: base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 exclude: # 在 include 的基础上,需要排除的端点。通过设置 * ,可以排除所有端点。 spring: # Spring Security 配置项,对应 SecurityProperties 配置类 security: # 配置默认的 InMemoryUserDetailsManager 的用户账号与密码。 user: name: user # 账号 password: user # 密码 ================================================ FILE: lab-34/lab-34-actuator-demo-custom-endpoint/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-34-actuator-demo-custom-endpoint org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator ================================================ FILE: lab-34/lab-34-actuator-demo-custom-endpoint/src/main/java/cn/iocoder/springboot/lab34/actuatordemo/Application.java ================================================ package cn.iocoder.springboot.lab34.actuatordemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-34/lab-34-actuator-demo-custom-endpoint/src/main/java/cn/iocoder/springboot/lab34/actuatordemo/endpoint/DemoEndPoint.java ================================================ package cn.iocoder.springboot.lab34.actuatordemo.endpoint; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; import org.springframework.stereotype.Component; import java.util.HashMap; import java.util.Map; @Component @Endpoint(id = "demo") public class DemoEndPoint { @ReadOperation public Map hello() { Map result = new HashMap<>(); result.put("作者", "yudaoyuanma"); result.put("秃头", "true"); return result; } } ================================================ FILE: lab-34/lab-34-actuator-demo-custom-endpoint/src/main/resources/application.yaml ================================================ management: endpoints: # Actuator HTTP 配置项,对应 WebEndpointProperties 配置类 web: base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 exclude: # 在 include 的基础上,需要排除的端点。通过设置 * ,可以排除所有端点。 # endpoint: # shutdown: # enabled: true ================================================ FILE: lab-34/lab-34-actuator-demo-health/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-34-actuator-demo-health org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator ================================================ FILE: lab-34/lab-34-actuator-demo-health/src/main/java/cn/iocoder/springboot/lab34/actuatordemo/Application.java ================================================ package cn.iocoder.springboot.lab34.actuatordemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-34/lab-34-actuator-demo-health/src/main/java/cn/iocoder/springboot/lab34/actuatordemo/actuate/DemoHealthIndicator.java ================================================ package cn.iocoder.springboot.lab34.actuatordemo.actuate; import org.springframework.boot.actuate.health.AbstractHealthIndicator; import org.springframework.boot.actuate.health.Health; import org.springframework.stereotype.Component; @Component public class DemoHealthIndicator extends AbstractHealthIndicator { @Override protected void doHealthCheck(Health.Builder builder) { // 判断是否健康 boolean success = checkSuccess(); // 如果健康,则标记状态为 UP if (success) { builder.up().build(); return; } // 如果不健康,则标记状态为 DOWN builder.down().withDetail("msg", "我就是做个示例而已"); } private boolean checkSuccess() { return false; } } ================================================ FILE: lab-34/lab-34-actuator-demo-health/src/main/resources/application.yaml ================================================ management: endpoint: # Health 端点配置项,对应 HealthProperties 配置类 health: enabled: true # 是否开启。默认为 true 开启。 show-details: ALWAYS # 何时显示完整的健康信息。默认为 NEVER 都不展示。可选 WHEN_AUTHORIZED 当经过授权的用户;可选 ALWAYS 总是展示。 status: http-mapping: # 设置不同健康状态对应的响应状态码 DOWN: 503 order: DOWN, OUT_OF_SERVICE, UP, UNKNOWN # 状态排序。 health: # DiskSpaceHealthIndicator 配置项,对应 DiskSpaceHealthIndicatorProperties diskspace: enabled: true # 是否开启。默认为 true 开启。 path: . # 目录。默认为 . 当前目录。 threshold: # 剩余空间的阀值。默认为 10M 。 endpoints: # Actuator HTTP 配置项,对应 WebEndpointProperties 配置类 web: base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 exclude: # 在 include 的基础上,需要排除的端点。通过设置 * ,可以排除所有端点。 ================================================ FILE: lab-34/lab-34-actuator-demo-httptrace/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-34-actuator-demo-httptrace org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator ================================================ FILE: lab-34/lab-34-actuator-demo-httptrace/src/main/java/cn/iocoder/springboot/lab34/actuatordemo/Application.java ================================================ package cn.iocoder.springboot.lab34.actuatordemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-34/lab-34-actuator-demo-httptrace/src/main/java/cn/iocoder/springboot/lab34/actuatordemo/config/ActuateConfig.java ================================================ package cn.iocoder.springboot.lab34.actuatordemo.config; import org.springframework.boot.actuate.trace.http.HttpTraceRepository; import org.springframework.boot.actuate.trace.http.InMemoryHttpTraceRepository; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class ActuateConfig { @Bean public HttpTraceRepository httpTraceRepository() { return new InMemoryHttpTraceRepository(); } } ================================================ FILE: lab-34/lab-34-actuator-demo-httptrace/src/main/resources/application.yaml ================================================ management: endpoint: # HttpTrace 端点配置项 httptrace: enabled: true # 是否开启。默认为 true 开启 # HttpTrace 的具体配置项,对应 HttpTraceProperties 配置类 trace: http: enabled: true # 是否开启。默认为 true 开启。 include: # 包含的 trace 项的数组。默认不包含 COOKIE_HEADERS、AUTHORIZATION_HEADER 项。 endpoints: # Actuator HTTP 配置项,对应 WebEndpointProperties 配置类 web: base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 exclude: # 在 include 的基础上,需要排除的端点。通过设置 * ,可以排除所有端点。 ================================================ FILE: lab-34/lab-34-actuator-demo-info/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-34-actuator-demo-info org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator org.springframework.boot spring-boot-maven-plugin build-info pl.project13.maven git-commit-id-plugin revision ================================================ FILE: lab-34/lab-34-actuator-demo-info/src/main/java/cn/iocoder/springboot/lab34/actuatordemo/Application.java ================================================ package cn.iocoder.springboot.lab34.actuatordemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-34/lab-34-actuator-demo-info/src/main/java/cn/iocoder/springboot/lab34/actuatordemo/config/ActuateConfig.java ================================================ package cn.iocoder.springboot.lab34.actuatordemo.config; import org.springframework.boot.actuate.info.InfoContributor; import org.springframework.boot.actuate.info.MapInfoContributor; import org.springframework.boot.actuate.info.SimpleInfoContributor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.Collections; @Configuration public class ActuateConfig { @Bean public InfoContributor exampleInfo() { return new SimpleInfoContributor("example", Collections.singletonMap("key", "value")); } @Bean public InfoContributor exampleInfo02() { return new MapInfoContributor(Collections.singletonMap("example02", "nicai")); } } ================================================ FILE: lab-34/lab-34-actuator-demo-info/src/main/java/cn/iocoder/springboot/lab34/actuatordemo/demo/DemoInfoContributor.java ================================================ package cn.iocoder.springboot.lab34.actuatordemo.demo; import org.springframework.boot.actuate.info.Info; import org.springframework.boot.actuate.info.InfoContributor; import org.springframework.stereotype.Component; import java.util.Collections; @Component public class DemoInfoContributor implements InfoContributor { @Override public void contribute(Info.Builder builder) { builder.withDetail("demo", Collections.singletonMap("key", "value")); } } ================================================ FILE: lab-34/lab-34-actuator-demo-info/src/main/resources/application.yaml ================================================ management: endpoint: # Info 端点配置项 info: enabled: true # 是否开启。默认为 true 开启。 info: # EnvironmentInfoContributor 的配置项 env: enabled: true # BuildInfoContributor 的配置属性 build: enabled: true # GitInfoContributor 的配置属性 git: enabled: true mode: SIMPLE # Git 信息展示模式。SIMPLE 默认,只展示精简的 Git 版本信息;FULL 模式,展示完整的 Git 版本信息。 endpoints: # Actuator HTTP 配置项,对应 WebEndpointProperties 配置类 web: base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 exclude: # 在 include 的基础上,需要排除的端点。通过设置 * ,可以排除所有端点。 # info 配置项 info: app: java: source: @java.version@ target: @java.version@ encoding: UTF-8 version: @project.version@ ================================================ FILE: lab-34/lab-34-actuator-demo-metrics/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-34-actuator-demo-metrics org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator ================================================ FILE: lab-34/lab-34-actuator-demo-metrics/src/main/java/cn/iocoder/springboot/lab34/actuatordemo/Application.java ================================================ package cn.iocoder.springboot.lab34.actuatordemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-34/lab-34-actuator-demo-metrics/src/main/java/cn/iocoder/springboot/lab34/actuatordemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab34.actuatordemo.controller; import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.Metrics; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { /** * DEMO 访问次数 Metrics */ private static final Counter METRICS_DEMO_COUNT = Counter .builder("demo.visit.count") // 指标的名字 .description("demo 访问次数") // 指标的描述 .baseUnit("次") // 指标的单位 .tag("test", "nicai") // 自定义标签 .register(Metrics.globalRegistry); // 注册到全局 MeterRegistry 指标注册表 // private static final Counter METRICS_DEMO_COUNT = Metrics.counter("demo.visit.count"); @GetMapping("/visit") public String visit() { // 增加次数 METRICS_DEMO_COUNT.increment(); return "Demo 示例"; } } ================================================ FILE: lab-34/lab-34-actuator-demo-metrics/src/main/java/cn/iocoder/springboot/lab34/actuatordemo/controller/ExampleController.java ================================================ package cn.iocoder.springboot.lab34.actuatordemo.controller; import io.micrometer.core.annotation.Timed; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; // 参考 https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html#production-ready-metrics-spring-mvc 文档 // 需要设置 management.metrics.web.server.request.autotime.enabled = false @RestController @RequestMapping("/example") @Timed public class ExampleController { @GetMapping("/visit") // @Counted(value = "example.visit.count", description = "example 访问次数" @Timed(value = "all.people", longTask = true) public String visit() { return "Example 示例"; } } ================================================ FILE: lab-34/lab-34-actuator-demo-metrics/src/main/resources/application.yaml ================================================ #spring: # application: # name: demo-application management: endpoint: # Metrics 端点配置项 metrics: enabled: true # 是否开启。默认为 true 开启。 # Metrics 的具体配置项,对应 MetricsProperties 配置类 metrics: # 设置指定前缀的指标是否开启 enable: xxx: false # 通用 tag tags: application: demo-application endpoints: # Actuator HTTP 配置项,对应 WebEndpointProperties 配置类 web: base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 exclude: # 在 include 的基础上,需要排除的端点。通过设置 * ,可以排除所有端点。 ================================================ FILE: lab-34/lab-34-actuator-demo-security/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-34-actuator-demo-security org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator org.springframework.boot spring-boot-starter-security ================================================ FILE: lab-34/lab-34-actuator-demo-security/src/main/java/cn/iocoder/springboot/lab34/actuatordemo/Application.java ================================================ package cn.iocoder.springboot.lab34.actuatordemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-34/lab-34-actuator-demo-security/src/main/java/cn/iocoder/springboot/lab34/actuatordemo/config/SecurityConfig.java ================================================ package cn.iocoder.springboot.lab34.actuatordemo.config; import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { // 访问 EndPoint 地址,需要经过认证,并且拥有 ADMIN 角色 http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests((requests) -> requests.anyRequest().hasRole("ADMIN")); // 开启 Basic Auth http.httpBasic(); } } ================================================ FILE: lab-34/lab-34-actuator-demo-security/src/main/resources/application.yaml ================================================ management: endpoints: # Actuator HTTP 配置项,对应 WebEndpointProperties 配置类 web: base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 exclude: # 在 include 的基础上,需要排除的端点。通过设置 * ,可以排除所有端点。 # server: # port: 8081 # endpoint: # shutdown: # enabled: true spring: # Spring Security 配置项,对应 SecurityProperties 配置类 security: # 配置默认的 InMemoryUserDetailsManager 的用户账号与密码。 user: name: user # 账号 password: user # 密码 roles: ADMIN # 拥有角色 ================================================ FILE: lab-34/lab-34-actuator-test/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-34-actuator-test org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator ================================================ FILE: lab-34/lab-34-actuator-test/src/main/java/cn/iocoder/springboot/lab34/actuatordemo/Application.java ================================================ package cn.iocoder.springboot.lab34.actuatordemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-34/lab-34-actuator-test/src/main/java/cn/iocoder/springboot/lab34/actuatordemo/controller/TestController.java ================================================ package cn.iocoder.springboot.lab34.actuatordemo.controller; import org.springframework.web.bind.annotation.RestController; @RestController public class TestController { } ================================================ FILE: lab-34/lab-34-actuator-test/src/main/resources/application.yaml ================================================ management: endpoints: # Actuator HTTP 配置项,对应 WebEndpointProperties 配置类 web: base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 exclude: # 在 include 的基础上,需要排除的端点。通过设置 * ,可以排除所有端点。 # server: # port: 8081 # endpoint: # shutdown: # enabled: true ================================================ FILE: lab-34/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-34 pom lab-34-actuator-demo lab-34-actuator-demo-health lab-34-actuator-demo-info lab-34-actuator-demo-metrics lab-34-actuator-demo-httptrace lab-34-actuator-demo-auditevents lab-34-actuator-demo-custom-endpoint lab-34-actuator-demo-security lab-34-actuator-test ================================================ FILE: lab-34/《芋道 Spring Boot 监控端点 Actuator 入门》.md ================================================ ================================================ FILE: lab-35/lab-35-admin-01-adminserver/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-35-admin-01-adminserver de.codecentric spring-boot-admin-starter-server 2.2.0 ================================================ FILE: lab-35/lab-35-admin-01-adminserver/src/main/java/cn/iocoder/springboot/lab35/adminserver/AdminServerApplication.java ================================================ package cn.iocoder.springboot.lab35.adminserver; import de.codecentric.boot.admin.server.config.EnableAdminServer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @EnableAdminServer public class AdminServerApplication { public static void main(String[] args) { SpringApplication.run(AdminServerApplication.class, args); } } ================================================ FILE: lab-35/lab-35-admin-01-demo-application/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-35-admin-01-demo-application org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator de.codecentric spring-boot-admin-starter-client 2.2.0 ================================================ FILE: lab-35/lab-35-admin-01-demo-application/src/main/java/cn/iocoder/springboot/lab35/demo/DemoApplication.java ================================================ package cn.iocoder.springboot.lab35.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } ================================================ FILE: lab-35/lab-35-admin-01-demo-application/src/main/resources/application.yaml ================================================ management: endpoints: # Actuator HTTP 配置项,对应 WebEndpointProperties 配置类 web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 spring: application: name: demo-application # 应用名 boot: admin: client: url: http://127.0.0.1:8080 # Spring Boot Admin Server 地址 server: port: 18080 # 设置自定义 Server 端口,避免和 Spring Boot Admin Server 端口冲突。 ================================================ FILE: lab-35/lab-35-admin-02-adminserver/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-35-admin-02-adminserver de.codecentric spring-boot-admin-starter-server 2.2.0 org.springframework.cloud spring-cloud-starter-netflix-eureka-client 2.2.1.RELEASE ================================================ FILE: lab-35/lab-35-admin-02-adminserver/src/main/java/cn/iocoder/springboot/lab35/adminserver/AdminServerApplication.java ================================================ package cn.iocoder.springboot.lab35.adminserver; import de.codecentric.boot.admin.server.config.EnableAdminServer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @SpringBootApplication @EnableAdminServer @EnableDiscoveryClient public class AdminServerApplication { public static void main(String[] args) { SpringApplication.run(AdminServerApplication.class, args); } } ================================================ FILE: lab-35/lab-35-admin-02-adminserver/src/main/resources/application.yaml ================================================ eureka: client: service-url: defaultZone: http://127.0.0.1:8761/eureka register-with-eureka: false # 不注册到 Eureka 中 ================================================ FILE: lab-35/lab-35-admin-02-demo-application/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-35-admin-02-demo-application org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator org.springframework.cloud spring-cloud-starter-netflix-eureka-client 2.2.1.RELEASE ================================================ FILE: lab-35/lab-35-admin-02-demo-application/src/main/java/cn/iocoder/springboot/lab35/demo/Demo01Application.java ================================================ package cn.iocoder.springboot.lab35.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @SpringBootApplication @EnableDiscoveryClient public class Demo01Application { public static void main(String[] args) { System.setProperty("server.port", "18081"); // 端口 18081 SpringApplication.run(Demo01Application.class, args); } } ================================================ FILE: lab-35/lab-35-admin-02-demo-application/src/main/java/cn/iocoder/springboot/lab35/demo/Demo02Application.java ================================================ package cn.iocoder.springboot.lab35.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @SpringBootApplication @EnableDiscoveryClient public class Demo02Application { public static void main(String[] args) { System.setProperty("server.port", "18082"); // 端口 18082 SpringApplication.run(Demo02Application.class, args); } } ================================================ FILE: lab-35/lab-35-admin-02-demo-application/src/main/resources/application.yaml ================================================ management: endpoints: # Actuator HTTP 配置项,对应 WebEndpointProperties 配置类 web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 spring: application: name: demo-application # 应用名 eureka: client: service-url: defaultZone: http://127.0.0.1:8761/eureka ================================================ FILE: lab-35/lab-35-admin-02-eurekaserver/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-35-admin-02-eurekaserver org.springframework.cloud spring-cloud-starter-netflix-eureka-server 2.2.1.RELEASE ================================================ FILE: lab-35/lab-35-admin-02-eurekaserver/src/main/java/cn/iocoder/springboot/lab35/eurekaserver/EurekaServerApplication.java ================================================ package cn.iocoder.springboot.lab35.eurekaserver; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; @SpringBootApplication @EnableEurekaServer public class EurekaServerApplication { public static void main(String[] args) { SpringApplication.run(EurekaServerApplication.class, args); } } ================================================ FILE: lab-35/lab-35-admin-02-eurekaserver/src/main/resources/application.yaml ================================================ spring: application: name: eureka-server # 应用名 server: port: 8761 # 自定义服务器端口,避免冲突 eureka: client: register-with-eureka: false # 不注册到 Eureka 中 fetch-registry: false # 不从 Eureka 拉取注册信息 ================================================ FILE: lab-35/lab-35-admin-03-adminserver/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-35-admin-03-adminserver de.codecentric spring-boot-admin-starter-server 2.2.0 org.springframework.boot spring-boot-starter-security ================================================ FILE: lab-35/lab-35-admin-03-adminserver/src/main/java/cn/iocoder/springboot/lab35/adminserver/AdminServerApplication.java ================================================ package cn.iocoder.springboot.lab35.adminserver; import de.codecentric.boot.admin.server.config.EnableAdminServer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @EnableAdminServer public class AdminServerApplication { public static void main(String[] args) { SpringApplication.run(AdminServerApplication.class, args); } } ================================================ FILE: lab-35/lab-35-admin-03-adminserver/src/main/java/cn/iocoder/springboot/lab35/adminserver/config/SecurityConfig.java ================================================ package cn.iocoder.springboot.lab35.adminserver.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; import org.springframework.security.config.web.server.ServerHttpSecurity; import org.springframework.security.core.userdetails.MapReactiveUserDetailsService; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.web.server.SecurityWebFilterChain; @Configuration @EnableWebFluxSecurity // 开启 Security 对 WebFlux 的安全功能 public class SecurityConfig { @Bean public MapReactiveUserDetailsService userDetailsService() { // 创建一个用户 UserDetails user = User.withDefaultPasswordEncoder() .username("user") .password("user") .roles("USER") .build(); // 如果胖友有更多用户的诉求,这里可以继续创建 // 创建 MapReactiveUserDetailsService return new MapReactiveUserDetailsService(user); } @Bean public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { http.authorizeExchange(exchanges -> // 设置权限配置 exchanges .pathMatchers("/assets/**").permitAll() // 静态资源,允许匿名访问 .pathMatchers("/login").permitAll() // 登陆接口,允许匿名访问 .anyExchange().authenticated() // ) .formLogin().loginPage("/login") // 登陆页面 .and().logout().logoutUrl("/logout") // 登出界面 .and().httpBasic() // HTTP Basic 认证方式 .and().csrf().disable(); // csrf 禁用 return http.build(); } } ================================================ FILE: lab-35/lab-35-admin-03-demo-application/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-35-admin-03-demo-application org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator de.codecentric spring-boot-admin-starter-client 2.2.0 org.springframework.boot spring-boot-starter-security ================================================ FILE: lab-35/lab-35-admin-03-demo-application/src/main/java/cn/iocoder/springboot/lab35/demo/DemoApplication.java ================================================ package cn.iocoder.springboot.lab35.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } ================================================ FILE: lab-35/lab-35-admin-03-demo-application/src/main/resources/application.yaml ================================================ management: endpoints: # Actuator HTTP 配置项,对应 WebEndpointProperties 配置类 web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 spring: application: name: demo-application # 应用名 # Spring Security 配置项,对应 SecurityProperties 配置类 security: # 配置默认的 InMemoryUserDetailsManager 的用户账号与密码。 user: name: test # 账号 password: test # 密码 boot: admin: client: url: http://127.0.0.1:8080 # Spring Boot Admin Server 地址 username: user # Spring Boot Admin Server 的认证账号 password: user # Spring Boot Admin Server 的认证密码 instance: metadata: user.name: ${spring.security.user.name} # Actuator 端点的认证账号 user.password: ${spring.security.user.password} # Actuator 端点的认证密码 server: port: 18080 # 设置自定义 Server 端口,避免和 Spring Boot Admin Server 端口冲突。 ================================================ FILE: lab-35/lab-35-admin-04-adminserver/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-35-admin-04-adminserver de.codecentric spring-boot-admin-starter-server 2.2.0 org.springframework.boot spring-boot-starter-mail ================================================ FILE: lab-35/lab-35-admin-04-adminserver/src/main/java/cn/iocoder/springboot/lab35/adminserver/AdminServerApplication.java ================================================ package cn.iocoder.springboot.lab35.adminserver; import de.codecentric.boot.admin.server.config.EnableAdminServer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @EnableAdminServer public class AdminServerApplication { public static void main(String[] args) { SpringApplication.run(AdminServerApplication.class, args); } } ================================================ FILE: lab-35/lab-35-admin-04-adminserver/src/main/resources/application.yaml ================================================ spring: mail: # 配置发送告警的邮箱 host: smtp.126.com username: wwbmlhh@126.com password: '******' default-encoding: UTF-8 boot: admin: notify: mail: from: ${spring.mail.username} # 告警发件人 to: 7685413@qq.com # 告警收件人 ================================================ FILE: lab-35/lab-35-admin-05-adminserver/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-35-admin-05-adminserver de.codecentric spring-boot-admin-starter-server 2.2.0 ================================================ FILE: lab-35/lab-35-admin-05-adminserver/src/main/java/cn/iocoder/springboot/lab35/adminserver/AdminServerApplication.java ================================================ package cn.iocoder.springboot.lab35.adminserver; import de.codecentric.boot.admin.server.config.EnableAdminServer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @EnableAdminServer public class AdminServerApplication { public static void main(String[] args) { SpringApplication.run(AdminServerApplication.class, args); } } ================================================ FILE: lab-35/lab-35-admin-05-adminserver/src/main/java/cn/iocoder/springboot/lab35/adminserver/notify/LoggerNotifier.java ================================================ package cn.iocoder.springboot.lab35.adminserver.notify; import de.codecentric.boot.admin.server.domain.entities.Instance; import de.codecentric.boot.admin.server.domain.entities.InstanceRepository; import de.codecentric.boot.admin.server.domain.events.InstanceEvent; import de.codecentric.boot.admin.server.domain.events.InstanceStatusChangedEvent; import de.codecentric.boot.admin.server.notify.AbstractEventNotifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import reactor.core.publisher.Mono; @Component public class LoggerNotifier extends AbstractEventNotifier { private Logger logger = LoggerFactory.getLogger(getClass()); public LoggerNotifier(InstanceRepository repository) { super(repository); } @Override protected Mono doNotify(InstanceEvent event, Instance instance) { return Mono.fromRunnable(() -> { if (event instanceof InstanceStatusChangedEvent) { logger.info("Instance {} ({}) is {}", instance.getRegistration().getName(), event.getInstance(), ((InstanceStatusChangedEvent) event).getStatusInfo().getStatus()); } else { logger.info("Instance {} ({}) {}", instance.getRegistration().getName(), event.getInstance(), event.getType()); } }); } } ================================================ FILE: lab-35/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-35 pom lab-35-admin-01-adminserver lab-35-admin-01-demo-application lab-35-admin-02-adminserver lab-35-admin-02-demo-application lab-35-admin-02-eurekaserver lab-35-admin-03-adminserver lab-35-admin-03-demo-application lab-35-admin-04-adminserver lab-35-admin-05-adminserver ================================================ FILE: lab-35/《芋道 Spring Boot 监控工具 Admin 入门》.md ================================================ ================================================ FILE: lab-36/lab-36-prometheus-demo/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-36-prometheus-demo org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator io.micrometer micrometer-registry-prometheus ================================================ FILE: lab-36/lab-36-prometheus-demo/src/main/java/cn/iocoder/springboot/lab36/prometheusdemo/Application.java ================================================ package cn.iocoder.springboot.lab36.prometheusdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-36/lab-36-prometheus-demo/src/main/resources/application.yaml ================================================ spring: application: name: demo-application # 应用名 management: endpoints: # Actuator HTTP 配置项,对应 WebEndpointProperties 配置类 web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 metrics: tags: # 通用标签 application: ${spring.application.name} ================================================ FILE: lab-36/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-36 pom lab-36-prometheus-demo ================================================ FILE: lab-36/《芋道 Spring Boot 监控平台 Prometheus + Grafana 入门》.md ================================================ ================================================ FILE: lab-37/lab-37-logging-actuator/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-37-logging-actuator org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator ================================================ FILE: lab-37/lab-37-logging-actuator/src/main/java/cn/iocoder/springboot/lab37/loggingdemo/Application.java ================================================ package cn.iocoder.springboot.lab37.loggingdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-37/lab-37-logging-actuator/src/main/java/cn/iocoder/springboot/lab37/loggingdemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab37.loggingdemo.controller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { private Logger logger = LoggerFactory.getLogger(getClass()); @GetMapping("/debug") public void debug() { logger.debug("debug"); } @GetMapping("/info") public void info() { logger.info("info"); } @GetMapping("/error") public void error() { logger.error("error"); } } ================================================ FILE: lab-37/lab-37-logging-actuator/src/main/resources/application.yaml ================================================ spring: application: name: demo-application # 应用名 logging: # 日志文件配置 file: # path: /Users/yunai/logs/ # 日志文件路径。 name: /Users/yunai/logs/${spring.application.name}.log # 日志文件名。 max-history: 7 # 日志文件要保留的归档的最大天数。默认为 7 天。 max-size: 10MB # 日志文件的最大大小。默认为 10MB 。 # 日志级别 level: cn: iocoder: springboot: lab37: loggingdemo: controller: DEBUG management: endpoints: # Actuator HTTP 配置项,对应 WebEndpointProperties 配置类 web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 ================================================ FILE: lab-37/lab-37-logging-aop/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-37-logging-aop org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-aop ================================================ FILE: lab-37/lab-37-logging-aop/src/main/java/cn/iocoder/springboot/lab37/loggingdemo/Application.java ================================================ package cn.iocoder.springboot.lab37.loggingdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-37/lab-37-logging-aop/src/main/java/cn/iocoder/springboot/lab37/loggingdemo/aspect/HttpAccessAspect.java ================================================ package cn.iocoder.springboot.lab37.loggingdemo.aspect; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component @Aspect public class HttpAccessAspect { private Logger logger = LoggerFactory.getLogger(getClass()); @Around("@within(org.springframework.stereotype.Controller)" + "|| @within(org.springframework.web.bind.annotation.RestController)") public Object around(ProceedingJoinPoint point) throws Throwable { // 获取类名 String className = point.getTarget().getClass().getName(); // 获取方法 String methodName = point.getSignature().getName(); // 记录开始时间 long beginTime = System.currentTimeMillis(); // 记录返回结果 Object result = null; Exception ex = null; try { // 执行方法 result = point.proceed(); return result; } catch (Exception e) { ex = e; throw e; } finally { // 计算消耗时间 long costTime = System.currentTimeMillis() - beginTime; // 发生异常,则打印 ERROR 日志 if (ex != null) { logger.error("[className: {}][methodName: {}][cost: {} ms][args: {}][发生异常]", className, methodName, point.getArgs(), ex); // 正常执行,则打印 INFO 日志 } else { logger.info("[className: {}][methodName: {}][cost: {} ms][args: {}][return: {}]", className, methodName, costTime, point.getArgs(), result); } } } } ================================================ FILE: lab-37/lab-37-logging-aop/src/main/java/cn/iocoder/springboot/lab37/loggingdemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab37.loggingdemo.controller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { private Logger logger = LoggerFactory.getLogger(getClass()); @GetMapping("/debug") public void debug() { logger.debug("debug"); } @GetMapping("/info") public void info() { logger.info("info"); } @GetMapping("/error") public void error() { logger.error("error"); } } ================================================ FILE: lab-37/lab-37-logging-aop/src/main/resources/application.yaml ================================================ spring: application: name: demo-application # 应用名 logging: # 日志文件配置 file: # path: /Users/yunai/logs/ # 日志文件路径。 name: /Users/yunai/logs/${spring.application.name}.log # 日志文件名。 max-history: 7 # 日志文件要保留的归档的最大天数。默认为 7 天。 max-size: 10MB # 日志文件的最大大小。默认为 10MB 。 # 日志级别 level: cn: iocoder: springboot: lab37: loggingdemo: controller: DEBUG ================================================ FILE: lab-37/lab-37-logging-debug/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-37-logging-debug org.springframework.boot spring-boot-starter-web ================================================ FILE: lab-37/lab-37-logging-debug/src/main/java/cn/iocoder/springboot/lab37/loggingdemo/Application.java ================================================ package cn.iocoder.springboot.lab37.loggingdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-37/lab-37-logging-debug/src/main/java/cn/iocoder/springboot/lab37/loggingdemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab37.loggingdemo.controller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { private Logger logger = LoggerFactory.getLogger(getClass()); @GetMapping("/debug") public void debug() { logger.debug("debug"); } @GetMapping("/info") public void info() { logger.info("info"); } @GetMapping("/error") public void error() { logger.error("error"); } } ================================================ FILE: lab-37/lab-37-logging-debug/src/main/resources/application.yaml ================================================ spring: application: name: demo-application # 应用名 logging: # 日志文件配置 file: # path: /Users/yunai/logs/ # 日志文件路径。 name: /Users/yunai/logs/${spring.application.name}.log # 日志文件名。 max-history: 7 # 日志文件要保留的归档的最大天数。默认为 7 天。 max-size: 10MB # 日志文件的最大大小。默认为 10MB 。 # 调试模式 debug: true ================================================ FILE: lab-37/lab-37-logging-demo/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-37-logging-demo org.springframework.boot spring-boot-starter-web ================================================ FILE: lab-37/lab-37-logging-demo/src/main/java/cn/iocoder/springboot/lab37/loggingdemo/Application.java ================================================ package cn.iocoder.springboot.lab37.loggingdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-37/lab-37-logging-demo/src/main/java/cn/iocoder/springboot/lab37/loggingdemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab37.loggingdemo.controller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { private Logger logger = LoggerFactory.getLogger(getClass()); @GetMapping("/debug") public void debug() { logger.debug("debug"); } @GetMapping("/info") public void info() { logger.info("info"); } @GetMapping("/error") public void error() { logger.error("error"); } } ================================================ FILE: lab-37/lab-37-logging-demo/src/main/resources/application.yaml ================================================ spring: application: name: demo-application # 应用名 logging: # 日志文件配置 file: # path: /Users/yunai/logs/ # 日志文件路径。 name: /Users/yunai/logs/${spring.application.name}.log # 日志文件名。 max-history: 7 # 日志文件要保留的归档的最大天数。默认为 7 天。 max-size: 10MB # 日志文件的最大大小。默认为 10MB 。 # 日志级别 level: cn: iocoder: springboot: lab37: loggingdemo: controller: DEBUG ================================================ FILE: lab-37/lab-37-logging-log4j2/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-37-logging-log4j2 org.springframework.boot spring-boot-starter org.springframework.boot spring-boot-starter-logging org.springframework.boot spring-boot-starter-log4j2 org.springframework.boot spring-boot-starter-web ================================================ FILE: lab-37/lab-37-logging-log4j2/src/main/java/cn/iocoder/springboot/lab37/loggingdemo/Application.java ================================================ package cn.iocoder.springboot.lab37.loggingdemo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { private static Logger logger = LoggerFactory.getLogger(Application.class); public static void main(String[] args) { SpringApplication.run(Application.class, args); // 打印日志 logger.debug("just do it"); } } ================================================ FILE: lab-37/lab-37-logging-log4j2/src/main/resources/application.yaml ================================================ spring: application: name: demo-application # 应用名 ================================================ FILE: lab-37/lab-37-logging-log4j2/src/main/resources/log4j2-spring.xml ================================================ ???? %xwEx %5p yyyy-MM-dd HH:mm:ss.SSS /Users/yunai/logs demo-application %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} %d{${LOG_DATEFORMAT_PATTERN}} ${LOG_LEVEL_PATTERN} ${sys:PID} --- [%t] %-40.40c{1.} : %m%n${sys:LOG_EXCEPTION_CONVERSION_WORD} ${sys:FILE_LOG_PATTERN} ================================================ FILE: lab-37/lab-37-logging-logback/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-37-logging-logback org.springframework.boot spring-boot-starter-web ================================================ FILE: lab-37/lab-37-logging-logback/src/main/java/cn/iocoder/springboot/lab37/loggingdemo/Application.java ================================================ package cn.iocoder.springboot.lab37.loggingdemo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { private static Logger logger = LoggerFactory.getLogger(Application.class); public static void main(String[] args) { SpringApplication.run(Application.class, args); // 打印日志 logger.debug("just do it"); } } ================================================ FILE: lab-37/lab-37-logging-logback/src/main/resources/application.yaml ================================================ spring: application: name: demo-application # 应用名 ================================================ FILE: lab-37/lab-37-logging-logback/src/main/resources/logback-spring.xml ================================================ ${CONSOLE_LOG_PATTERN} utf8 ${LOG_FILE} ${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz 7 10MB ${FILE_LOG_PATTERN} utf8 ================================================ FILE: lab-37/lab-37-logging-multi-env/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-37-logging-multi-env org.springframework.boot spring-boot-starter-web ================================================ FILE: lab-37/lab-37-logging-multi-env/src/main/java/cn/iocoder/springboot/lab37/loggingdemo/Application.java ================================================ package cn.iocoder.springboot.lab37.loggingdemo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { private static Logger logger = LoggerFactory.getLogger(Application.class); public static void main(String[] args) { SpringApplication.run(Application.class, args); // 打印日志 logger.debug("just do it"); } } ================================================ FILE: lab-37/lab-37-logging-multi-env/src/main/resources/application-dev.yaml ================================================ spring: application: name: demo-application # 应用名 logging: # 日志级别 level: cn: iocoder: springboot: lab37: loggingdemo: DEBUG ================================================ FILE: lab-37/lab-37-logging-multi-env/src/main/resources/application-prod.yaml ================================================ spring: application: name: demo-application # 应用名 logging: # 日志文件配置 file: name: /Users/yunai/logs/${spring.application.name}.log # 日志文件名。 ================================================ FILE: lab-37/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-37 pom lab-37-logging-demo lab-37-logging-actuator lab-37-logging-logback lab-37-logging-debug lab-37-logging-multi-env lab-37-logging-log4j2 lab-37-logging-aop ================================================ FILE: lab-37/《芋道 Spring Boot 日志集成 Logging 入门》.md ================================================ ================================================ FILE: lab-38/lab-38-elk-demo/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-38-elk-demo org.springframework.boot spring-boot-starter-web ================================================ FILE: lab-38/lab-38-elk-demo/src/main/java/cn/iocoder/springboot/lab38/elkdemo/Application.java ================================================ package cn.iocoder.springboot.lab38.elkdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-38/lab-38-elk-demo/src/main/resources/application.yaml ================================================ spring: application: name: demo-application # 应用名 logging: # 日志文件配置 file: name: /Users/yunai/logs/${spring.application.name}.log # 日志文件名。 ================================================ FILE: lab-38/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-38 pom lab-38-elk-demo ================================================ FILE: lab-38/《芋道 Spring Boot 日志平台 ELK + Filebeat 入门》.md ================================================ ================================================ FILE: lab-39/lab-39-activemq/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-39-activemq org.springframework.boot spring-boot-starter-activemq org.springframework.boot spring-boot-starter-web ================================================ FILE: lab-39/lab-39-activemq/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/ActiveMQApplication.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ActiveMQApplication { public static void main(String[] args) { SpringApplication.run(ActiveMQApplication.class, args); } } ================================================ FILE: lab-39/lab-39-activemq/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/consumer/DemoConsumer.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo.consumer; import cn.iocoder.springboot.lab39.skywalkingdemo.message.DemoMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jms.annotation.JmsListener; import org.springframework.stereotype.Component; @Component public class DemoConsumer { private Logger logger = LoggerFactory.getLogger(getClass()); @JmsListener(destination = DemoMessage.QUEUE) public void onMessage(DemoMessage message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: lab-39/lab-39-activemq/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo.controller; import cn.iocoder.springboot.lab39.skywalkingdemo.producer.DemoProducer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @Autowired private DemoProducer producer; @GetMapping("/activemq") public String echo() { this.sendMessage(1); return "activemq"; } public void sendMessage(Integer id) { producer.syncSend(id); } } ================================================ FILE: lab-39/lab-39-activemq/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/message/DemoMessage.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo.message; import java.io.Serializable; public class DemoMessage implements Serializable { public static final String QUEUE = "QUEUE_DEMO_"; /** * 编号 */ private Integer id; public DemoMessage setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "DemoMessage{" + "id=" + id + '}'; } } ================================================ FILE: lab-39/lab-39-activemq/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/producer/DemoProducer.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo.producer; import cn.iocoder.springboot.lab39.skywalkingdemo.message.DemoMessage; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jms.core.JmsMessagingTemplate; import org.springframework.stereotype.Component; @Component public class DemoProducer { @Autowired private JmsMessagingTemplate jmsTemplate; public void syncSend(Integer id) { // 创建 DemoMessage 消息 DemoMessage message = new DemoMessage(); message.setId(id); // 同步发送消息 jmsTemplate.convertAndSend(DemoMessage.QUEUE, message); } } ================================================ FILE: lab-39/lab-39-activemq/src/main/resources/application.yaml ================================================ server: port: 8079 spring: # ActiveMQ 配置项,对应 ActiveMQProperties 配置类 activemq: broker-url: tcp://127.0.0.1:61616 # Activemq Broker 的地址 user: admin # 账号 password: admin # 密码 packages: trust-all: true # 可信任的反序列化包 ================================================ FILE: lab-39/lab-39-async/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-39-async org.springframework.boot spring-boot-starter-web ================================================ FILE: lab-39/lab-39-async/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/AsyncApplication.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableAsync; @SpringBootApplication @EnableAsync(proxyTargetClass = true) // 开启 @Async 的支持 public class AsyncApplication { public static void main(String[] args) { SpringApplication.run(AsyncApplication.class, args); } } ================================================ FILE: lab-39/lab-39-async/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo.controller; import cn.iocoder.springboot.lab39.skywalkingdemo.service.DemoService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @Autowired private DemoService demoService; @GetMapping("/async") public String echo() { demoService.async(); return "async"; } } ================================================ FILE: lab-39/lab-39-async/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/service/DemoService.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo.service; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; @Service public class DemoService { @Async public void async() { System.out.println("异步任务的执行"); } } ================================================ FILE: lab-39/lab-39-async/src/main/resources/application.yml ================================================ server: port: 8079 ================================================ FILE: lab-39/lab-39-demo/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 jar lab-39-demo org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-maven-plugin true ================================================ FILE: lab-39/lab-39-demo/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/Application.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import java.io.IOException; import java.net.URISyntaxException; @SpringBootApplication public class Application { public static void main(String[] args) throws URISyntaxException, IOException { // ProtectionDomain protectionDomain = Launcher.class.getProtectionDomain(); // CodeSource codeSource = protectionDomain.getCodeSource(); // URI location = (codeSource != null) ? codeSource.getLocation().toURI() : null; // String path = (location != null) ? location.getSchemeSpecificPart() : null; // if (path == null) { // throw new IllegalStateException("Unable to determine code source archive"); // } // File root = new File(path); // System.out.println(root + "" + root.isDirectory()); // // 创建 Archive 对象 // Archive archive = new JarFileArchive(root); // // 创建 EntryFilter 对象 // Archive.EntryFilter filter = new Archive.EntryFilter() { // // static final String BOOT_INF_CLASSES = "BOOT-INF/classes/"; // // static final String BOOT_INF_LIB = "BOOT-INF/lib/"; // // @Override // public boolean matches(Archive.Entry entry) { // // 如果是目录的情况,只要 BOOT-INF/classes/ 目录 // if (entry.isDirectory()) { // return entry.getName().equals(BOOT_INF_CLASSES); // } // // 如果是文件的情况,只要 BOOT-INF/lib/ 目录下的 `jar` 包 // return entry.getName().startsWith(BOOT_INF_LIB); // } // // }; // // 执行读取 // for (Archive item : archive.getNestedArchives(filter)) { // System.out.println(item.getUrl()); // } SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-39/lab-39-demo/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @GetMapping("/echo") public String echo() { return "echo"; } } ================================================ FILE: lab-39/lab-39-demo/src/main/resources/application.yml ================================================ server: port: 8079 ================================================ FILE: lab-39/lab-39-elasticsearch/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-39-elasticsearch org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-data-elasticsearch ================================================ FILE: lab-39/lab-39-elasticsearch/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/ElasticsearchApplication.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ElasticsearchApplication { public static void main(String[] args) { SpringApplication.run(ElasticsearchApplication.class, args); } } ================================================ FILE: lab-39/lab-39-elasticsearch/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo.controller; import cn.iocoder.springboot.lab39.skywalkingdemo.dataobject.ESUserDO; import cn.iocoder.springboot.lab39.skywalkingdemo.repository.ESUserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @Autowired private ESUserRepository userRepository; @GetMapping("/elasticsearch") public String mysql() { this.findById(1); return "elasticsearch"; } public ESUserDO findById(Integer id) { return userRepository.findById(id).orElse(null); } } ================================================ FILE: lab-39/lab-39-elasticsearch/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/dataobject/ESUserDO.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo.dataobject; import org.springframework.data.annotation.Id; import org.springframework.data.elasticsearch.annotations.Document; import java.util.Date; @Document(indexName = "user", // 索引名 type = "user", // 类型。未来的版本即将废弃 shards = 1, // 默认索引分区数 replicas = 0, // 每个分区的备份数 refreshInterval = "-1" // 刷新间隔 ) public class ESUserDO { @Id private Integer id; /** * 账号 */ private String username; /** * 密码 */ private String password; /** * 创建时间 */ private Date createTime; @Override public String toString() { return "UserDO{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", createTime=" + createTime + '}'; } } ================================================ FILE: lab-39/lab-39-elasticsearch/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/repository/ESUserRepository.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo.repository; import cn.iocoder.springboot.lab39.skywalkingdemo.dataobject.ESUserDO; import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; public interface ESUserRepository extends ElasticsearchRepository { } ================================================ FILE: lab-39/lab-39-elasticsearch/src/main/resources/application.yml ================================================ server: port: 8079 spring: data: # Elasticsearch 配置项 elasticsearch: cluster-name: elasticsearch2 # 集群名 cluster-nodes: 127.0.0.1:9301 # 集群节点 ================================================ FILE: lab-39/lab-39-elasticsearch-jest/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-39-elasticsearch-jest org.springframework.boot spring-boot-starter-web com.github.vanroy spring-boot-starter-data-jest 3.3.0.RELEASE ================================================ FILE: lab-39/lab-39-elasticsearch-jest/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/ElasticsearchJestApplication.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration; import org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration; @SpringBootApplication(exclude = {ElasticsearchAutoConfiguration.class, ElasticsearchDataAutoConfiguration.class}) public class ElasticsearchJestApplication { public static void main(String[] args) { SpringApplication.run(ElasticsearchJestApplication.class, args); } } ================================================ FILE: lab-39/lab-39-elasticsearch-jest/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo.controller; import cn.iocoder.springboot.lab39.skywalkingdemo.dataobject.ESUserDO; import cn.iocoder.springboot.lab39.skywalkingdemo.repository.ESUserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Date; @RestController @RequestMapping("/demo") public class DemoController { @Autowired private ESUserRepository userRepository; @GetMapping("/elasticsearch") public String mysql() { this.save(1); this.findById(1); return "elasticsearch"; } public void save(Integer id) { ESUserDO user = new ESUserDO(); user.setId(id); user.setUsername("username:" + id); user.setPassword("password:" + id); user.setCreateTime(new Date()); userRepository.save(user); } public ESUserDO findById(Integer id) { return userRepository.findById(id).orElse(null); } } ================================================ FILE: lab-39/lab-39-elasticsearch-jest/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/dataobject/ESUserDO.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo.dataobject; import org.springframework.data.annotation.Id; import org.springframework.data.elasticsearch.annotations.Document; import java.util.Date; @Document(indexName = "user", // 索引名 type = "user", // 类型。未来的版本即将废弃 shards = 1, // 默认索引分区数 replicas = 0, // 每个分区的备份数 refreshInterval = "-1" // 刷新间隔 ) public class ESUserDO { @Id private Integer id; /** * 账号 */ private String username; /** * 密码 */ private String password; /** * 创建时间 */ private Date createTime; public Integer getId() { return id; } public ESUserDO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public ESUserDO setUsername(String username) { this.username = username; return this; } public String getPassword() { return password; } public ESUserDO setPassword(String password) { this.password = password; return this; } public Date getCreateTime() { return createTime; } public ESUserDO setCreateTime(Date createTime) { this.createTime = createTime; return this; } @Override public String toString() { return "UserDO{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", createTime=" + createTime + '}'; } } ================================================ FILE: lab-39/lab-39-elasticsearch-jest/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/repository/ESUserRepository.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo.repository; import cn.iocoder.springboot.lab39.skywalkingdemo.dataobject.ESUserDO; import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; public interface ESUserRepository extends ElasticsearchRepository { } ================================================ FILE: lab-39/lab-39-elasticsearch-jest/src/main/resources/application.yml ================================================ server: port: 8079 spring: data: # Jest 配置项 jest: uri: http://127.0.0.1:9400 ================================================ FILE: lab-39/lab-39-kafka/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.11.RELEASE 4.0.0 lab-39-kafka org.springframework.kafka spring-kafka 2.2.11.RELEASE org.springframework.boot spring-boot-starter-web ================================================ FILE: lab-39/lab-39-kafka/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/KafkaApplication.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class KafkaApplication { public static void main(String[] args) { SpringApplication.run(KafkaApplication.class, args); } } ================================================ FILE: lab-39/lab-39-kafka/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/consumer/DemoConsumer.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo.consumer; import cn.iocoder.springboot.lab39.skywalkingdemo.message.DemoMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.kafka.annotation.KafkaListener; import org.springframework.stereotype.Component; @Component public class DemoConsumer { private Logger logger = LoggerFactory.getLogger(getClass()); @KafkaListener(topics = DemoMessage.TOPIC, groupId = "demo-consumer-group-" + DemoMessage.TOPIC) public void onMessage(DemoMessage message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: lab-39/lab-39-kafka/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo.controller; import cn.iocoder.springboot.lab39.skywalkingdemo.producer.DemoProducer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.concurrent.ExecutionException; @RestController @RequestMapping("/demo") public class DemoController { @Autowired private DemoProducer producer; @GetMapping("/kafka") public String echo() throws ExecutionException, InterruptedException { this.sendMessage(1); return "kafka"; } public void sendMessage(Integer id) throws ExecutionException, InterruptedException { producer.syncSend(id); } } ================================================ FILE: lab-39/lab-39-kafka/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/message/DemoMessage.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo.message; /** * 示例 Message 消息 */ public class DemoMessage { public static final String TOPIC = "DEMO"; /** * 编号 */ private Integer id; public DemoMessage setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "DemoMessage{" + "id=" + id + '}'; } } ================================================ FILE: lab-39/lab-39-kafka/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/producer/DemoProducer.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo.producer; import cn.iocoder.springboot.lab39.skywalkingdemo.message.DemoMessage; import org.springframework.kafka.core.KafkaTemplate; import org.springframework.kafka.support.SendResult; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.util.concurrent.ExecutionException; @Component public class DemoProducer { @Resource private KafkaTemplate kafkaTemplate; public SendResult syncSend(Integer id) throws ExecutionException, InterruptedException { // 创建 DemoMessage 消息 DemoMessage message = new DemoMessage(); message.setId(id); // 同步发送消息 return kafkaTemplate.send(DemoMessage.TOPIC, message).get(); } } ================================================ FILE: lab-39/lab-39-kafka/src/main/resources/application.yaml ================================================ server: port: 8079 spring: # Kafka 配置项,对应 KafkaProperties 配置类 kafka: bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 # Kafka Producer 配置项 producer: acks: 1 # 0-不应答。1-leader 应答。all-所有 leader 和 follower 应答。 retries: 3 # 发送失败时,重试发送的次数 key-serializer: org.apache.kafka.common.serialization.StringSerializer # 消息的 key 的序列化 value-serializer: org.springframework.kafka.support.serializer.JsonSerializer # 消息的 value 的序列化 # Kafka Consumer 配置项 consumer: auto-offset-reset: earliest # 设置消费者分组最初的消费进度为 earliest 。可参考博客 https://blog.csdn.net/lishuangzhe7047/article/details/74530417 理解 key-deserializer: org.apache.kafka.common.serialization.StringDeserializer value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer properties: spring: json: trusted: packages: cn.iocoder.springboot.lab39.skywalkingdemo.message # 消息 POJO 可信目录,解决 JSON 无法反序列化的问题 # Kafka Consumer Listener 监听器配置 listener: missing-topics-fatal: false # 消费监听接口监听的主题不存在时,默认会报错。所以通过设置为 false ,解决报错 ================================================ FILE: lab-39/lab-39-logback/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-39-logback org.apache.skywalking apm-toolkit-logback-1.x 6.6.0 org.springframework.boot spring-boot-starter-web ================================================ FILE: lab-39/lab-39-logback/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/LogbackApplication.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class LogbackApplication { public static void main(String[] args) { SpringApplication.run(LogbackApplication.class, args); } } ================================================ FILE: lab-39/lab-39-logback/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo.controller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { private Logger logger = LoggerFactory.getLogger(getClass()); @GetMapping("/logback") public String echo() { logger.info("测试日志"); return "logback"; } } ================================================ FILE: lab-39/lab-39-logback/src/main/resources/application.yml ================================================ server: port: 8079 spring: application: name: demo-application-logback ================================================ FILE: lab-39/lab-39-logback/src/main/resources/logback-spring.xml ================================================ ${CONSOLE_LOG_PATTERN} ${LOG_FILE} ${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz 7 10MB ${FILE_LOG_PATTERN} ================================================ FILE: lab-39/lab-39-mongodb/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-39-mongodb org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-data-mongodb ================================================ FILE: lab-39/lab-39-mongodb/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/MongoDBApplication.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class MongoDBApplication { public static void main(String[] args) { SpringApplication.run(MongoDBApplication.class, args); } } ================================================ FILE: lab-39/lab-39-mongodb/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo.controller; import cn.iocoder.springboot.lab39.skywalkingdemo.dataobject.UserDO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @Autowired private MongoTemplate mongoTemplate; @GetMapping("/mongodb") public String mysql() { this.findById(1); return "mongodb"; } public UserDO findById(Integer id) { return mongoTemplate.findOne(new Query(Criteria.where("_id").is(id)), UserDO.class); } } ================================================ FILE: lab-39/lab-39-mongodb/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/dataobject/UserDO.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo.dataobject; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.mapping.Document; import java.util.Date; /** * 用户 DO */ @Document(collection = "User") public class UserDO { @Id private Integer id; /** * 账号 */ private String username; /** * 密码 */ private String password; /** * 创建时间 */ private Date createTime; @Override public String toString() { return "UserDO{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", createTime=" + createTime + '}'; } } ================================================ FILE: lab-39/lab-39-mongodb/src/main/resources/application.yml ================================================ server: port: 8079 spring: data: # MongoDB 配置项,对应 MongoProperties 类 mongodb: host: 127.0.0.1 port: 27017 database: yourdatabase username: test01 password: password01 # 上述属性,也可以只配置 uri ================================================ FILE: lab-39/lab-39-mysql/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-39-mysql org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 5.1.46 ================================================ FILE: lab-39/lab-39-mysql/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/MySQLApplication.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class MySQLApplication { public static void main(String[] args) { SpringApplication.run(MySQLApplication.class, args); } } ================================================ FILE: lab-39/lab-39-mysql/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @Autowired private JdbcTemplate template; @GetMapping("/mysql") public String mysql() { this.selectById(1); return "mysql"; } public Object selectById(Integer id) { return template.queryForObject("SELECT id, username, password FROM t_user WHERE id = ?", new BeanPropertyRowMapper<>(Object.class), // 结果转换成对应的对象。Object 理论来说是 UserDO.class ,这里偷懒了。 id); } } ================================================ FILE: lab-39/lab-39-mysql/src/main/resources/application.yml ================================================ server: port: 8079 spring: # datasource 数据源配置内容 datasource: url: jdbc:mysql://127.0.0.1:3306/lab-39-mysql?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: ================================================ FILE: lab-39/lab-39-opentracing/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-39-opentracing org.apache.skywalking apm-toolkit-opentracing 6.6.0 org.springframework.boot spring-boot-starter-web ================================================ FILE: lab-39/lab-39-opentracing/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/OpentracingApplication.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class OpentracingApplication { public static void main(String[] args) { SpringApplication.run(OpentracingApplication.class, args); } } ================================================ FILE: lab-39/lab-39-opentracing/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo.controller; import io.opentracing.Tracer; import org.apache.skywalking.apm.toolkit.opentracing.SkywalkingTracer; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @GetMapping("/opentracing") public String echo() { // 创建一个 Span Tracer tracer = new SkywalkingTracer(); tracer.buildSpan("custom_operation").withTag("mp", "芋道源码").startManual().finish(); // 返回 return "opentracing"; } } ================================================ FILE: lab-39/lab-39-opentracing/src/main/resources/application.yml ================================================ server: port: 8079 ================================================ FILE: lab-39/lab-39-rabbitmq-demo/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-39-rabbitmq-demo org.springframework.boot spring-boot-starter-amqp org.springframework.boot spring-boot-starter-web ================================================ FILE: lab-39/lab-39-rabbitmq-demo/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/RabbitMQApplication.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class RabbitMQApplication { public static void main(String[] args) { SpringApplication.run(RabbitMQApplication.class, args); } } ================================================ FILE: lab-39/lab-39-rabbitmq-demo/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/config/RabbitConfig.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo.config; import cn.iocoder.springboot.lab39.skywalkingdemo.message.DemoMessage; import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.BindingBuilder; import org.springframework.amqp.core.DirectExchange; import org.springframework.amqp.core.Queue; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class RabbitConfig { // 创建 Queue @Bean public Queue demoQueue() { return new Queue(DemoMessage.QUEUE, // Queue 名字 true, // durable: 是否持久化 false, // exclusive: 是否排它 false); // autoDelete: 是否自动删除 } // 创建 Direct Exchange @Bean public DirectExchange demoExchange() { return new DirectExchange(DemoMessage.EXCHANGE, true, // durable: 是否持久化 false); // exclusive: 是否排它 } // 创建 Binding // Exchange:DemoMessage.EXCHANGE // Routing key:DemoMessage.ROUTING_KEY // Queue:DemoMessage.QUEUE @Bean public Binding demoBinding() { return BindingBuilder.bind(demoQueue()).to(demoExchange()).with(DemoMessage.ROUTING_KEY); } } ================================================ FILE: lab-39/lab-39-rabbitmq-demo/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/consumer/DemoConsumer.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo.consumer; import cn.iocoder.springboot.lab39.skywalkingdemo.message.DemoMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; @Component @RabbitListener(queues = DemoMessage.QUEUE) public class DemoConsumer { private Logger logger = LoggerFactory.getLogger(getClass()); @RabbitHandler public void onMessage(DemoMessage message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: lab-39/lab-39-rabbitmq-demo/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo.controller; import cn.iocoder.springboot.lab39.skywalkingdemo.producer.DemoProducer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @Autowired private DemoProducer producer; @GetMapping("/rabbitmq") public String echo() { this.sendMessage(1); return "rabbitmq"; } public void sendMessage(Integer id) { producer.syncSend(id); } } ================================================ FILE: lab-39/lab-39-rabbitmq-demo/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/message/DemoMessage.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo.message; import java.io.Serializable; public class DemoMessage implements Serializable { public static final String QUEUE = "QUEUE_DEMO_"; public static final String EXCHANGE = "EXCHANGE_DEMO_"; public static final String ROUTING_KEY = "ROUTING_KEY_"; /** * 编号 */ private Integer id; public DemoMessage setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "DemoMessage{" + "id=" + id + '}'; } } ================================================ FILE: lab-39/lab-39-rabbitmq-demo/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/producer/DemoProducer.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo.producer; import cn.iocoder.springboot.lab39.skywalkingdemo.message.DemoMessage; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class DemoProducer { @Autowired private RabbitTemplate rabbitTemplate; public void syncSend(Integer id) { // 创建 DemoMessage 消息 DemoMessage message = new DemoMessage(); message.setId(id); // 同步发送消息 rabbitTemplate.convertAndSend(DemoMessage.EXCHANGE, DemoMessage.ROUTING_KEY, message); } } ================================================ FILE: lab-39/lab-39-rabbitmq-demo/src/main/resources/application.yaml ================================================ server: port: 8079 spring: # RabbitMQ 配置项,对应 RabbitProperties 配置类 rabbitmq: host: 127.0.0.1 # RabbitMQ 服务的地址 port: 5672 # RabbitMQ 服务的端口 username: guest # RabbitMQ 服务的账号 password: guest # RabbitMQ 服务的密码 ================================================ FILE: lab-39/lab-39-redis/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-39-redis org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-data-redis io.lettuce lettuce-core redis.clients jedis ================================================ FILE: lab-39/lab-39-redis/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/RedisApplication.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class RedisApplication { public static void main(String[] args) { SpringApplication.run(RedisApplication.class, args); } } ================================================ FILE: lab-39/lab-39-redis/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @Autowired private StringRedisTemplate redisTemplate; @GetMapping("/redis") public String redis() { this.get("demo"); return "redis"; } public void get(String key) { redisTemplate.opsForValue().get(key); } } ================================================ FILE: lab-39/lab-39-redis/src/main/resources/application.yml ================================================ server: port: 8079 spring: # 对应 RedisProperties 类 redis: host: 127.0.0.1 port: 6379 password: # Redis 服务器密码,默认为空。生产中,一定要设置 Redis 密码! database: 0 # Redis 数据库号,默认为 0 。 timeout: 0 # Redis 连接超时时间,单位:毫秒。 # 对应 RedisProperties.Jedis 内部类 jedis: pool: max-active: 8 # 连接池最大连接数,默认为 8 。使用负数表示没有限制。 max-idle: 8 # 默认连接数最小空闲的连接数,默认为 8 。使用负数表示没有限制。 min-idle: 0 # 默认连接池最小空闲的连接数,默认为 0 。允许设置 0 和 正数。 max-wait: -1 # 连接池最大阻塞等待时间,单位:毫秒。默认为 -1 ,表示不限制。 ================================================ FILE: lab-39/lab-39-rocketmq/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-39-rocketmq org.springframework.boot spring-boot-starter-web org.apache.rocketmq rocketmq-spring-boot-starter 2.0.4 ================================================ FILE: lab-39/lab-39-rocketmq/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/rocketmqdemo/RocketMQApplication.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo.rocketmqdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class RocketMQApplication { public static void main(String[] args) { SpringApplication.run(RocketMQApplication.class, args); } } ================================================ FILE: lab-39/lab-39-rocketmq/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/rocketmqdemo/consumer/DemoConsumer.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo.rocketmqdemo.consumer; import cn.iocoder.springboot.lab39.skywalkingdemo.rocketmqdemo.message.DemoMessage; import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; import org.apache.rocketmq.spring.core.RocketMQListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component @RocketMQMessageListener( topic = DemoMessage.TOPIC, consumerGroup = "demo-consumer-group-" + DemoMessage.TOPIC ) public class DemoConsumer implements RocketMQListener { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public void onMessage(DemoMessage message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: lab-39/lab-39-rocketmq/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/rocketmqdemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo.rocketmqdemo.controller; import cn.iocoder.springboot.lab39.skywalkingdemo.rocketmqdemo.producer.DemoProducer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @Autowired private DemoProducer producer; @GetMapping("/rocketmq") public String echo() { this.sendMessage(1); return "rocketmq"; } public void sendMessage(Integer id) { producer.syncSend(id); } } ================================================ FILE: lab-39/lab-39-rocketmq/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/rocketmqdemo/message/DemoMessage.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo.rocketmqdemo.message; /** * 示例的 Message 消息 */ public class DemoMessage { public static final String TOPIC = "DEMO"; /** * 编号 */ private Integer id; public DemoMessage setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "DemoMessage{" + "id=" + id + '}'; } } ================================================ FILE: lab-39/lab-39-rocketmq/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/rocketmqdemo/producer/DemoProducer.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo.rocketmqdemo.producer; import cn.iocoder.springboot.lab39.skywalkingdemo.rocketmqdemo.message.DemoMessage; import org.apache.rocketmq.client.producer.SendResult; import org.apache.rocketmq.spring.core.RocketMQTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class DemoProducer { @Autowired private RocketMQTemplate rocketMQTemplate; public SendResult syncSend(Integer id) { // 创建 DemoMessage 消息 DemoMessage message = new DemoMessage(); message.setId(id); // 同步发送消息 return rocketMQTemplate.syncSend(DemoMessage.TOPIC, message); } } ================================================ FILE: lab-39/lab-39-rocketmq/src/main/resources/application.yaml ================================================ server: port: 8079 # rocketmq 配置项,对应 RocketMQProperties 配置类 rocketmq: name-server: 127.0.0.1:9876 # RocketMQ Namesrv # Producer 配置项 producer: group: demo-producer-group # 生产者分组 send-message-timeout: 3000 # 发送消息超时时间,单位:毫秒。默认为 3000 。 ================================================ FILE: lab-39/lab-39-skywalking-dubbo/lab-39-skywalking-dubbo-api/pom.xml ================================================ lab-39-skywalking-dubbo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-39-skywalking-dubbo-api ================================================ FILE: lab-39/lab-39-skywalking-dubbo/lab-39-skywalking-dubbo-api/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/api/UserService.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo.api; /** * 用户服务 RPC Service 接口 */ public interface UserService { /** * 根据指定用户编号,获得用户信息 * * @param id 用户编号 * @return 用户信息 */ String get(Integer id); } ================================================ FILE: lab-39/lab-39-skywalking-dubbo/lab-39-skywalking-dubbo-api/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/package-info.java ================================================ package cn.iocoder.springcloud.labx13; ================================================ FILE: lab-39/lab-39-skywalking-dubbo/lab-39-skywalking-dubbo-consumer/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-39-skywalking-dubbo-consumer cn.iocoder.springboot.labs lab-39-skywalking-dubbo-api 1.0-SNAPSHOT org.springframework.boot spring-boot-starter-web org.apache.dubbo dubbo 2.7.4.1 org.apache.dubbo dubbo-spring-boot-starter 2.7.4.1 org.apache.curator curator-framework 2.13.0 org.apache.curator curator-recipes 2.13.0 ================================================ FILE: lab-39/lab-39-skywalking-dubbo/lab-39-skywalking-dubbo-consumer/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/consumerdemo/ConsumerApplication.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo.consumerdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class); } } ================================================ FILE: lab-39/lab-39-skywalking-dubbo/lab-39-skywalking-dubbo-consumer/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/consumerdemo/controller/UserController.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo.consumerdemo.controller; import cn.iocoder.springboot.lab39.skywalkingdemo.api.UserService; import org.apache.dubbo.config.annotation.Reference; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/user") public class UserController { @Reference(protocol = "dubbo", version = "1.0.0") private UserService userService; @GetMapping("/get") public String get(@RequestParam("id") Integer id) { return userService.get(id); } } ================================================ FILE: lab-39/lab-39-skywalking-dubbo/lab-39-skywalking-dubbo-consumer/src/main/resources/application.yaml ================================================ server: port: 8079 spring: application: name: user-service-consumer # dubbo 配置项,对应 DubboConfigurationProperties 配置类 dubbo: # Dubbo 应用配置 application: name: ${spring.application.name} # 应用名 # Dubbo 注册中心配置 registry: address: zookeeper://127.0.0.1:2181 # 注册中心地址。个鞥多注册中心,可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。 ================================================ FILE: lab-39/lab-39-skywalking-dubbo/lab-39-skywalking-dubbo-provider/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-39-skywalking-dubbo-provider cn.iocoder.springboot.labs lab-39-skywalking-dubbo-api 1.0-SNAPSHOT org.springframework.boot spring-boot-starter org.apache.dubbo dubbo 2.7.4.1 org.apache.dubbo dubbo-spring-boot-starter 2.7.4.1 org.apache.curator curator-framework 2.13.0 org.apache.curator curator-recipes 2.13.0 ================================================ FILE: lab-39/lab-39-skywalking-dubbo/lab-39-skywalking-dubbo-provider/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/providerdemo/ProviderApplication.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo.providerdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ProviderApplication { public static void main(String[] args) { SpringApplication.run(ProviderApplication.class); } } ================================================ FILE: lab-39/lab-39-skywalking-dubbo/lab-39-skywalking-dubbo-provider/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/providerdemo/service/UserServiceImpl.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo.providerdemo.service; import cn.iocoder.springboot.lab39.skywalkingdemo.api.UserService; @org.apache.dubbo.config.annotation.Service(version = "1.0.0") public class UserServiceImpl implements UserService { @Override public String get(Integer id) { return "user:" + id; } } ================================================ FILE: lab-39/lab-39-skywalking-dubbo/lab-39-skywalking-dubbo-provider/src/main/resources/application.yaml ================================================ spring: application: name: user-service-provider # dubbo 配置项,对应 DubboConfigurationProperties 配置类 dubbo: # Dubbo 应用配置 application: name: ${spring.application.name} # 应用名 # Dubbo 注册中心配 registry: address: zookeeper://127.0.0.1:2181 # 注册中心地址。个鞥多注册中心,可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。 # Dubbo 服务提供者协议配置 protocol: port: -1 # 协议端口。使用 -1 表示随机端口。 name: dubbo # 使用 `dubbo://` 协议。更多协议,可见 http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html 文档 # 配置扫描 Dubbo 自定义的 @Service 注解,暴露成 Dubbo 服务提供者 scan: base-packages: cn.iocoder.springboot.lab39.skywalkingdemo.providerdemo.service ================================================ FILE: lab-39/lab-39-skywalking-dubbo/pom.xml ================================================ lab-39 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-39-skywalking-dubbo pom lab-39-skywalking-dubbo-api lab-39-skywalking-dubbo-provider lab-39-skywalking-dubbo-consumer ================================================ FILE: lab-39/lab-39-springmvc/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-39-springmvc org.springframework.boot spring-boot-starter-web ================================================ FILE: lab-39/lab-39-springmvc/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/Application.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-39/lab-39-springmvc/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @GetMapping("/echo") public String echo() { return "echo"; } @GetMapping("/hello") public String hello() { return "hello"; } } ================================================ FILE: lab-39/lab-39-springmvc/src/main/resources/application.yml ================================================ server: port: 8079 ================================================ FILE: lab-39/lab-39-trace-annotations/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-39-trace-annotations org.apache.skywalking apm-toolkit-trace 6.6.0 org.springframework.boot spring-boot-starter-web ================================================ FILE: lab-39/lab-39-trace-annotations/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/TraceAnnotationsApplication.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class TraceAnnotationsApplication { public static void main(String[] args) { SpringApplication.run(TraceAnnotationsApplication.class, args); } } ================================================ FILE: lab-39/lab-39-trace-annotations/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab39.skywalkingdemo.controller; import org.apache.skywalking.apm.toolkit.trace.ActiveSpan; import org.apache.skywalking.apm.toolkit.trace.Trace; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @GetMapping("/trace_annotations") @Trace(operationName = "trace_annotations") public String echo() { // 自定义 SkyWalking Span ActiveSpan.tag("mp", "芋道源码"); // 返回 return "trace_annotations"; } } ================================================ FILE: lab-39/lab-39-trace-annotations/src/main/resources/application.yml ================================================ server: port: 8079 ================================================ FILE: lab-39/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-39 pom lab-39-demo lab-39-springmvc lab-39-mysql lab-39-redis lab-39-mongodb lab-39-elasticsearch lab-39-elasticsearch-jest lab-39-rocketmq lab-39-kafka lab-39-rabbitmq-demo lab-39-activemq lab-39-logback lab-39-trace-annotations lab-39-opentracing lab-39-async lab-39-skywalking-dubbo ================================================ FILE: lab-39/《芋道 Spring Boot 链路追踪 SkyWalking 入门》.md ================================================ ================================================ FILE: lab-40/lab-40-activemq/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-40-activemq org.springframework.boot spring-boot-starter-activemq org.springframework.boot spring-boot-starter-web io.zipkin.brave brave io.zipkin.reporter2 zipkin-sender-okhttp3 io.zipkin.brave brave-instrumentation-spring-webmvc io.zipkin.brave brave-instrumentation-jms io.zipkin.brave brave-bom 5.9.1 pom import ================================================ FILE: lab-40/lab-40-activemq/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/ActiveMQApplication.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ActiveMQApplication { public static void main(String[] args) { SpringApplication.run(ActiveMQApplication.class, args); } } ================================================ FILE: lab-40/lab-40-activemq/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/config/SpringMvcConfiguration.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.config; import brave.spring.webmvc.SpanCustomizingAsyncHandlerInterceptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration @Import(SpanCustomizingAsyncHandlerInterceptor.class) // 创建拦截器 SpanCustomizingAsyncHandlerInterceptor Bean public class SpringMvcConfiguration implements WebMvcConfigurer { @Autowired public SpanCustomizingAsyncHandlerInterceptor webMvcTracingCustomizer; /** * Decorates server spans with application-defined web tags */ @Override public void addInterceptors(InterceptorRegistry registry) { // 记录 SpringMVC 相关信息到 Span 中 registry.addInterceptor(webMvcTracingCustomizer); } } ================================================ FILE: lab-40/lab-40-activemq/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/config/ZipkinConfiguration.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.config; import brave.CurrentSpanCustomizer; import brave.SpanCustomizer; import brave.Tracing; import brave.http.HttpTracing; import brave.jms.JmsTracing; import brave.servlet.TracingFilter; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import zipkin2.Span; import zipkin2.reporter.AsyncReporter; import zipkin2.reporter.Sender; import zipkin2.reporter.okhttp3.OkHttpSender; import javax.jms.ConnectionFactory; import javax.servlet.Filter; @Configuration public class ZipkinConfiguration { // ==================== 通用配置 ==================== /** * Configuration for how to send spans to Zipkin */ @Bean public Sender sender() { // Sender 采用 HTTP 通信方式 return OkHttpSender.create("http://127.0.0.1:9411/api/v2/spans"); } /** * Configuration for how to buffer spans into messages for Zipkin */ @Bean public AsyncReporter spanReporter() { // 异步 Reporter return AsyncReporter.create(sender()); } /** * Controls aspects of tracing such as the service name that shows up in the UI */ @Bean public Tracing tracing(@Value("${spring.application.name}") String serviceName) { return Tracing.newBuilder() .localServiceName(serviceName) // 应用名 .spanReporter(this.spanReporter()).build(); } /** * Allows someone to add tags to a span if a trace is in progress */ @Bean public SpanCustomizer spanCustomizer(Tracing tracing) { return CurrentSpanCustomizer.create(tracing); } // ==================== HTTP 相关 ==================== /** * Decides how to name and tag spans. By default they are named the same as the http method */ @Bean public HttpTracing httpTracing(Tracing tracing) { return HttpTracing.create(tracing); } /** * Creates server spans for http requests */ @Bean public Filter tracingFilter(HttpTracing httpTracing) { // 拦截请求,记录 HTTP 请求的链路信息 return TracingFilter.create(httpTracing); } // ==================== SpringMVC 相关 ==================== // @see SpringMvcConfiguration 类上的,@Import(SpanCustomizingAsyncHandlerInterceptor.class) 。因为 SpanCustomizingAsyncHandlerInterceptor 未提供 public 构造方法 // ==================== RabbitMQ 相关 ==================== @Bean public JmsTracing jmsTracing(Tracing tracing) { return JmsTracing.newBuilder(tracing) .remoteServiceName("demo-mq-activemq") // 远程 ActiveMQ 服务名,可自定义 .build(); } @Bean public BeanPostProcessor activeMQBeanPostProcessor(JmsTracing jmsTracing) { return new BeanPostProcessor() { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { // 如果是 ConnectionFactory ,针对 ActiveMQ Producer 和 Consumer if (bean instanceof ConnectionFactory) { return jmsTracing.connectionFactory((ConnectionFactory) bean); } return bean; } }; } } ================================================ FILE: lab-40/lab-40-activemq/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/consumer/DemoConsumer.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.consumer; import cn.iocoder.springboot.lab40.zipkindemo.message.DemoMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jms.annotation.JmsListener; import org.springframework.stereotype.Component; @Component public class DemoConsumer { private Logger logger = LoggerFactory.getLogger(getClass()); @JmsListener(destination = DemoMessage.QUEUE) public void onMessage(DemoMessage message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: lab-40/lab-40-activemq/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.controller; import cn.iocoder.springboot.lab40.zipkindemo.producer.DemoProducer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @Autowired private DemoProducer producer; @GetMapping("/activemq") public String echo() { this.sendMessage(1); return "activemq"; } public void sendMessage(Integer id) { producer.syncSend(id); } } ================================================ FILE: lab-40/lab-40-activemq/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/message/DemoMessage.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.message; import java.io.Serializable; public class DemoMessage implements Serializable { public static final String QUEUE = "QUEUE_DEMO_"; /** * 编号 */ private Integer id; public DemoMessage setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "DemoMessage{" + "id=" + id + '}'; } } ================================================ FILE: lab-40/lab-40-activemq/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/producer/DemoProducer.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.producer; import cn.iocoder.springboot.lab40.zipkindemo.message.DemoMessage; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jms.core.JmsMessagingTemplate; import org.springframework.stereotype.Component; @Component public class DemoProducer { @Autowired private JmsMessagingTemplate jmsTemplate; public void syncSend(Integer id) { // 创建 DemoMessage 消息 DemoMessage message = new DemoMessage(); message.setId(id); // 同步发送消息 jmsTemplate.convertAndSend(DemoMessage.QUEUE, message); } } ================================================ FILE: lab-40/lab-40-activemq/src/main/resources/application.yaml ================================================ spring: application: name: demo-application-activemq # ActiveMQ 配置项,对应 ActiveMQProperties 配置类 activemq: broker-url: tcp://127.0.0.1:61616 # Activemq Broker 的地址 user: admin # 账号 password: admin # 密码 packages: trust-all: true # 可信任的反序列化包 ================================================ FILE: lab-40/lab-40-demo/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-40-demo jar org.springframework.boot spring-boot-starter-web org.apache.httpcomponents httpclient io.zipkin.brave brave io.zipkin.reporter2 zipkin-sender-okhttp3 io.zipkin.brave brave-instrumentation-spring-webmvc io.zipkin.brave brave-instrumentation-httpclient io.zipkin.brave brave-context-slf4j io.zipkin.brave brave-bom 5.9.1 pom import org.springframework.boot spring-boot-maven-plugin true ================================================ FILE: lab-40/lab-40-demo/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/Application.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-40/lab-40-demo/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/Application2.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo; import org.springframework.boot.SpringApplication; //@SpringBootApplication public class Application2 { public static void main(String[] args) { System.setProperty("spring.application.name", "demo-application-02"); System.setProperty("server.port", "8079"); SpringApplication.run(Application2.class, args); } } ================================================ FILE: lab-40/lab-40-demo/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/config/SpringMvcConfiguration.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.config; import brave.spring.webmvc.SpanCustomizingAsyncHandlerInterceptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration @Import(SpanCustomizingAsyncHandlerInterceptor.class) public class SpringMvcConfiguration implements WebMvcConfigurer { @Autowired public SpanCustomizingAsyncHandlerInterceptor webMvcTracingCustomizer; /** * Decorates server spans with application-defined web tags */ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(webMvcTracingCustomizer); } } ================================================ FILE: lab-40/lab-40-demo/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/config/ZipkinConfiguration.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.config; import brave.CurrentSpanCustomizer; import brave.SpanCustomizer; import brave.Tracing; import brave.http.HttpTracing; import brave.httpclient.TracingHttpClientBuilder; import brave.servlet.TracingFilter; import org.apache.http.impl.client.CloseableHttpClient; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.web.client.RestTemplateCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.web.client.RestTemplate; import zipkin2.Span; import zipkin2.reporter.AsyncReporter; import zipkin2.reporter.Sender; import zipkin2.reporter.okhttp3.OkHttpSender; import javax.servlet.Filter; @Configuration public class ZipkinConfiguration { // ==================== 通用配置 ==================== /** * Configuration for how to send spans to Zipkin */ @Bean public Sender sender() { return OkHttpSender.create("http://127.0.0.1:9411/api/v2/spans"); } /** * Configuration for how to buffer spans into messages for Zipkin */ @Bean public AsyncReporter spanReporter() { return AsyncReporter.create(sender()); } /** * Controls aspects of tracing such as the service name that shows up in the UI */ @Bean public Tracing tracing(@Value("${spring.application.name}") String serviceName) { return Tracing.newBuilder() .localServiceName(serviceName) // .currentTraceContext(ThreadLocalCurrentTraceContext.newBuilder() // .addScopeDecorator(MDCScopeDecorator.create()) // puts trace IDs into logs // .build() // ) .spanReporter(spanReporter()).build(); } /** * Allows someone to add tags to a span if a trace is in progress */ @Bean public SpanCustomizer spanCustomizer(Tracing tracing) { return CurrentSpanCustomizer.create(tracing); } // ==================== HTTP 相关 ==================== /** * Decides how to name and tag spans. By default they are named the same as the http method */ @Bean public HttpTracing httpTracing(Tracing tracing) { return HttpTracing.create(tracing); } /** * Creates server spans for http requests */ @Bean public Filter tracingFilter(HttpTracing httpTracing) { return TracingFilter.create(httpTracing); } // ==================== SpringMVC 相关 ==================== // @see SpringMvcConfiguration 类上的,@Import(SpanCustomizingAsyncHandlerInterceptor.class) // ==================== HttpClient 相关 ==================== @Bean public RestTemplateCustomizer useTracedHttpClient(HttpTracing httpTracing) { // 创建 CloseableHttpClient 对象,内置 HttpTracing 进行 HTTP 链路追踪。 final CloseableHttpClient httpClient = TracingHttpClientBuilder.create(httpTracing).build(); // 创建 RestTemplateCustomizer 对象 return new RestTemplateCustomizer() { @Override public void customize(RestTemplate restTemplate) { restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(httpClient)); } }; } } ================================================ FILE: lab-40/lab-40-demo/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @RestController @RequestMapping("/demo") public class DemoController { private final RestTemplate restTemplate; DemoController(@Autowired RestTemplateBuilder restTemplateBuilder) { this.restTemplate = restTemplateBuilder.build(); } @GetMapping("/echo") public String echo() { return "echo"; } @GetMapping("/http") public String http() { // restTemplate.getForObject("https://www.baidu.com", String.class); restTemplate.getForObject("http://127.0.0.1:8079/demo/echo", String.class); return "echo"; } } ================================================ FILE: lab-40/lab-40-demo/src/main/resources/application.yaml ================================================ spring: application: name: demo-application ================================================ FILE: lab-40/lab-40-elasticsearch/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-40-elasticsearch org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-data-elasticsearch io.zipkin.brave brave io.zipkin.reporter2 zipkin-sender-okhttp3 io.zipkin.brave brave-instrumentation-spring-webmvc io.opentracing.brave brave-opentracing 0.35.0 io.opentracing.contrib opentracing-elasticsearch6-client 0.1.6 io.zipkin.brave brave-bom 5.9.1 pom import ================================================ FILE: lab-40/lab-40-elasticsearch/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/ElasticsearchApplication.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ElasticsearchApplication { public static void main(String[] args) { SpringApplication.run(ElasticsearchApplication.class, args); } } ================================================ FILE: lab-40/lab-40-elasticsearch/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/config/SpringMvcConfiguration.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.config; import brave.spring.webmvc.SpanCustomizingAsyncHandlerInterceptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration @Import(SpanCustomizingAsyncHandlerInterceptor.class) public class SpringMvcConfiguration implements WebMvcConfigurer { @Autowired public SpanCustomizingAsyncHandlerInterceptor webMvcTracingCustomizer; /** * Decorates server spans with application-defined web tags */ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(webMvcTracingCustomizer); } } ================================================ FILE: lab-40/lab-40-elasticsearch/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/config/ZipkinConfiguration.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.config; import brave.CurrentSpanCustomizer; import brave.SpanCustomizer; import brave.Tracing; import brave.http.HttpTracing; import brave.opentracing.BraveTracer; import brave.servlet.TracingFilter; import cn.iocoder.springboot.lab40.zipkindemo.spring.TracingTransportClientFactoryBean; import io.opentracing.Tracer; import org.elasticsearch.client.transport.TransportClient; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import zipkin2.Span; import zipkin2.reporter.AsyncReporter; import zipkin2.reporter.Sender; import zipkin2.reporter.okhttp3.OkHttpSender; import javax.servlet.Filter; import java.util.Properties; @Configuration public class ZipkinConfiguration { // ==================== 通用配置 ==================== /** * Configuration for how to send spans to Zipkin */ @Bean public Sender sender() { return OkHttpSender.create("http://127.0.0.1:9411/api/v2/spans"); } /** * Configuration for how to buffer spans into messages for Zipkin */ @Bean public AsyncReporter spanReporter() { return AsyncReporter.create(sender()); } /** * Controls aspects of tracing such as the service name that shows up in the UI */ @Bean public Tracing tracing(@Value("${spring.application.name}") String serviceName) { return Tracing.newBuilder() .localServiceName(serviceName) .spanReporter(spanReporter()).build(); } @Bean public Tracer openTracer(Tracing tracing) { return BraveTracer.create(tracing); } /** * Allows someone to add tags to a span if a trace is in progress */ @Bean public SpanCustomizer spanCustomizer(Tracing tracing) { return CurrentSpanCustomizer.create(tracing); } // ==================== HTTP 相关 ==================== /** * Decides how to name and tag spans. By default they are named the same as the http method */ @Bean public HttpTracing httpTracing(Tracing tracing) { return HttpTracing.create(tracing); } /** * Creates server spans for http requests */ @Bean public Filter tracingFilter(HttpTracing httpTracing) { return TracingFilter.create(httpTracing); } // ==================== SpringMVC 相关 ==================== // @see SpringMvcConfiguration 类上的,@Import(SpanCustomizingAsyncHandlerInterceptor.class) // ==================== Elasticsearch 相关 ==================== @Bean public TransportClient elasticsearchClient(Tracer tracer, ElasticsearchProperties elasticsearchProperties) throws Exception { // 创建 TracingTransportClientFactoryBean 对象 TracingTransportClientFactoryBean factory = new TracingTransportClientFactoryBean(tracer); // 设置其属性 factory.setClusterNodes(elasticsearchProperties.getClusterNodes()); factory.setProperties(this.createElasticsearch(elasticsearchProperties)); // 创建 TransportClient 对象,并返回 factory.afterPropertiesSet(); return factory.getObject(); } private Properties createElasticsearch(ElasticsearchProperties elasticsearchProperties) { Properties properties = new Properties(); properties.put("cluster.name", elasticsearchProperties.getClusterName()); properties.putAll(elasticsearchProperties.getProperties()); return properties; } } ================================================ FILE: lab-40/lab-40-elasticsearch/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.controller; import cn.iocoder.springboot.lab40.zipkindemo.dataobject.ESUserDO; import cn.iocoder.springboot.lab40.zipkindemo.repository.ESUserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @Autowired private ESUserRepository userRepository; @GetMapping("/elasticsearch") public String mysql() { this.findById(1); return "elasticsearch"; } public ESUserDO findById(Integer id) { return userRepository.findById(id).orElse(null); } } ================================================ FILE: lab-40/lab-40-elasticsearch/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/dataobject/ESUserDO.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.dataobject; import org.springframework.data.annotation.Id; import org.springframework.data.elasticsearch.annotations.Document; import java.util.Date; @Document(indexName = "user", // 索引名 type = "user", // 类型。未来的版本即将废弃 shards = 1, // 默认索引分区数 replicas = 0, // 每个分区的备份数 refreshInterval = "-1" // 刷新间隔 ) public class ESUserDO { @Id private Integer id; /** * 账号 */ private String username; /** * 密码 */ private String password; /** * 创建时间 */ private Date createTime; @Override public String toString() { return "UserDO{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", createTime=" + createTime + '}'; } } ================================================ FILE: lab-40/lab-40-elasticsearch/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/repository/ESUserRepository.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.repository; import cn.iocoder.springboot.lab40.zipkindemo.dataobject.ESUserDO; import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; public interface ESUserRepository extends ElasticsearchRepository { } ================================================ FILE: lab-40/lab-40-elasticsearch/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/spring/ClusterNodes.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.spring; import org.elasticsearch.common.transport.TransportAddress; import org.springframework.data.util.Streamable; import org.springframework.util.Assert; import org.springframework.util.StringUtils; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.stream.Collectors; class ClusterNodes implements Streamable { public static ClusterNodes DEFAULT = ClusterNodes.of("127.0.0.1:9300"); private static final String COLON = ":"; private static final String COMMA = ","; private final List clusterNodes; /** * Creates a new {@link ClusterNodes} by parsing the given source. * * @param source must not be {@literal null} or empty. */ private ClusterNodes(String source) { Assert.hasText(source, "Cluster nodes source must not be null or empty!"); String[] nodes = StringUtils.delimitedListToStringArray(source, COMMA); this.clusterNodes = Arrays.stream(nodes).map(node -> { String[] segments = StringUtils.delimitedListToStringArray(node, COLON); Assert.isTrue(segments.length == 2, () -> String.format("Invalid cluster node %s in %s! Must be in the format host:port!", node, source)); String host = segments[0].trim(); String port = segments[1].trim(); Assert.hasText(host, () -> String.format("No host name given cluster node %s!", node)); Assert.hasText(port, () -> String.format("No port given in cluster node %s!", node)); return new TransportAddress(toInetAddress(host), Integer.valueOf(port)); }).collect(Collectors.toList()); } /** * Creates a new {@link ClusterNodes} by parsing the given source. The expected format is a comma separated list of * host-port-combinations separated by a colon: {@code host:port,host:port,…}. * * @param source must not be {@literal null} or empty. * @return */ public static ClusterNodes of(String source) { return new ClusterNodes(source); } /* * (non-Javadoc) * @see java.lang.Iterable#iterator() */ @Override public Iterator iterator() { return clusterNodes.iterator(); } private static InetAddress toInetAddress(String host) { try { return InetAddress.getByName(host); } catch (UnknownHostException o_O) { throw new IllegalArgumentException(o_O); } } } ================================================ FILE: lab-40/lab-40-elasticsearch/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/spring/TracingTransportClientFactoryBean.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.spring; import io.opentracing.Tracer; import io.opentracing.contrib.elasticsearch6.TracingPreBuiltTransportClient; import org.elasticsearch.client.transport.TransportClient; import org.elasticsearch.common.settings.Settings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.data.elasticsearch.client.TransportClientFactoryBean; import java.util.Properties; /** * 参考 {@link TransportClientFactoryBean} 来实现。 */ public class TracingTransportClientFactoryBean implements FactoryBean, InitializingBean, DisposableBean { private static final Logger logger = LoggerFactory.getLogger(TransportClientFactoryBean.class); private ClusterNodes clusterNodes = ClusterNodes.of("127.0.0.1:9300"); private String clusterName = "elasticsearch"; private Boolean clientTransportSniff = true; private Boolean clientIgnoreClusterName = Boolean.FALSE; private String clientPingTimeout = "5s"; private String clientNodesSamplerInterval = "5s"; private TransportClient client; private Properties properties; private Tracer tracer; public TracingTransportClientFactoryBean(Tracer tracer) { this.tracer = tracer; } @Override public void destroy() throws Exception { try { logger.info("Closing elasticSearch client"); if (client != null) { client.close(); } } catch (final Exception e) { logger.error("Error closing ElasticSearch client: ", e); } } @Override public TransportClient getObject() throws Exception { return client; } @Override public Class getObjectType() { return TransportClient.class; } @Override public boolean isSingleton() { return true; } @Override public void afterPropertiesSet() throws Exception { buildClient(); } protected void buildClient() throws Exception { // 创建可追踪的 TracingPreBuiltTransportClient client = new TracingPreBuiltTransportClient(tracer, settings()); clusterNodes.stream() // .peek(it -> logger.info("Adding transport node : " + it.toString())) // .forEach(client::addTransportAddress); client.connectedNodes(); } private Settings settings() { if (properties != null) { Settings.Builder builder = Settings.builder(); properties.forEach((key, value) -> { builder.put(key.toString(), value.toString()); }); return builder.build(); } return Settings.builder() .put("cluster.name", clusterName) .put("client.transport.sniff", clientTransportSniff) .put("client.transport.ignore_cluster_name", clientIgnoreClusterName) .put("client.transport.ping_timeout", clientPingTimeout) .put("client.transport.nodes_sampler_interval", clientNodesSamplerInterval) .build(); } public void setClusterNodes(String clusterNodes) { this.clusterNodes = ClusterNodes.of(clusterNodes); } public void setClusterName(String clusterName) { this.clusterName = clusterName; } public void setClientTransportSniff(Boolean clientTransportSniff) { this.clientTransportSniff = clientTransportSniff; } public String getClientNodesSamplerInterval() { return clientNodesSamplerInterval; } public void setClientNodesSamplerInterval(String clientNodesSamplerInterval) { this.clientNodesSamplerInterval = clientNodesSamplerInterval; } public String getClientPingTimeout() { return clientPingTimeout; } public void setClientPingTimeout(String clientPingTimeout) { this.clientPingTimeout = clientPingTimeout; } public Boolean getClientIgnoreClusterName() { return clientIgnoreClusterName; } public void setClientIgnoreClusterName(Boolean clientIgnoreClusterName) { this.clientIgnoreClusterName = clientIgnoreClusterName; } public void setProperties(Properties properties) { this.properties = properties; } } ================================================ FILE: lab-40/lab-40-elasticsearch/src/main/resources/application.yml ================================================ spring: application: name: demo-application-elasticsearch data: # Elasticsearch 配置项 elasticsearch: cluster-name: elasticsearch # 集群名 cluster-nodes: 127.0.0.1:9300 # 集群节点 ================================================ FILE: lab-40/lab-40-kafka/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.1.11.RELEASE 4.0.0 lab-40-kafka org.springframework.kafka spring-kafka 2.2.11.RELEASE org.springframework.boot spring-boot-starter-web io.zipkin.brave brave io.zipkin.reporter2 zipkin-sender-okhttp3 io.zipkin.brave brave-instrumentation-spring-webmvc io.zipkin.brave brave-instrumentation-kafka-clients io.zipkin.brave brave-bom 5.9.1 pom import ================================================ FILE: lab-40/lab-40-kafka/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/KafkaApplication.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class KafkaApplication { public static void main(String[] args) { SpringApplication.run(KafkaApplication.class, args); } } ================================================ FILE: lab-40/lab-40-kafka/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/config/SpringMvcConfiguration.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.config; import brave.spring.webmvc.SpanCustomizingAsyncHandlerInterceptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration @Import(SpanCustomizingAsyncHandlerInterceptor.class) // 创建拦截器 SpanCustomizingAsyncHandlerInterceptor Bean public class SpringMvcConfiguration implements WebMvcConfigurer { @Autowired public SpanCustomizingAsyncHandlerInterceptor webMvcTracingCustomizer; /** * Decorates server spans with application-defined web tags */ @Override public void addInterceptors(InterceptorRegistry registry) { // 记录 SpringMVC 相关信息到 Span 中 registry.addInterceptor(webMvcTracingCustomizer); } } ================================================ FILE: lab-40/lab-40-kafka/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/config/ZipkinConfiguration.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.config; import brave.CurrentSpanCustomizer; import brave.SpanCustomizer; import brave.Tracing; import brave.http.HttpTracing; import brave.kafka.clients.KafkaTracing; import brave.servlet.TracingFilter; import org.apache.kafka.clients.consumer.Consumer; import org.apache.kafka.clients.producer.Producer; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.kafka.KafkaProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.kafka.core.ConsumerFactory; import org.springframework.kafka.core.DefaultKafkaConsumerFactory; import org.springframework.kafka.core.DefaultKafkaProducerFactory; import org.springframework.kafka.core.ProducerFactory; import zipkin2.Span; import zipkin2.reporter.AsyncReporter; import zipkin2.reporter.Sender; import zipkin2.reporter.okhttp3.OkHttpSender; import javax.servlet.Filter; import java.util.Properties; @Configuration public class ZipkinConfiguration { // ==================== 通用配置 ==================== /** * Configuration for how to send spans to Zipkin */ @Bean public Sender sender() { // Sender 采用 HTTP 通信方式 return OkHttpSender.create("http://127.0.0.1:9411/api/v2/spans"); } /** * Configuration for how to buffer spans into messages for Zipkin */ @Bean public AsyncReporter spanReporter() { // 异步 Reporter return AsyncReporter.create(sender()); } /** * Controls aspects of tracing such as the service name that shows up in the UI */ @Bean public Tracing tracing(@Value("${spring.application.name}") String serviceName) { return Tracing.newBuilder() .localServiceName(serviceName) // 应用名 .spanReporter(this.spanReporter()).build(); } /** * Allows someone to add tags to a span if a trace is in progress */ @Bean public SpanCustomizer spanCustomizer(Tracing tracing) { return CurrentSpanCustomizer.create(tracing); } // ==================== HTTP 相关 ==================== /** * Decides how to name and tag spans. By default they are named the same as the http method */ @Bean public HttpTracing httpTracing(Tracing tracing) { return HttpTracing.create(tracing); } /** * Creates server spans for http requests */ @Bean public Filter tracingFilter(HttpTracing httpTracing) { // 拦截请求,记录 HTTP 请求的链路信息 return TracingFilter.create(httpTracing); } // ==================== SpringMVC 相关 ==================== // @see SpringMvcConfiguration 类上的,@Import(SpanCustomizingAsyncHandlerInterceptor.class) 。因为 SpanCustomizingAsyncHandlerInterceptor 未提供 public 构造方法 // ==================== Kafka 相关 ==================== @Bean public KafkaTracing kafkaTracing(Tracing tracing) { return KafkaTracing.newBuilder(tracing) .remoteServiceName("demo-mq-kafka") // 远程 Kafka 服务名,可自定义 .build(); } @Bean public ProducerFactory kafkaProducerFactory(KafkaProperties properties, KafkaTracing kafkaTracing) { // 创建 DefaultKafkaProducerFactory 对象 DefaultKafkaProducerFactory factory = new DefaultKafkaProducerFactory(properties.buildProducerProperties()) { @Override public Producer createProducer() { // 创建默认的 Producer Producer producer = super.createProducer(); // 创建可链路追踪的 Producer return kafkaTracing.producer(producer); } }; // 设置事务前缀 String transactionIdPrefix = properties.getProducer().getTransactionIdPrefix(); if (transactionIdPrefix != null) { factory.setTransactionIdPrefix(transactionIdPrefix); } return factory; } @Bean public ConsumerFactory kafkaConsumerFactory(KafkaProperties properties, KafkaTracing kafkaTracing) { // 创建 DefaultKafkaConsumerFactory 对象 return new DefaultKafkaConsumerFactory(properties.buildConsumerProperties()) { @Override public Consumer createConsumer(String groupId, String clientIdPrefix, String clientIdSuffix) { return this.createConsumer(groupId, clientIdPrefix, clientIdSuffix, null); } @Override public Consumer createConsumer(String groupId, String clientIdPrefix, final String clientIdSuffixArg, Properties properties) { // 创建默认的 Consumer Consumer consumer = super.createConsumer(groupId, clientIdPrefix, clientIdSuffixArg, properties); // 创建可链路追踪的 Consumer return kafkaTracing.consumer(consumer); } }; } } ================================================ FILE: lab-40/lab-40-kafka/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/consumer/DemoConsumer.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.consumer; import cn.iocoder.springboot.lab40.zipkindemo.message.DemoMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.kafka.annotation.KafkaListener; import org.springframework.stereotype.Component; @Component public class DemoConsumer { private Logger logger = LoggerFactory.getLogger(getClass()); @KafkaListener(topics = DemoMessage.TOPIC, groupId = "demo-consumer-group-" + DemoMessage.TOPIC) public void onMessage(DemoMessage message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: lab-40/lab-40-kafka/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.controller; import cn.iocoder.springboot.lab40.zipkindemo.producer.DemoProducer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.concurrent.ExecutionException; @RestController @RequestMapping("/demo") public class DemoController { @Autowired private DemoProducer producer; @GetMapping("/kafka") public String echo() throws ExecutionException, InterruptedException { this.sendMessage(1); return "kafka"; } public void sendMessage(Integer id) throws ExecutionException, InterruptedException { producer.syncSend(id); } } ================================================ FILE: lab-40/lab-40-kafka/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/message/DemoMessage.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.message; /** * 示例 Message 消息 */ public class DemoMessage { public static final String TOPIC = "DEMO"; /** * 编号 */ private Integer id; public DemoMessage setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "DemoMessage{" + "id=" + id + '}'; } } ================================================ FILE: lab-40/lab-40-kafka/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/producer/DemoProducer.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.producer; import cn.iocoder.springboot.lab40.zipkindemo.message.DemoMessage; import org.springframework.kafka.core.KafkaTemplate; import org.springframework.kafka.support.SendResult; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.util.concurrent.ExecutionException; @Component public class DemoProducer { @Resource private KafkaTemplate kafkaTemplate; public SendResult syncSend(Integer id) throws ExecutionException, InterruptedException { // 创建 DemoMessage 消息 DemoMessage message = new DemoMessage(); message.setId(id); // 同步发送消息 return kafkaTemplate.send(DemoMessage.TOPIC, message).get(); } } ================================================ FILE: lab-40/lab-40-kafka/src/main/resources/application.yaml ================================================ spring: application: name: demo-application-kafka # Kafka 配置项,对应 KafkaProperties 配置类 kafka: bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 # Kafka Producer 配置项 producer: acks: 1 # 0-不应答。1-leader 应答。all-所有 leader 和 follower 应答。 retries: 3 # 发送失败时,重试发送的次数 key-serializer: org.apache.kafka.common.serialization.StringSerializer # 消息的 key 的序列化 value-serializer: org.springframework.kafka.support.serializer.JsonSerializer # 消息的 value 的序列化 # Kafka Consumer 配置项 consumer: auto-offset-reset: earliest # 设置消费者分组最初的消费进度为 earliest 。可参考博客 https://blog.csdn.net/lishuangzhe7047/article/details/74530417 理解 key-deserializer: org.apache.kafka.common.serialization.StringDeserializer value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer properties: spring: json: trusted: packages: cn.iocoder.springboot.lab40.zipkindemo.message # 消息 POJO 可信目录,解决 JSON 无法反序列化的问题 # Kafka Consumer Listener 监听器配置 listener: missing-topics-fatal: false # 消费监听接口监听的主题不存在时,默认会报错。所以通过设置为 false ,解决报错 ================================================ FILE: lab-40/lab-40-logback/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-40-logback org.springframework.boot spring-boot-starter-web io.zipkin.brave brave io.zipkin.reporter2 zipkin-sender-okhttp3 io.zipkin.brave brave-instrumentation-spring-webmvc io.zipkin.brave brave-context-slf4j io.zipkin.brave brave-bom 5.9.1 pom import ================================================ FILE: lab-40/lab-40-logback/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/LogbackApplication.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class LogbackApplication { public static void main(String[] args) { SpringApplication.run(LogbackApplication.class, args); } } ================================================ FILE: lab-40/lab-40-logback/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/config/SpringMvcConfiguration.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.config; import brave.spring.webmvc.SpanCustomizingAsyncHandlerInterceptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration @Import(SpanCustomizingAsyncHandlerInterceptor.class) public class SpringMvcConfiguration implements WebMvcConfigurer { @Autowired public SpanCustomizingAsyncHandlerInterceptor webMvcTracingCustomizer; /** * Decorates server spans with application-defined web tags */ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(webMvcTracingCustomizer); } } ================================================ FILE: lab-40/lab-40-logback/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/config/ZipkinConfiguration.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.config; import brave.CurrentSpanCustomizer; import brave.SpanCustomizer; import brave.Tracing; import brave.context.slf4j.MDCScopeDecorator; import brave.http.HttpTracing; import brave.propagation.ThreadLocalCurrentTraceContext; import brave.servlet.TracingFilter; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import zipkin2.Span; import zipkin2.reporter.AsyncReporter; import zipkin2.reporter.Sender; import zipkin2.reporter.okhttp3.OkHttpSender; import javax.servlet.Filter; @Configuration public class ZipkinConfiguration { // ==================== 通用配置 ==================== /** * Configuration for how to send spans to Zipkin */ @Bean public Sender sender() { return OkHttpSender.create("http://127.0.0.1:9411/api/v2/spans"); } /** * Configuration for how to buffer spans into messages for Zipkin */ @Bean public AsyncReporter spanReporter() { return AsyncReporter.create(sender()); } /** * Controls aspects of tracing such as the service name that shows up in the UI */ @Bean public Tracing tracing(@Value("${spring.application.name}") String serviceName) { return Tracing.newBuilder() .localServiceName(serviceName) .currentTraceContext(ThreadLocalCurrentTraceContext.newBuilder() .addScopeDecorator(MDCScopeDecorator.create()) // puts trace IDs into logs .build() ) .spanReporter(spanReporter()).build(); } /** * Allows someone to add tags to a span if a trace is in progress */ @Bean public SpanCustomizer spanCustomizer(Tracing tracing) { return CurrentSpanCustomizer.create(tracing); } // ==================== HTTP 相关 ==================== /** * Decides how to name and tag spans. By default they are named the same as the http method */ @Bean public HttpTracing httpTracing(Tracing tracing) { return HttpTracing.create(tracing); } /** * Creates server spans for http requests */ @Bean public Filter tracingFilter(HttpTracing httpTracing) { return TracingFilter.create(httpTracing); } // ==================== SpringMVC 相关 ==================== // @see SpringMvcConfiguration 类上的,@Import(SpanCustomizingAsyncHandlerInterceptor.class) } ================================================ FILE: lab-40/lab-40-logback/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.controller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { private Logger logger = LoggerFactory.getLogger(getClass()); @GetMapping("/logback") public String echo() { logger.info("测试日志"); return "logback"; } } ================================================ FILE: lab-40/lab-40-logback/src/main/resources/application.yaml ================================================ spring: application: name: demo-application-springmvc logging: pattern: 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}" 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}" ================================================ FILE: lab-40/lab-40-mongodb/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-40-mongodb org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-data-mongodb io.zipkin.brave brave io.zipkin.reporter2 zipkin-sender-okhttp3 io.zipkin.brave brave-instrumentation-spring-webmvc io.opentracing.brave brave-opentracing 0.35.0 io.opentracing.contrib opentracing-mongo-driver 0.1.5 io.zipkin.brave brave-bom 5.9.1 pom import ================================================ FILE: lab-40/lab-40-mongodb/src/main/java/cn/iocoder/springboot/lab40/zipkin/MongoDBApplication.java ================================================ package cn.iocoder.springboot.lab40.zipkin; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class MongoDBApplication { public static void main(String[] args) { SpringApplication.run(MongoDBApplication.class, args); } } ================================================ FILE: lab-40/lab-40-mongodb/src/main/java/cn/iocoder/springboot/lab40/zipkin/config/SpringMvcConfiguration.java ================================================ package cn.iocoder.springboot.lab40.zipkin.config; import brave.spring.webmvc.SpanCustomizingAsyncHandlerInterceptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration @Import(SpanCustomizingAsyncHandlerInterceptor.class) public class SpringMvcConfiguration implements WebMvcConfigurer { @Autowired public SpanCustomizingAsyncHandlerInterceptor webMvcTracingCustomizer; /** * Decorates server spans with application-defined web tags */ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(webMvcTracingCustomizer); } } ================================================ FILE: lab-40/lab-40-mongodb/src/main/java/cn/iocoder/springboot/lab40/zipkin/config/ZipkinConfiguration.java ================================================ package cn.iocoder.springboot.lab40.zipkin.config; import brave.CurrentSpanCustomizer; import brave.SpanCustomizer; import brave.Tracing; import brave.http.HttpTracing; import brave.opentracing.BraveTracer; import brave.servlet.TracingFilter; import com.mongodb.MongoClientOptions; import io.opentracing.Tracer; import io.opentracing.contrib.mongo.common.TracingCommandListener; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import zipkin2.Span; import zipkin2.reporter.AsyncReporter; import zipkin2.reporter.Sender; import zipkin2.reporter.okhttp3.OkHttpSender; import javax.servlet.Filter; @Configuration public class ZipkinConfiguration { // ==================== 通用配置 ==================== /** * Configuration for how to send spans to Zipkin */ @Bean public Sender sender() { return OkHttpSender.create("http://127.0.0.1:9411/api/v2/spans"); } /** * Configuration for how to buffer spans into messages for Zipkin */ @Bean public AsyncReporter spanReporter() { return AsyncReporter.create(sender()); } /** * Controls aspects of tracing such as the service name that shows up in the UI */ @Bean public Tracing tracing(@Value("${spring.application.name}") String serviceName) { return Tracing.newBuilder() .localServiceName(serviceName) .spanReporter(spanReporter()).build(); } @Bean public Tracer openTracer(Tracing tracing) { return BraveTracer.create(tracing); } /** * Allows someone to add tags to a span if a trace is in progress */ @Bean public SpanCustomizer spanCustomizer(Tracing tracing) { return CurrentSpanCustomizer.create(tracing); } // ==================== HTTP 相关 ==================== /** * Decides how to name and tag spans. By default they are named the same as the http method */ @Bean public HttpTracing httpTracing(Tracing tracing) { return HttpTracing.create(tracing); } /** * Creates server spans for http requests */ @Bean public Filter tracingFilter(HttpTracing httpTracing) { return TracingFilter.create(httpTracing); } // ==================== SpringMVC 相关 ==================== // @see SpringMvcConfiguration 类上的,@Import(SpanCustomizingAsyncHandlerInterceptor.class) // ==================== MongoDB 相关 ==================== @Bean public MongoClientOptions mongoClientOptions(Tracer tracer) { // 创建 TracingCommandListener 对象 TracingCommandListener listener = new TracingCommandListener.Builder(tracer).build(); // 创建 MongoClientOptions 对象,并设置监听器 return MongoClientOptions.builder().addCommandListener(listener).build(); } } ================================================ FILE: lab-40/lab-40-mongodb/src/main/java/cn/iocoder/springboot/lab40/zipkin/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab40.zipkin.controller; import cn.iocoder.springboot.lab40.zipkin.dataobject.UserDO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @Autowired private MongoTemplate mongoTemplate; @GetMapping("/mongodb") public String mysql() { this.findById(1); return "mongodb"; } public UserDO findById(Integer id) { return mongoTemplate.findOne(new Query(Criteria.where("_id").is(id)), UserDO.class); } } ================================================ FILE: lab-40/lab-40-mongodb/src/main/java/cn/iocoder/springboot/lab40/zipkin/dataobject/UserDO.java ================================================ package cn.iocoder.springboot.lab40.zipkin.dataobject; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.mapping.Document; import java.util.Date; /** * 用户 DO */ @Document(collection = "User") public class UserDO { @Id private Integer id; /** * 账号 */ private String username; /** * 密码 */ private String password; /** * 创建时间 */ private Date createTime; @Override public String toString() { return "UserDO{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", createTime=" + createTime + '}'; } } ================================================ FILE: lab-40/lab-40-mongodb/src/main/resources/application.yml ================================================ spring: application: name: dmeo-application-mongodb data: # MongoDB 配置项,对应 MongoProperties 类 mongodb: host: 127.0.0.1 port: 27017 database: yourdatabase username: test01 password: password01 # 上述属性,也可以只配置 uri ================================================ FILE: lab-40/lab-40-mysql/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-40-mysql org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 5.1.46 io.zipkin.brave brave io.zipkin.reporter2 zipkin-sender-okhttp3 io.zipkin.brave brave-instrumentation-spring-webmvc io.zipkin.brave brave-instrumentation-mysql io.zipkin.brave brave-bom 5.9.1 pom import ================================================ FILE: lab-40/lab-40-mysql/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/MySQLApplication.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class MySQLApplication { public static void main(String[] args) { SpringApplication.run(MySQLApplication.class, args); } } ================================================ FILE: lab-40/lab-40-mysql/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/config/SpringMvcConfiguration.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.config; import brave.spring.webmvc.SpanCustomizingAsyncHandlerInterceptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration @Import(SpanCustomizingAsyncHandlerInterceptor.class) public class SpringMvcConfiguration implements WebMvcConfigurer { @Autowired public SpanCustomizingAsyncHandlerInterceptor webMvcTracingCustomizer; /** * Decorates server spans with application-defined web tags */ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(webMvcTracingCustomizer); } } ================================================ FILE: lab-40/lab-40-mysql/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/config/ZipkinConfiguration.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.config; import brave.CurrentSpanCustomizer; import brave.SpanCustomizer; import brave.Tracing; import brave.http.HttpTracing; import brave.servlet.TracingFilter; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import zipkin2.Span; import zipkin2.reporter.AsyncReporter; import zipkin2.reporter.Sender; import zipkin2.reporter.okhttp3.OkHttpSender; import javax.servlet.Filter; @Configuration public class ZipkinConfiguration { // ==================== 通用配置 ==================== /** * Configuration for how to send spans to Zipkin */ @Bean public Sender sender() { return OkHttpSender.create("http://127.0.0.1:9411/api/v2/spans"); } /** * Configuration for how to buffer spans into messages for Zipkin */ @Bean public AsyncReporter spanReporter() { return AsyncReporter.create(sender()); } /** * Controls aspects of tracing such as the service name that shows up in the UI */ @Bean public Tracing tracing(@Value("${spring.application.name}") String serviceName) { return Tracing.newBuilder() .localServiceName(serviceName) .spanReporter(spanReporter()).build(); } /** * Allows someone to add tags to a span if a trace is in progress */ @Bean public SpanCustomizer spanCustomizer(Tracing tracing) { return CurrentSpanCustomizer.create(tracing); } // ==================== HTTP 相关 ==================== /** * Decides how to name and tag spans. By default they are named the same as the http method */ @Bean public HttpTracing httpTracing(Tracing tracing) { return HttpTracing.create(tracing); } /** * Creates server spans for http requests */ @Bean public Filter tracingFilter(HttpTracing httpTracing) { return TracingFilter.create(httpTracing); } // ==================== SpringMVC 相关 ==================== // @see SpringMvcConfiguration 类上的,@Import(SpanCustomizingAsyncHandlerInterceptor.class) } ================================================ FILE: lab-40/lab-40-mysql/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @Autowired private JdbcTemplate template; @GetMapping("/mysql") public String echo() { this.selectById(1); return "mysql"; } public Object selectById(Integer id) { return template.queryForObject("SELECT id, username, password FROM t_user WHERE id = ?", new BeanPropertyRowMapper<>(Object.class), // 结果转换成对应的对象。Object 理论来说是 UserDO.class ,这里偷懒了。 id); } } ================================================ FILE: lab-40/lab-40-mysql/src/main/resources/application.yaml ================================================ spring: application: name: demo-application-mysql # datasource 数据源配置内容 datasource: 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 driver-class-name: com.mysql.jdbc.Driver username: root password: ================================================ FILE: lab-40/lab-40-opentracing/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-40-opentracing org.springframework.boot spring-boot-starter-web io.zipkin.brave brave io.zipkin.reporter2 zipkin-sender-okhttp3 io.zipkin.brave brave-instrumentation-spring-webmvc io.opentracing.brave brave-opentracing 0.35.0 io.zipkin.brave brave-bom 5.9.1 pom import ================================================ FILE: lab-40/lab-40-opentracing/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/OpentracingApplication.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class OpentracingApplication { public static void main(String[] args) { SpringApplication.run(OpentracingApplication.class, args); } } ================================================ FILE: lab-40/lab-40-opentracing/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/config/SpringMvcConfiguration.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.config; import brave.spring.webmvc.SpanCustomizingAsyncHandlerInterceptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration @Import(SpanCustomizingAsyncHandlerInterceptor.class) public class SpringMvcConfiguration implements WebMvcConfigurer { @Autowired public SpanCustomizingAsyncHandlerInterceptor webMvcTracingCustomizer; /** * Decorates server spans with application-defined web tags */ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(webMvcTracingCustomizer); } } ================================================ FILE: lab-40/lab-40-opentracing/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/config/ZipkinConfiguration.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.config; import brave.CurrentSpanCustomizer; import brave.SpanCustomizer; import brave.Tracing; import brave.http.HttpTracing; import brave.opentracing.BraveTracer; import brave.servlet.TracingFilter; import io.opentracing.Tracer; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import zipkin2.Span; import zipkin2.reporter.AsyncReporter; import zipkin2.reporter.Sender; import zipkin2.reporter.okhttp3.OkHttpSender; import javax.servlet.Filter; @Configuration public class ZipkinConfiguration { // ==================== 通用配置 ==================== /** * Configuration for how to send spans to Zipkin */ @Bean public Sender sender() { return OkHttpSender.create("http://127.0.0.1:9411/api/v2/spans"); } /** * Configuration for how to buffer spans into messages for Zipkin */ @Bean public AsyncReporter spanReporter() { return AsyncReporter.create(sender()); } /** * Controls aspects of tracing such as the service name that shows up in the UI */ @Bean public Tracing tracing(@Value("${spring.application.name}") String serviceName) { return Tracing.newBuilder() .localServiceName(serviceName) .spanReporter(spanReporter()).build(); } @Bean public Tracer openTracer(Tracing tracing) { return BraveTracer.create(tracing); } /** * Allows someone to add tags to a span if a trace is in progress */ @Bean public SpanCustomizer spanCustomizer(Tracing tracing) { return CurrentSpanCustomizer.create(tracing); } // ==================== HTTP 相关 ==================== /** * Decides how to name and tag spans. By default they are named the same as the http method */ @Bean public HttpTracing httpTracing(Tracing tracing) { return HttpTracing.create(tracing); } /** * Creates server spans for http requests */ @Bean public Filter tracingFilter(HttpTracing httpTracing) { return TracingFilter.create(httpTracing); } // ==================== SpringMVC 相关 ==================== // @see SpringMvcConfiguration 类上的,@Import(SpanCustomizingAsyncHandlerInterceptor.class) } ================================================ FILE: lab-40/lab-40-opentracing/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.controller; import io.opentracing.Tracer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @Autowired private Tracer tracer; @GetMapping("/opentracing") public String echo() { // 创建一个 Span tracer.buildSpan("custom_operation").withTag("mp", "芋道源码").start().finish(); // 返回 return "opentracing"; } } ================================================ FILE: lab-40/lab-40-opentracing/src/main/resources/application.yml ================================================ spring: application: name: demo-application-opentracing ================================================ FILE: lab-40/lab-40-rabbitmq/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-40-rabbitmq org.springframework.boot spring-boot-starter-amqp org.springframework.boot spring-boot-starter-web io.zipkin.brave brave io.zipkin.reporter2 zipkin-sender-okhttp3 io.zipkin.brave brave-instrumentation-spring-webmvc io.zipkin.brave brave-instrumentation-spring-rabbit io.zipkin.brave brave-bom 5.9.1 pom import ================================================ FILE: lab-40/lab-40-rabbitmq/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/RabbitMQApplication.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class RabbitMQApplication { public static void main(String[] args) { SpringApplication.run(RabbitMQApplication.class, args); } } ================================================ FILE: lab-40/lab-40-rabbitmq/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/config/RabbitConfig.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.config; import cn.iocoder.springboot.lab40.zipkindemo.message.DemoMessage; import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.BindingBuilder; import org.springframework.amqp.core.DirectExchange; import org.springframework.amqp.core.Queue; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class RabbitConfig { // 创建 Queue @Bean public Queue demoQueue() { return new Queue(DemoMessage.QUEUE, // Queue 名字 true, // durable: 是否持久化 false, // exclusive: 是否排它 false); // autoDelete: 是否自动删除 } // 创建 Direct Exchange @Bean public DirectExchange demoExchange() { return new DirectExchange(DemoMessage.EXCHANGE, true, // durable: 是否持久化 false); // exclusive: 是否排它 } // 创建 Binding // Exchange:DemoMessage.EXCHANGE // Routing key:DemoMessage.ROUTING_KEY // Queue:DemoMessage.QUEUE @Bean public Binding demoBinding() { return BindingBuilder.bind(demoQueue()).to(demoExchange()).with(DemoMessage.ROUTING_KEY); } } ================================================ FILE: lab-40/lab-40-rabbitmq/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/config/SpringMvcConfiguration.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.config; import brave.spring.webmvc.SpanCustomizingAsyncHandlerInterceptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration @Import(SpanCustomizingAsyncHandlerInterceptor.class) // 创建拦截器 SpanCustomizingAsyncHandlerInterceptor Bean public class SpringMvcConfiguration implements WebMvcConfigurer { @Autowired public SpanCustomizingAsyncHandlerInterceptor webMvcTracingCustomizer; /** * Decorates server spans with application-defined web tags */ @Override public void addInterceptors(InterceptorRegistry registry) { // 记录 SpringMVC 相关信息到 Span 中 registry.addInterceptor(webMvcTracingCustomizer); } } ================================================ FILE: lab-40/lab-40-rabbitmq/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/config/ZipkinConfiguration.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.config; import brave.CurrentSpanCustomizer; import brave.SpanCustomizer; import brave.Tracing; import brave.http.HttpTracing; import brave.servlet.TracingFilter; import brave.spring.rabbit.SpringRabbitTracing; import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import zipkin2.Span; import zipkin2.reporter.AsyncReporter; import zipkin2.reporter.Sender; import zipkin2.reporter.okhttp3.OkHttpSender; import javax.servlet.Filter; @Configuration public class ZipkinConfiguration { // ==================== 通用配置 ==================== /** * Configuration for how to send spans to Zipkin */ @Bean public Sender sender() { // Sender 采用 HTTP 通信方式 return OkHttpSender.create("http://127.0.0.1:9411/api/v2/spans"); } /** * Configuration for how to buffer spans into messages for Zipkin */ @Bean public AsyncReporter spanReporter() { // 异步 Reporter return AsyncReporter.create(sender()); } /** * Controls aspects of tracing such as the service name that shows up in the UI */ @Bean public Tracing tracing(@Value("${spring.application.name}") String serviceName) { return Tracing.newBuilder() .localServiceName(serviceName) // 应用名 .spanReporter(this.spanReporter()).build(); } /** * Allows someone to add tags to a span if a trace is in progress */ @Bean public SpanCustomizer spanCustomizer(Tracing tracing) { return CurrentSpanCustomizer.create(tracing); } // ==================== HTTP 相关 ==================== /** * Decides how to name and tag spans. By default they are named the same as the http method */ @Bean public HttpTracing httpTracing(Tracing tracing) { return HttpTracing.create(tracing); } /** * Creates server spans for http requests */ @Bean public Filter tracingFilter(HttpTracing httpTracing) { // 拦截请求,记录 HTTP 请求的链路信息 return TracingFilter.create(httpTracing); } // ==================== SpringMVC 相关 ==================== // @see SpringMvcConfiguration 类上的,@Import(SpanCustomizingAsyncHandlerInterceptor.class) 。因为 SpanCustomizingAsyncHandlerInterceptor 未提供 public 构造方法 // ==================== RabbitMQ 相关 ==================== @Bean public SpringRabbitTracing springRabbitTracing(Tracing tracing) { return SpringRabbitTracing.newBuilder(tracing) .remoteServiceName("demo-mq-rabbit") // 远程 RabbitMQ 服务名,可自定义 .build(); } @Bean public BeanPostProcessor rabbitmqBeanPostProcessor(SpringRabbitTracing springRabbitTracing) { return new BeanPostProcessor() { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { // 如果是 RabbitTemplate ,针对 RabbitMQ Producer if (bean instanceof RabbitTemplate) { return springRabbitTracing.decorateRabbitTemplate((RabbitTemplate) bean); } // 如果是 SimpleRabbitListenerContainerFactory ,针对 RabbitMQ Consumer if (bean instanceof SimpleRabbitListenerContainerFactory) { return springRabbitTracing.decorateSimpleRabbitListenerContainerFactory((SimpleRabbitListenerContainerFactory) bean); } return bean; } }; } } ================================================ FILE: lab-40/lab-40-rabbitmq/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/consumer/DemoConsumer.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.consumer; import cn.iocoder.springboot.lab40.zipkindemo.message.DemoMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; @Component @RabbitListener(queues = DemoMessage.QUEUE) public class DemoConsumer { private Logger logger = LoggerFactory.getLogger(getClass()); @RabbitHandler public void onMessage(DemoMessage message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: lab-40/lab-40-rabbitmq/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.controller; import cn.iocoder.springboot.lab40.zipkindemo.producer.DemoProducer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @Autowired private DemoProducer producer; @GetMapping("/rabbitmq") public String echo() { this.sendMessage(1); return "rabbitmq"; } public void sendMessage(Integer id) { producer.syncSend(id); } } ================================================ FILE: lab-40/lab-40-rabbitmq/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/message/DemoMessage.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.message; import java.io.Serializable; public class DemoMessage implements Serializable { public static final String QUEUE = "QUEUE_DEMO_"; public static final String EXCHANGE = "EXCHANGE_DEMO_"; public static final String ROUTING_KEY = "ROUTING_KEY_"; /** * 编号 */ private Integer id; public DemoMessage setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "DemoMessage{" + "id=" + id + '}'; } } ================================================ FILE: lab-40/lab-40-rabbitmq/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/producer/DemoProducer.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.producer; import cn.iocoder.springboot.lab40.zipkindemo.message.DemoMessage; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class DemoProducer { @Autowired private RabbitTemplate rabbitTemplate; public void syncSend(Integer id) { // 创建 DemoMessage 消息 DemoMessage message = new DemoMessage(); message.setId(id); // 同步发送消息 rabbitTemplate.convertAndSend(DemoMessage.EXCHANGE, DemoMessage.ROUTING_KEY, message); } } ================================================ FILE: lab-40/lab-40-rabbitmq/src/main/resources/application.yaml ================================================ spring: application: name: demo-application-rabbitmq # RabbitMQ 配置项,对应 RabbitProperties 配置类 rabbitmq: host: 127.0.0.1 # RabbitMQ 服务的地址 port: 5672 # RabbitMQ 服务的端口 username: guest # RabbitMQ 服务的账号 password: guest # RabbitMQ 服务的密码 ================================================ FILE: lab-40/lab-40-redis/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-40-redis org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-data-redis io.lettuce lettuce-core redis.clients jedis io.zipkin.brave brave io.zipkin.reporter2 zipkin-sender-okhttp3 io.zipkin.brave brave-instrumentation-spring-webmvc io.opentracing.brave brave-opentracing 0.35.0 io.opentracing.contrib opentracing-redis-jedis3 0.1.14 io.opentracing.contrib opentracing-redis-spring-data 0.1.14 io.zipkin.brave brave-bom 5.9.1 pom import ================================================ FILE: lab-40/lab-40-redis/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/RedisApplication.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class RedisApplication { public static void main(String[] args) { SpringApplication.run(RedisApplication.class, args); } } ================================================ FILE: lab-40/lab-40-redis/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/config/SpringMvcConfiguration.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.config; import brave.spring.webmvc.SpanCustomizingAsyncHandlerInterceptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration @Import(SpanCustomizingAsyncHandlerInterceptor.class) public class SpringMvcConfiguration implements WebMvcConfigurer { @Autowired public SpanCustomizingAsyncHandlerInterceptor webMvcTracingCustomizer; /** * Decorates server spans with application-defined web tags */ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(webMvcTracingCustomizer); } } ================================================ FILE: lab-40/lab-40-redis/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/config/ZipkinConfiguration.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.config; import brave.CurrentSpanCustomizer; import brave.SpanCustomizer; import brave.Tracing; import brave.http.HttpTracing; import brave.opentracing.BraveTracer; import brave.servlet.TracingFilter; import io.opentracing.Tracer; import io.opentracing.contrib.redis.common.TracingConfiguration; import io.opentracing.contrib.redis.spring.data.connection.TracingRedisConnectionFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.data.redis.RedisProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import zipkin2.Span; import zipkin2.reporter.AsyncReporter; import zipkin2.reporter.Sender; import zipkin2.reporter.okhttp3.OkHttpSender; import javax.servlet.Filter; @Configuration public class ZipkinConfiguration { // ==================== 通用配置 ==================== /** * Configuration for how to send spans to Zipkin */ @Bean public Sender sender() { return OkHttpSender.create("http://127.0.0.1:9411/api/v2/spans"); } /** * Configuration for how to buffer spans into messages for Zipkin */ @Bean public AsyncReporter spanReporter() { return AsyncReporter.create(sender()); } /** * Controls aspects of tracing such as the service name that shows up in the UI */ @Bean public Tracing tracing(@Value("${spring.application.name}") String serviceName) { return Tracing.newBuilder() .localServiceName(serviceName) .spanReporter(spanReporter()).build(); } @Bean public Tracer openTracer(Tracing tracing) { return BraveTracer.create(tracing); } /** * Allows someone to add tags to a span if a trace is in progress */ @Bean public SpanCustomizer spanCustomizer(Tracing tracing) { return CurrentSpanCustomizer.create(tracing); } // ==================== HTTP 相关 ==================== /** * Decides how to name and tag spans. By default they are named the same as the http method */ @Bean public HttpTracing httpTracing(Tracing tracing) { return HttpTracing.create(tracing); } /** * Creates server spans for http requests */ @Bean public Filter tracingFilter(HttpTracing httpTracing) { return TracingFilter.create(httpTracing); } // ==================== SpringMVC 相关 ==================== // @see SpringMvcConfiguration 类上的,@Import(SpanCustomizingAsyncHandlerInterceptor.class) // ==================== Redis 相关 ==================== @Bean public RedisConnectionFactory redisConnectionFactory(Tracer tracer, RedisProperties redisProperties) { // 创建 JedisConnectionFactory 对象 RedisConnectionFactory connectionFactory = new JedisConnectionFactory(); // 创建 TracingConfiguration 对象 TracingConfiguration tracingConfiguration = new TracingConfiguration.Builder(tracer) // 设置拓展 Tag ,设置 Redis 服务器地址。因为默认情况下,不会在操作 Redis 链路的 Span 上记录 Redis 服务器的地址,所以这里需要设置。 .extensionTag("Server Address", redisProperties.getHost() + ":" + redisProperties.getPort()) .build(); // 创建 TracingRedisConnectionFactory 对象 return new TracingRedisConnectionFactory(connectionFactory, tracingConfiguration); } } ================================================ FILE: lab-40/lab-40-redis/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @Autowired private StringRedisTemplate redisTemplate; @GetMapping("/redis") public String redis() { this.get("demo"); return "redis"; } public void get(String key) { redisTemplate.opsForValue().get(key); } } ================================================ FILE: lab-40/lab-40-redis/src/main/resources/application.yaml ================================================ spring: application: name: demo-application-redis # 对应 RedisProperties 类 redis: host: 127.0.0.1 port: 6379 password: # Redis 服务器密码,默认为空。生产中,一定要设置 Redis 密码! database: 0 # Redis 数据库号,默认为 0 。 timeout: 0 # Redis 连接超时时间,单位:毫秒。 # 对应 RedisProperties.Jedis 内部类 jedis: pool: max-active: 8 # 连接池最大连接数,默认为 8 。使用负数表示没有限制。 max-idle: 8 # 默认连接数最小空闲的连接数,默认为 8 。使用负数表示没有限制。 min-idle: 0 # 默认连接池最小空闲的连接数,默认为 0 。允许设置 0 和 正数。 max-wait: -1 # 连接池最大阻塞等待时间,单位:毫秒。默认为 -1 ,表示不限制。 ================================================ FILE: lab-40/lab-40-springmvc/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-40-springmvc org.springframework.boot spring-boot-starter-web io.zipkin.brave brave io.zipkin.reporter2 zipkin-sender-okhttp3 io.zipkin.brave brave-instrumentation-spring-webmvc io.zipkin.brave brave-bom 5.9.1 pom import ================================================ FILE: lab-40/lab-40-springmvc/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/SpringMVCApplication.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SpringMVCApplication { public static void main(String[] args) { SpringApplication.run(SpringMVCApplication.class, args); } } ================================================ FILE: lab-40/lab-40-springmvc/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/config/SpringMvcConfiguration.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.config; import brave.spring.webmvc.SpanCustomizingAsyncHandlerInterceptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration @Import(SpanCustomizingAsyncHandlerInterceptor.class) // 创建拦截器 SpanCustomizingAsyncHandlerInterceptor Bean public class SpringMvcConfiguration implements WebMvcConfigurer { @Autowired public SpanCustomizingAsyncHandlerInterceptor webMvcTracingCustomizer; /** * Decorates server spans with application-defined web tags */ @Override public void addInterceptors(InterceptorRegistry registry) { // 记录 SpringMVC 相关信息到 Span 中 registry.addInterceptor(webMvcTracingCustomizer); } } ================================================ FILE: lab-40/lab-40-springmvc/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/config/ZipkinConfiguration.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.config; import brave.CurrentSpanCustomizer; import brave.SpanCustomizer; import brave.Tracing; import brave.http.HttpTracing; import brave.servlet.TracingFilter; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import zipkin2.Span; import zipkin2.reporter.AsyncReporter; import zipkin2.reporter.Sender; import zipkin2.reporter.okhttp3.OkHttpSender; import javax.servlet.Filter; @Configuration public class ZipkinConfiguration { // ==================== 通用配置 ==================== /** * Configuration for how to send spans to Zipkin */ @Bean public Sender sender() { // Sender 采用 HTTP 通信方式 return OkHttpSender.create("http://127.0.0.1:9411/api/v2/spans"); } /** * Configuration for how to buffer spans into messages for Zipkin */ @Bean public AsyncReporter spanReporter() { // 异步 Reporter return AsyncReporter.create(sender()); } /** * Controls aspects of tracing such as the service name that shows up in the UI */ @Bean public Tracing tracing(@Value("${spring.application.name}") String serviceName) { return Tracing.newBuilder() .localServiceName(serviceName) // 应用名 .spanReporter(this.spanReporter()).build(); } /** * Allows someone to add tags to a span if a trace is in progress */ @Bean public SpanCustomizer spanCustomizer(Tracing tracing) { return CurrentSpanCustomizer.create(tracing); } // ==================== HTTP 相关 ==================== /** * Decides how to name and tag spans. By default they are named the same as the http method */ @Bean public HttpTracing httpTracing(Tracing tracing) { return HttpTracing.create(tracing); } /** * Creates server spans for http requests */ @Bean public Filter tracingFilter(HttpTracing httpTracing) { // 拦截请求,记录 HTTP 请求的链路信息 return TracingFilter.create(httpTracing); } // ==================== SpringMVC 相关 ==================== // @see SpringMvcConfiguration 类上的,@Import(SpanCustomizingAsyncHandlerInterceptor.class) 。因为 SpanCustomizingAsyncHandlerInterceptor 未提供 public 构造方法 } ================================================ FILE: lab-40/lab-40-springmvc/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @GetMapping("/springmvc") public String echo() { return "springmvc"; } } ================================================ FILE: lab-40/lab-40-springmvc/src/main/resources/application.yaml ================================================ spring: application: name: demo-application-springmvc ================================================ FILE: lab-40/lab-40-zipkin-dubbo/lab-40-zipkin-dubbo-api/pom.xml ================================================ lab-40-zipkin-dubbo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-40-zipkin-dubbo-api ================================================ FILE: lab-40/lab-40-zipkin-dubbo/lab-40-zipkin-dubbo-api/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/api/UserService.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.api; /** * 用户服务 RPC Service 接口 */ public interface UserService { /** * 根据指定用户编号,获得用户信息 * * @param id 用户编号 * @return 用户信息 */ String get(Integer id); } ================================================ FILE: lab-40/lab-40-zipkin-dubbo/lab-40-zipkin-dubbo-api/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/package-info.java ================================================ package cn.iocoder.springcloud.labx13; ================================================ FILE: lab-40/lab-40-zipkin-dubbo/lab-40-zipkin-dubbo-consumer/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-40-zipkin-dubbo-consumer cn.iocoder.springboot.labs lab-40-zipkin-dubbo-api 1.0-SNAPSHOT org.springframework.boot spring-boot-starter-web org.apache.dubbo dubbo 2.7.4.1 org.apache.dubbo dubbo-spring-boot-starter 2.7.4.1 org.apache.curator curator-framework 2.13.0 org.apache.curator curator-recipes 2.13.0 io.zipkin.brave brave io.zipkin.reporter2 zipkin-sender-okhttp3 io.zipkin.brave brave-instrumentation-spring-webmvc io.zipkin.brave brave-instrumentation-dubbo 5.10.1 io.zipkin.brave brave-bom 5.9.1 pom import ================================================ FILE: lab-40/lab-40-zipkin-dubbo/lab-40-zipkin-dubbo-consumer/src/main/java/cn/iocoder/springboot/lab40/zpkindemo/consumerdemo/ConsumerApplication.java ================================================ package cn.iocoder.springboot.lab40.zpkindemo.consumerdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class); } } ================================================ FILE: lab-40/lab-40-zipkin-dubbo/lab-40-zipkin-dubbo-consumer/src/main/java/cn/iocoder/springboot/lab40/zpkindemo/consumerdemo/config/SpringMvcConfiguration.java ================================================ package cn.iocoder.springboot.lab40.zpkindemo.consumerdemo.config; import brave.spring.webmvc.SpanCustomizingAsyncHandlerInterceptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration @Import(SpanCustomizingAsyncHandlerInterceptor.class) public class SpringMvcConfiguration implements WebMvcConfigurer { @Autowired public SpanCustomizingAsyncHandlerInterceptor webMvcTracingCustomizer; /** * Decorates server spans with application-defined web tags */ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(webMvcTracingCustomizer); } } ================================================ FILE: lab-40/lab-40-zipkin-dubbo/lab-40-zipkin-dubbo-consumer/src/main/java/cn/iocoder/springboot/lab40/zpkindemo/consumerdemo/config/ZipkinConfiguration.java ================================================ package cn.iocoder.springboot.lab40.zpkindemo.consumerdemo.config; import brave.CurrentSpanCustomizer; import brave.SpanCustomizer; import brave.Tracing; import brave.http.HttpTracing; import brave.servlet.TracingFilter; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import zipkin2.Span; import zipkin2.reporter.AsyncReporter; import zipkin2.reporter.Sender; import zipkin2.reporter.okhttp3.OkHttpSender; import javax.servlet.Filter; @Configuration public class ZipkinConfiguration { // ==================== 通用配置 ==================== /** * Configuration for how to send spans to Zipkin */ @Bean public Sender sender() { return OkHttpSender.create("http://127.0.0.1:9411/api/v2/spans"); } /** * Configuration for how to buffer spans into messages for Zipkin */ @Bean public AsyncReporter spanReporter() { return AsyncReporter.create(sender()); } /** * Controls aspects of tracing such as the service name that shows up in the UI */ @Bean public Tracing tracing(@Value("${spring.application.name}") String serviceName) { return Tracing.newBuilder() .localServiceName(serviceName) // .currentTraceContext(ThreadLocalCurrentTraceContext.newBuilder() // .addScopeDecorator(MDCScopeDecorator.create()) // puts trace IDs into logs // .build() // ) .spanReporter(spanReporter()).build(); } /** * Allows someone to add tags to a span if a trace is in progress */ @Bean public SpanCustomizer spanCustomizer(Tracing tracing) { return CurrentSpanCustomizer.create(tracing); } // ==================== HTTP 相关 ==================== /** * Decides how to name and tag spans. By default they are named the same as the http method */ @Bean public HttpTracing httpTracing(Tracing tracing) { return HttpTracing.create(tracing); } /** * Creates server spans for http requests */ @Bean public Filter tracingFilter(HttpTracing httpTracing) { return TracingFilter.create(httpTracing); } // ==================== SpringMVC 相关 ==================== // @see SpringMvcConfiguration 类上的,@Import(SpanCustomizingAsyncHandlerInterceptor.class) } ================================================ FILE: lab-40/lab-40-zipkin-dubbo/lab-40-zipkin-dubbo-consumer/src/main/java/cn/iocoder/springboot/lab40/zpkindemo/consumerdemo/controller/UserController.java ================================================ package cn.iocoder.springboot.lab40.zpkindemo.consumerdemo.controller; import cn.iocoder.springboot.lab40.zipkindemo.api.UserService; import org.apache.dubbo.config.annotation.Reference; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/user") public class UserController { @Reference(protocol = "dubbo", version = "1.0.0") private UserService userService; @GetMapping("/get") public String get(@RequestParam("id") Integer id) { return userService.get(id); } } ================================================ FILE: lab-40/lab-40-zipkin-dubbo/lab-40-zipkin-dubbo-consumer/src/main/resources/application.yaml ================================================ spring: application: name: user-service-consumer # dubbo 配置项,对应 DubboConfigurationProperties 配置类 dubbo: # Dubbo 应用配置 application: name: ${spring.application.name} # 应用名 # Dubbo 注册中心配置 registry: address: zookeeper://127.0.0.1:2181 # 注册中心地址。个鞥多注册中心,可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。 # Dubbo 服务提供者的配置,对应 ConsumerConfig 类 consumer: filter: tracing ================================================ FILE: lab-40/lab-40-zipkin-dubbo/lab-40-zipkin-dubbo-provider/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-40-zipkin-dubbo-provider cn.iocoder.springboot.labs lab-40-zipkin-dubbo-api 1.0-SNAPSHOT org.springframework.boot spring-boot-starter org.apache.dubbo dubbo 2.7.4.1 org.apache.dubbo dubbo-spring-boot-starter 2.7.4.1 org.apache.curator curator-framework 2.13.0 org.apache.curator curator-recipes 2.13.0 io.zipkin.brave brave io.zipkin.reporter2 zipkin-sender-okhttp3 io.zipkin.brave brave-instrumentation-dubbo 5.10.1 io.zipkin.brave brave-bom 5.9.1 pom import ================================================ FILE: lab-40/lab-40-zipkin-dubbo/lab-40-zipkin-dubbo-provider/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/providerdemo/ProviderApplication.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.providerdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ProviderApplication { public static void main(String[] args) { SpringApplication.run(ProviderApplication.class); } } ================================================ FILE: lab-40/lab-40-zipkin-dubbo/lab-40-zipkin-dubbo-provider/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/providerdemo/config/ZipkinConfiguration.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.providerdemo.config; import brave.CurrentSpanCustomizer; import brave.SpanCustomizer; import brave.Tracing; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import zipkin2.Span; import zipkin2.reporter.AsyncReporter; import zipkin2.reporter.Sender; import zipkin2.reporter.okhttp3.OkHttpSender; @Configuration public class ZipkinConfiguration { // ==================== 通用配置 ==================== /** * Configuration for how to send spans to Zipkin */ @Bean public Sender sender() { return OkHttpSender.create("http://127.0.0.1:9411/api/v2/spans"); } /** * Configuration for how to buffer spans into messages for Zipkin */ @Bean public AsyncReporter spanReporter() { return AsyncReporter.create(sender()); } /** * Controls aspects of tracing such as the service name that shows up in the UI */ @Bean public Tracing tracing(@Value("${spring.application.name}") String serviceName) { return Tracing.newBuilder() .localServiceName(serviceName) // .currentTraceContext(ThreadLocalCurrentTraceContext.newBuilder() // .addScopeDecorator(MDCScopeDecorator.create()) // puts trace IDs into logs // .build() // ) .spanReporter(spanReporter()).build(); } /** * Allows someone to add tags to a span if a trace is in progress */ @Bean public SpanCustomizer spanCustomizer(Tracing tracing) { return CurrentSpanCustomizer.create(tracing); } } ================================================ FILE: lab-40/lab-40-zipkin-dubbo/lab-40-zipkin-dubbo-provider/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/providerdemo/service/UserServiceImpl.java ================================================ package cn.iocoder.springboot.lab40.zipkindemo.providerdemo.service; import cn.iocoder.springboot.lab40.zipkindemo.api.UserService; @org.apache.dubbo.config.annotation.Service(version = "1.0.0") public class UserServiceImpl implements UserService { @Override public String get(Integer id) { return "user:" + id; } } ================================================ FILE: lab-40/lab-40-zipkin-dubbo/lab-40-zipkin-dubbo-provider/src/main/resources/application.yaml ================================================ spring: application: name: user-service-provider # dubbo 配置项,对应 DubboConfigurationProperties 配置类 dubbo: # Dubbo 应用配置 application: name: ${spring.application.name} # 应用名 # Dubbo 注册中心配 registry: address: zookeeper://127.0.0.1:2181 # 注册中心地址。个鞥多注册中心,可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。 # Dubbo 服务提供者协议配置 protocol: port: -1 # 协议端口。使用 -1 表示随机端口。 name: dubbo # 使用 `dubbo://` 协议。更多协议,可见 http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html 文档 # 配置扫描 Dubbo 自定义的 @Service 注解,暴露成 Dubbo 服务提供者 scan: base-packages: cn.iocoder.springboot.lab40.zipkindemo.providerdemo.service # Dubbo 服务提供者的配置,对应 ProviderConfig 类 provider: filter: tracing ================================================ FILE: lab-40/lab-40-zipkin-dubbo/pom.xml ================================================ lab-40 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-40-zipkin-dubbo pom lab-40-zipkin-dubbo-api lab-40-zipkin-dubbo-provider lab-40-zipkin-dubbo-consumer ================================================ FILE: lab-40/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-40 pom lab-40-demo lab-40-springmvc lab-40-zipkin-dubbo lab-40-mysql lab-40-redis lab-40-mongodb lab-40-elasticsearch lab-40-kafka lab-40-rabbitmq lab-40-activemq lab-40-logback lab-40-opentracing ================================================ FILE: lab-40/《芋道 Spring Boot 链路追踪 Zipkin 入门》.md ================================================ ================================================ FILE: lab-41/deploy.sh ================================================ #!/bin/bash set -e # 基础 # export JAVA_HOME=/work/programs/jdk/jdk1.8.0_181 # export PATH=PATH=$PATH:$JAVA_HOME/bin # export CLASSPATH=$JAVA_HOME/jre/lib/rt.jar:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar DATE=$(date +%Y%m%d%H%M) # 基础路径 BASE_PATH=/work/projects/lab-41-demo01 # 编译后 jar 的地址。部署时,Jenkins 会上传 jar 包到该目录下 SOURCE_PATH=$BASE_PATH/build # 服务名称。同时约定部署服务的 jar 包名字也为它。 SERVER_NAME=lab-41-demo01 # 环境 PROFILES_ACTIVE=prod # 健康检查 URL HEALTH_CHECK_URL=http://127.0.0.1:8078/actuator/health/ # heapError 存放路径 HEAP_ERROR_PATH=$BASE_PATH/heapError # JVM 参数 JAVA_OPS="-Xms1024m -Xmx1024m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=$HEAP_ERROR_PATH" # JavaAgent 参数。可用于配置 SkyWalking 等链路追踪 JAVA_AGENT= # 备份 function backup() { # 如果不存在,则无需备份 if [ ! -f "$BASE_PATH/$SERVER_NAME.jar" ]; then echo "[backup] $BASE_PATH/$SERVER_NAME.jar 不存在,跳过备份" # 如果存在,则备份到 backup 目录下,使用时间作为后缀 else echo "[backup] 开始备份 $SERVER_NAME ..." cp $BASE_PATH/$SERVER_NAME.jar $BASE_PATH/backup/$SERVER_NAME-$DATE.jar echo "[backup] 备份 $SERVER_NAME 完成" fi } # 最新构建代码 移动到项目环境 function transfer() { echo "[transfer] 开始转移 $SERVER_NAME.jar" # 删除原 jar 包 if [ ! -f "$BASE_PATH/$SERVER_NAME.jar" ]; then echo "[transfer] $BASE_PATH/$SERVER_NAME.jar 不存在,跳过删除" else echo "[transfer] 移除 $BASE_PATH/$SERVER_NAME.jar 完成" rm $BASE_PATH/$SERVER_NAME.jar fi # 复制新 jar 包 echo "[transfer] 从 $SOURCE_PATH 中获取 $SERVER_NAME.jar 并迁移至 $BASE_PATH ...." cp $SOURCE_PATH/$SERVER_NAME.jar $BASE_PATH echo "[transfer] 转移 $SERVER_NAME.jar 完成" } # 停止 function stop() { echo "[stop] 开始停止 $BASE_PATH/$SERVER_NAME" PID=$(ps -ef | grep $BASE_PATH/$SERVER_NAME | grep -v "grep" | awk '{print $2}') # 如果 Java 服务启动中,则进行关闭 if [ -n "$PID" ]; then # 正常关闭 echo "[stop] $BASE_PATH/$SERVER_NAME 运行中,开始 kill [$PID]" kill -15 $PID # 等待最大 60 秒,直到关闭完成。 for ((i = 0; i < 60; i++)) do sleep 1 PID=$(ps -ef | grep $BASE_PATH/$SERVER_NAME | grep -v "grep" | awk '{print $2}') if [ -n "$PID" ]; then echo -e ".\c" else echo '[stop] 停止 $BASE_PATH/$SERVER_NAME 成功' break fi done # 如果正常关闭失败,那么进行强制 kill -9 进行关闭 if [ -n "$PID" ]; then echo "[stop] $BASE_PATH/$SERVER_NAME 失败,强制 kill -9 $PID" kill -9 $PID fi # 如果 Java 服务未启动,则无需关闭 else echo "[stop] $BASE_PATH/$SERVER_NAME 未启动,无需停止" fi } # 启动 function start() { # 开启启动前,打印启动参数 echo "[start] 开始启动 $BASE_PATH/$SERVER_NAME" echo "[start] JAVA_OPS: $JAVA_OPS" echo "[start] JAVA_AGENT: $JAVA_AGENT" echo "[start] PROFILES: $PROFILES_ACTIVE" # 开始启动 BUILD_ID=dontKillMe nohup java -server $JAVA_OPS $JAVA_AGENT -jar $BASE_PATH/$SERVER_NAME.jar --spring.profiles.active=$PROFILES_ACTIVE & echo "[start] 启动 $BASE_PATH/$SERVER_NAME 完成" } # 健康检查 function healthCheck() { # 如果配置健康检查,则进行健康检查 if [ -n "$HEALTH_CHECK_URL" ]; then # 健康检查最大 60 秒,直到健康检查通过 echo "[healthCheck] 开始通过 $HEALTH_CHECK_URL 地址,进行健康检查"; for ((i = 0; i < 60; i++)) do # 请求健康检查地址,只获取状态码。 result=`curl -I -m 10 -o /dev/null -s -w %{http_code} $HEALTH_CHECK_URL || echo "000"` # 如果状态码为 200,则说明健康检查通过 if [ "$result" == "200" ]; then echo "[healthCheck] 健康检查通过"; break # 如果状态码非 200,则说明未通过。sleep 1 秒后,继续重试 else echo -e ".\c" sleep 1 fi done # 健康检查未通过,则异常退出 shell 脚本,不继续部署。 if [ ! "$result" == "200" ]; then echo "[healthCheck] 健康检查不通过,可能部署失败。查看日志,自行判断是否启动成功"; tail -n 10 nohup.out exit 1; # 健康检查通过,打印最后 10 行日志,可能部署的人想看下日志。 else tail -n 10 nohup.out fi # 如果未配置健康检查,则 slepp 60 秒,人工看日志是否部署成功。 else echo "[healthCheck] HEALTH_CHECK_URL 未配置,开始 sleep 60 秒"; sleep 60 echo "[healthCheck] sleep 60 秒完成,查看日志,自行判断是否启动成功"; tail -n 50 nohup.out fi } # 部署 function deploy() { cd $BASE_PATH # 备份原 jar backup # 停止 Java 服务 stop # 部署新 jar transfer # 启动 Java 服务 start # 健康检查 healthCheck } deploy ================================================ FILE: lab-41/lab-41-demo01/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-41-demo01 jar org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator ${project.artifactId} org.springframework.boot spring-boot-maven-plugin ================================================ FILE: lab-41/lab-41-demo01/src/main/java/cn/iocoder/springboot/lab40/jenkinsdemo/Application.java ================================================ package cn.iocoder.springboot.lab40.jenkinsdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextClosedEvent; import org.springframework.stereotype.Component; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } @Component public class Listener implements ApplicationListener { @Override public void onApplicationEvent(ApplicationEvent event) { if (event instanceof ContextClosedEvent) { this.sleep(10); } } private void sleep(int seconds) { try { Thread.sleep(seconds * 1000L); } catch (InterruptedException ignore) { } } } } ================================================ FILE: lab-41/lab-41-demo01/src/main/java/cn/iocoder/springboot/lab40/jenkinsdemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab40.jenkinsdemo.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @GetMapping("/echo") public String echo() { return "echo"; } } ================================================ FILE: lab-41/lab-41-demo01/src/main/resources/application-dev.yaml ================================================ server: port: 8079 management: server: port: 8078 # 自定义端口,避免 Nginx 暴露出去 endpoints: web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 ================================================ FILE: lab-41/lab-41-demo01/src/main/resources/application-local.yaml ================================================ server: port: 8079 management: server: port: 8078 # 自定义端口,避免 Nginx 暴露出去 endpoints: web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 ================================================ FILE: lab-41/lab-41-demo01/src/main/resources/application-pre.yaml ================================================ server: port: 8079 management: server: port: 8078 # 自定义端口,避免 Nginx 暴露出去 endpoints: web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 ================================================ FILE: lab-41/lab-41-demo01/src/main/resources/application-prod.yaml ================================================ server: port: 8079 management: server: port: 8078 # 自定义端口,避免 Nginx 暴露出去 endpoints: web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 ================================================ FILE: lab-41/lab-41-demo01/src/main/resources/application-uat.yaml ================================================ server: port: 8079 management: server: port: 8078 # 自定义端口,避免 Nginx 暴露出去 endpoints: web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 ================================================ FILE: lab-41/lab-41-demo02/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-41-demo02 1.0.0 jar org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator ${project.artifactId} org.springframework.boot spring-boot-maven-plugin ================================================ FILE: lab-41/lab-41-demo02/src/main/java/cn/iocoder/springboot/lab40/jenkinsdemo/Demo02Application.java ================================================ package cn.iocoder.springboot.lab40.jenkinsdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Demo02Application { public static void main(String[] args) { SpringApplication.run(Demo02Application.class, args); } } ================================================ FILE: lab-41/lab-41-demo02/src/main/java/cn/iocoder/springboot/lab40/jenkinsdemo/actuate/ServerHealthIndicator.java ================================================ package cn.iocoder.springboot.lab40.jenkinsdemo.actuate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.actuate.health.AbstractHealthIndicator; import org.springframework.boot.actuate.health.Health; import org.springframework.boot.context.event.ApplicationFailedEvent; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextClosedEvent; import org.springframework.stereotype.Component; @Component public class ServerHealthIndicator extends AbstractHealthIndicator implements ApplicationListener { private Logger logger = LoggerFactory.getLogger(getClass()); /** * 是否在服务中 */ private volatile boolean inService = false; @Override protected void doHealthCheck(Health.Builder builder) { if (inService) { builder.up(); } else { builder.down(); } } @Override public void onApplicationEvent(ApplicationEvent event) { if (event instanceof ApplicationReadyEvent) { this.handleApplicationReadyEvent((ApplicationReadyEvent) event); } else if (event instanceof ApplicationFailedEvent) { this.handleApplicationFailedEvent((ApplicationFailedEvent) event); } else if (event instanceof ContextClosedEvent) { this.handleContextClosedEvent((ContextClosedEvent) event); } } @SuppressWarnings("unused") private void handleApplicationReadyEvent(ApplicationReadyEvent event) { this.inService = true; } @SuppressWarnings("unused") private void handleApplicationFailedEvent(ApplicationFailedEvent event) { this.inService = false; } @SuppressWarnings("unused") private void handleContextClosedEvent(ContextClosedEvent event) { // 标记不提供服务 this.inService = false; // sleep 等待负载均衡完成健康检查 for (int i = 0; i < 20; i++) { // TODO 20 需要配置 logger.info("[handleContextClosedEvent][优雅关闭,第 {} sleep 等待负载均衡完成健康检查]", i); try { Thread.sleep(1000L); } catch (InterruptedException ignore) { } } } } ================================================ FILE: lab-41/lab-41-demo02/src/main/java/cn/iocoder/springboot/lab40/jenkinsdemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab40.jenkinsdemo.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @GetMapping("/echo") public String echo() { return "echo"; } } ================================================ FILE: lab-41/lab-41-demo02/src/main/resources/application-dev.yaml ================================================ server: port: 8079 management: server: port: 8078 # 自定义端口,避免 Nginx 暴露出去 endpoint: health: show-details: always # 配置展示明细,这样自定义的 ServerHealthIndicator 才可以被访问 web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 ================================================ FILE: lab-41/lab-41-demo02/src/main/resources/application-local.yaml ================================================ server: port: 8079 management: server: port: 8078 # 自定义端口,避免 Nginx 暴露出去 endpoint: health: show-details: always # 配置展示明细,这样自定义的 ServerHealthIndicator 才可以被访问 web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 ================================================ FILE: lab-41/lab-41-demo02/src/main/resources/application-pre.yaml ================================================ server: port: 8079 management: server: port: 8078 # 自定义端口,避免 Nginx 暴露出去 endpoint: health: show-details: always # 配置展示明细,这样自定义的 ServerHealthIndicator 才可以被访问 web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 ================================================ FILE: lab-41/lab-41-demo02/src/main/resources/application-prod.yaml ================================================ server: port: 8079 management: server: port: 8078 # 自定义端口,避免 Nginx 暴露出去 endpoint: health: show-details: always # 配置展示明细,这样自定义的 ServerHealthIndicator 才可以被访问 web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 ================================================ FILE: lab-41/lab-41-demo02/src/main/resources/application-uat.yaml ================================================ server: port: 8079 management: server: port: 8078 # 自定义端口,避免 Nginx 暴露出去 endpoint: health: show-details: always # 配置展示明细,这样自定义的 ServerHealthIndicator 才可以被访问 web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 ================================================ FILE: lab-41/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-41 pom lab-41-demo01 lab-41-demo02 ================================================ FILE: lab-41/《芋道 Spring Boot 持续交付 Jenkins 入门》.md ================================================ ================================================ FILE: lab-42/lab-42-demo01/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-42-demo01 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 5.1.46 org.springframework.boot spring-boot-starter-test test com.h2database h2 test ================================================ FILE: lab-42/lab-42-demo01/src/main/java/cn/iocoder/springboot/lab23/testdemo/Application.java ================================================ package cn.iocoder.springboot.lab23.testdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-42/lab-42-demo01/src/main/java/cn/iocoder/springboot/lab23/testdemo/controller/UserController.java ================================================ package cn.iocoder.springboot.lab23.testdemo.controller; import cn.iocoder.springboot.lab23.testdemo.dataobject.UserDO; import cn.iocoder.springboot.lab23.testdemo.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; /** * 用户 Controller */ @RestController @RequestMapping("/user") public class UserController { @Autowired private UserService userService; /** * 获得指定用户编号的用户 * * @param id 用户编号 * @return 用户 */ @GetMapping("/get") // URL 修改成 /get public UserDO get(@RequestParam("id") Integer id) { // 查询并返回用户 return userService.get(id); } } ================================================ FILE: lab-42/lab-42-demo01/src/main/java/cn/iocoder/springboot/lab23/testdemo/dao/UserDao.java ================================================ package cn.iocoder.springboot.lab23.testdemo.dao; import cn.iocoder.springboot.lab23.testdemo.dataobject.UserDO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; @Repository public class UserDao { @Autowired private JdbcTemplate template; public UserDO selectById(Integer id) { return template.queryForObject("SELECT id, username, password FROM t_user WHERE id = ?", new BeanPropertyRowMapper<>(UserDO.class), // 结果转换成对应的对象 id); } } ================================================ FILE: lab-42/lab-42-demo01/src/main/java/cn/iocoder/springboot/lab23/testdemo/dataobject/UserDO.java ================================================ package cn.iocoder.springboot.lab23.testdemo.dataobject; /** * 用户 DO */ public class UserDO { /** * 用户编号 */ private Integer id; /** * 账号 */ private String username; /** * 密码(明文) * * ps:生产环境下,千万不要明文噢 */ private String password; public Integer getId() { return id; } public UserDO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserDO setUsername(String username) { this.username = username; return this; } public String getPassword() { return password; } public UserDO setPassword(String password) { this.password = password; return this; } } ================================================ FILE: lab-42/lab-42-demo01/src/main/java/cn/iocoder/springboot/lab23/testdemo/service/UserService.java ================================================ package cn.iocoder.springboot.lab23.testdemo.service; import cn.iocoder.springboot.lab23.testdemo.dao.UserDao; import cn.iocoder.springboot.lab23.testdemo.dataobject.UserDO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserService { @Autowired private UserDao userDao; public UserDO get(Integer id) { return userDao.selectById(id); } } ================================================ FILE: lab-42/lab-42-demo01/src/main/resources/application.yaml ================================================ spring: # datasource 数据源配置内容 datasource: url: jdbc:mysql://127.0.0.1:3306/lab-39-mysql?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: ================================================ FILE: lab-42/lab-42-demo01/src/test/java/cn/iocoder/springboot/lab23/testdemo/controller/UserControllerTest.java ================================================ package cn.iocoder.springboot.lab23.testdemo.controller; import cn.iocoder.springboot.lab23.testdemo.dataobject.UserDO; import cn.iocoder.springboot.lab23.testdemo.service.UserService; import org.hamcrest.core.IsEqual; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; /** * UserController 单元测试 */ @RunWith(SpringRunner.class) @SpringBootTest @AutoConfigureMockMvc public class UserControllerTest { @Autowired private MockMvc mvc; @MockBean private UserService userService; @Test public void testGet() throws Exception { // Mock UserService 的 get 方法 Mockito.when(userService.get(1)).thenReturn( new UserDO().setId(1).setUsername("username:1").setPassword("password:1")); // 查询用户 ResultActions resultActions = mvc.perform(MockMvcRequestBuilders.get("/user/get?id=1")); // 校验响应状态码 resultActions.andExpect(MockMvcResultMatchers.status().isOk()); // 响应状态码 200 // 校验响应内容方式一:直接全部匹配 resultActions.andExpect(MockMvcResultMatchers.content().json("{\n" + " \"id\": 1,\n" + " \"username\": \"username:1\",\n" + " \"password\": \"password:1\"\n" + "}", true)); // 响应结果 // 校验响应内容方式二:逐个字段匹配 resultActions.andExpect(MockMvcResultMatchers.jsonPath("id", IsEqual.equalTo(1))); resultActions.andExpect(MockMvcResultMatchers.jsonPath("username", IsEqual.equalTo("username:1"))); resultActions.andExpect(MockMvcResultMatchers.jsonPath("password", IsEqual.equalTo("password:1"))); } } ================================================ FILE: lab-42/lab-42-demo01/src/test/java/cn/iocoder/springboot/lab23/testdemo/dao/UserDaoTest.java ================================================ package cn.iocoder.springboot.lab23.testdemo.dao; import cn.iocoder.springboot.lab23.testdemo.dataobject.UserDO; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.jdbc.Sql; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest public class UserDaoTest { @Autowired private UserDao userDao; @Test @Sql(scripts = "/sql/create_tables.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) @Sql(statements = "INSERT INTO `t_user`(`id`, `username`, `password`) VALUES (1, 'username:1', 'password:1');", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) @Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) public void testSelectById() { // 查询用户 UserDO user = userDao.selectById(1); // 校验结果 Assert.assertEquals("编号不匹配", 1, (int) user.getId()); Assert.assertEquals("用户名不匹配", "username:1", user.getUsername()); Assert.assertEquals("密码不匹配", "password:1", user.getPassword()); } } ================================================ FILE: lab-42/lab-42-demo01/src/test/java/cn/iocoder/springboot/lab23/testdemo/package-info.java ================================================ package cn.iocoder.springboot.lab23.testdemo; ================================================ FILE: lab-42/lab-42-demo01/src/test/java/cn/iocoder/springboot/lab23/testdemo/service/UserServiceTest.java ================================================ package cn.iocoder.springboot.lab23.testdemo.service; import cn.iocoder.springboot.lab23.testdemo.dao.UserDao; import cn.iocoder.springboot.lab23.testdemo.dataobject.UserDO; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest public class UserServiceTest { @MockBean private UserDao userDao; @Autowired private UserService userService; @Test public void testGet() { // Mock UserDao 的 selectById 方法 Mockito.when(userDao.selectById(1)).thenReturn( new UserDO().setId(1).setUsername("username:1").setPassword("password:1")); // 查询用户 UserDO user = userService.get(1); // 校验结果 Assert.assertEquals("编号不匹配", 1, (int) user.getId()); Assert.assertEquals("用户名不匹配", "username:1", user.getUsername()); Assert.assertEquals("密码不匹配", "password:1", user.getPassword()); } } ================================================ FILE: lab-42/lab-42-demo01/src/test/resources/application.yaml ================================================ spring: # datasource 数据源配置内容 datasource: url: jdbc:h2:mem:testdb driver-class-name: org.h2.Driver username: sa password: ================================================ FILE: lab-42/lab-42-demo01/src/test/resources/sql/clean.sql ================================================ DROP TABLE `t_user` ================================================ FILE: lab-42/lab-42-demo01/src/test/resources/sql/create_tables.sql ================================================ CREATE TABLE `t_user` ( `id` INT AUTO_INCREMENT PRIMARY KEY COMMENT '用户编号', `username` VARCHAR(64) NOT NULL COMMENT '账号', `password` VARCHAR(64) NOT NULL COMMENT '密码' ); ================================================ FILE: lab-42/lab-42-demo02/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-42-demo02 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 5.1.46 org.springframework.boot spring-boot-starter-test test com.h2database h2 test ================================================ FILE: lab-42/lab-42-demo02/src/main/java/cn/iocoder/springboot/lab23/testdemo/Application.java ================================================ package cn.iocoder.springboot.lab23.testdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-42/lab-42-demo02/src/main/java/cn/iocoder/springboot/lab23/testdemo/controller/UserController.java ================================================ package cn.iocoder.springboot.lab23.testdemo.controller; import cn.iocoder.springboot.lab23.testdemo.dataobject.UserDO; import cn.iocoder.springboot.lab23.testdemo.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; /** * 用户 Controller */ @RestController @RequestMapping("/user") public class UserController { @Autowired private UserService userService; /** * 获得指定用户编号的用户 * * @param id 用户编号 * @return 用户 */ @GetMapping("/get") // URL 修改成 /get public UserDO get(@RequestParam("id") Integer id) { // 查询并返回用户 return userService.get(id); } } ================================================ FILE: lab-42/lab-42-demo02/src/main/java/cn/iocoder/springboot/lab23/testdemo/dao/UserDao.java ================================================ package cn.iocoder.springboot.lab23.testdemo.dao; import cn.iocoder.springboot.lab23.testdemo.dataobject.UserDO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; @Repository public class UserDao { @Autowired private JdbcTemplate template; public UserDO selectById(Integer id) { return template.queryForObject("SELECT id, username, password FROM t_user WHERE id = ?", new BeanPropertyRowMapper<>(UserDO.class), // 结果转换成对应的对象 id); } public UserDO selectByUsername(String username) { return new UserDO().setId(1) .setUsername(username) .setPassword(null); } } ================================================ FILE: lab-42/lab-42-demo02/src/main/java/cn/iocoder/springboot/lab23/testdemo/dataobject/UserDO.java ================================================ package cn.iocoder.springboot.lab23.testdemo.dataobject; /** * 用户 DO */ public class UserDO { /** * 用户编号 */ private Integer id; /** * 账号 */ private String username; /** * 密码(明文) * * ps:生产环境下,千万不要明文噢 */ private String password; public Integer getId() { return id; } public UserDO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserDO setUsername(String username) { this.username = username; return this; } public String getPassword() { return password; } public UserDO setPassword(String password) { this.password = password; return this; } } ================================================ FILE: lab-42/lab-42-demo02/src/main/java/cn/iocoder/springboot/lab23/testdemo/service/UserService.java ================================================ package cn.iocoder.springboot.lab23.testdemo.service; import cn.iocoder.springboot.lab23.testdemo.dao.UserDao; import cn.iocoder.springboot.lab23.testdemo.dataobject.UserDO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserService { @Autowired private UserDao userDao; public UserDO get(Integer id) { return userDao.selectById(id); } public boolean exists(String username) { return true; } public Integer add(String username, String password) { if (userDao.selectByUsername(username) != null) { return null; } return 1; } } ================================================ FILE: lab-42/lab-42-demo02/src/main/resources/application.yaml ================================================ spring: # datasource 数据源配置内容 datasource: url: jdbc:mysql://127.0.0.1:3306/lab-39-mysql?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: ================================================ FILE: lab-42/lab-42-demo02/src/test/java/cn/iocoder/springboot/lab23/testdemo/controller/UserControllerTest.java ================================================ package cn.iocoder.springboot.lab23.testdemo.controller; import cn.iocoder.springboot.lab23.testdemo.dataobject.UserDO; import cn.iocoder.springboot.lab23.testdemo.service.UserService; import org.hamcrest.core.IsEqual; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; /** * UserController 单元测试 */ @RunWith(SpringRunner.class) @SpringBootTest @AutoConfigureMockMvc public class UserControllerTest { @Autowired private MockMvc mvc; @MockBean private UserService userService; @Test public void testGet() throws Exception { // Mock UserService 的 get 方法 Mockito.when(userService.get(1)).thenReturn( new UserDO().setId(1).setUsername("username:1").setPassword("password:1")); // 查询用户 ResultActions resultActions = mvc.perform(MockMvcRequestBuilders.get("/user/get?id=1")); // 校验响应状态码 resultActions.andExpect(MockMvcResultMatchers.status().isOk()); // 响应状态码 200 // 校验响应内容方式一:直接全部匹配 resultActions.andExpect(MockMvcResultMatchers.content().json("{\n" + " \"id\": 1,\n" + " \"username\": \"username:1\",\n" + " \"password\": \"password:1\"\n" + "}", true)); // 响应结果 // 校验响应内容方式二:逐个字段匹配 resultActions.andExpect(MockMvcResultMatchers.jsonPath("id", IsEqual.equalTo(1))); resultActions.andExpect(MockMvcResultMatchers.jsonPath("username", IsEqual.equalTo("username:1"))); resultActions.andExpect(MockMvcResultMatchers.jsonPath("password", IsEqual.equalTo("password:1"))); } } ================================================ FILE: lab-42/lab-42-demo02/src/test/java/cn/iocoder/springboot/lab23/testdemo/dao/UserDaoTest.java ================================================ package cn.iocoder.springboot.lab23.testdemo.dao; import cn.iocoder.springboot.lab23.testdemo.dataobject.UserDO; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.jdbc.Sql; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest public class UserDaoTest { @Autowired private UserDao userDao; @Test @Sql(scripts = "/sql/create_tables.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) @Sql(statements = "INSERT INTO `t_user`(`id`, `username`, `password`) VALUES (1, 'username:1', 'password:1');", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) @Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) public void testSelectById() { // 查询用户 UserDO user = userDao.selectById(1); // 校验结果 Assert.assertEquals("编号不匹配", 1, (int) user.getId()); Assert.assertEquals("用户名不匹配", "username:1", user.getUsername()); Assert.assertEquals("密码不匹配", "password:1", user.getPassword()); } } ================================================ FILE: lab-42/lab-42-demo02/src/test/java/cn/iocoder/springboot/lab23/testdemo/package-info.java ================================================ package cn.iocoder.springboot.lab23.testdemo; ================================================ FILE: lab-42/lab-42-demo02/src/test/java/cn/iocoder/springboot/lab23/testdemo/service/UserServiceTest.java ================================================ package cn.iocoder.springboot.lab23.testdemo.service; import cn.iocoder.springboot.lab23.testdemo.dao.UserDao; import cn.iocoder.springboot.lab23.testdemo.dataobject.UserDO; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest public class UserServiceTest { @MockBean private UserDao userDao; @Autowired private UserService userService; @Test public void testGet() { // Mock UserDao 的 selectById 方法 Mockito.when(userDao.selectById(1)).thenReturn( new UserDO().setId(1).setUsername("username:1").setPassword("password:1")); // 查询用户 UserDO user = userService.get(1); // 校验结果 Assert.assertEquals("编号不匹配", 1, (int) user.getId()); Assert.assertEquals("用户名不匹配", "username:1", user.getUsername()); Assert.assertEquals("密码不匹配", "password:1", user.getPassword()); } } ================================================ FILE: lab-42/lab-42-demo02/src/test/java/cn/iocoder/springboot/lab23/testdemo/service/UserServiceTest2.java ================================================ package cn.iocoder.springboot.lab23.testdemo.service; import cn.iocoder.springboot.lab23.testdemo.dao.UserDao; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest public class UserServiceTest2 { @Autowired private UserService userService; @SpyBean private UserDao userDao; @Test public void testAddSuccess() { System.out.println("testAddSuccess"); // Mock UserService 的 exists 方法 Mockito.when(userDao.selectByUsername("username")).thenReturn(null); Assert.assertNotNull("注册返回为 null,注册失败", userService.add("username", "password")); } @Test public void testAddFailure() { System.out.println("testAddFailure"); Assert.assertNull("注册返回为 null,注册失败", userService.add("username", "password")); } } ================================================ FILE: lab-42/lab-42-demo02/src/test/resources/application.yaml ================================================ spring: # datasource 数据源配置内容 datasource: url: jdbc:h2:mem:testdb driver-class-name: org.h2.Driver username: sa password: ================================================ FILE: lab-42/lab-42-demo02/src/test/resources/sql/clean.sql ================================================ DROP TABLE `t_user` ================================================ FILE: lab-42/lab-42-demo02/src/test/resources/sql/create_tables.sql ================================================ CREATE TABLE `t_user` ( `id` INT AUTO_INCREMENT PRIMARY KEY COMMENT '用户编号', `username` VARCHAR(64) NOT NULL COMMENT '账号', `password` VARCHAR(64) NOT NULL COMMENT '密码' ); ================================================ FILE: lab-42/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-42 pom lab-42-demo01 lab-42-demo02 ================================================ FILE: lab-42/《芋道 Spring Boot 单元测试 Test 入门》.md ================================================ ================================================ FILE: lab-43/lab-43-demo/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-43-demo org.springframework.boot spring-boot-starter org.springframework.boot spring-boot-configuration-processor true ================================================ FILE: lab-43/lab-43-demo/src/main/java/cn/iocoder/springboot/lab43/propertydemo/Application.java ================================================ package cn.iocoder.springboot.lab43.propertydemo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.stereotype.Component; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } @Component public class OrderPropertiesCommandLineRunner implements CommandLineRunner { private final Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private OrderProperties orderProperties; @Override public void run(String... args) { logger.info("payTimeoutSeconds:" + orderProperties.getPayTimeoutSeconds()); logger.info("createFrequencySeconds:" + orderProperties.getCreateFrequencySeconds()); } } @Component public class ValueCommandLineRunner implements CommandLineRunner { private final Logger logger = LoggerFactory.getLogger(getClass()); @Value("${order.pay-timeout-seconds}") private Integer payTimeoutSeconds; @Value("${order.create-frequency-seconds}") private Integer createFrequencySeconds; // @Value("${order.desc}") // private String desc; @Override public void run(String... args) { logger.info("payTimeoutSeconds:" + payTimeoutSeconds); logger.info("createFrequencySeconds:" + createFrequencySeconds); } } } ================================================ FILE: lab-43/lab-43-demo/src/main/java/cn/iocoder/springboot/lab43/propertydemo/OrderProperties.java ================================================ package cn.iocoder.springboot.lab43.propertydemo; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component @ConfigurationProperties(prefix = "order") public class OrderProperties { /** * 订单支付超时时长,单位:秒。 */ private Integer payTimeoutSeconds; /** * 订单创建频率,单位:秒 */ private Integer createFrequencySeconds; // /** // * 配置描述 // */ // private String desc; public Integer getPayTimeoutSeconds() { return payTimeoutSeconds; } public OrderProperties setPayTimeoutSeconds(Integer payTimeoutSeconds) { this.payTimeoutSeconds = payTimeoutSeconds; return this; } public Integer getCreateFrequencySeconds() { return createFrequencySeconds; } public OrderProperties setCreateFrequencySeconds(Integer createFrequencySeconds) { this.createFrequencySeconds = createFrequencySeconds; return this; } // public String getDesc() { // return desc; // } // // public OrderProperties setDesc(String desc) { // this.desc = desc; // return this; // } } ================================================ FILE: lab-43/lab-43-demo/src/main/resources/application.yaml ================================================ order: pay-timeout-seconds: 120 # 订单支付超时时长,单位:秒。 create-frequency-seconds: 10 # 订单创建频率,单位:秒 # desc: "订单支付超时时长为 ${order.pay-timeout-seconds} 秒,订单创建频率为 ${order.create-frequency-seconds} 秒" ================================================ FILE: lab-43/lab-43-demo-configname/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-43-demo-configname org.springframework.boot spring-boot-starter ================================================ FILE: lab-43/lab-43-demo-configname/src/main/java/cn/iocoder/springboot/lab43/propertydemo/Application.java ================================================ package cn.iocoder.springboot.lab43.propertydemo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.config.ConfigFileApplicationListener; import org.springframework.stereotype.Component; @SpringBootApplication public class Application { /** * 设置需要读取的配置文件的名字。 * 基于 {@link org.springframework.boot.context.config.ConfigFileApplicationListener#CONFIG_NAME_PROPERTY} 实现。 */ private static final String CONFIG_NAME_VALUE = "application,rpc"; public static void main(String[] args) { // 设置环境变量 System.setProperty(ConfigFileApplicationListener.CONFIG_NAME_PROPERTY, CONFIG_NAME_VALUE); // 启动 Spring Boot 应用 SpringApplication.run(Application.class, args); } @Component public class ValueCommandLineRunner implements CommandLineRunner { private final Logger logger = LoggerFactory.getLogger(getClass()); @Value("${application-test}") private String applicationTest; @Value("${rpc-test}") private String rpcTest; @Override public void run(String... args) { logger.info("applicationTest:" + applicationTest); logger.info("rpcTest:" + rpcTest); } } } ================================================ FILE: lab-43/lab-43-demo-configname/src/main/resources/application.yaml ================================================ application-test: hahaha ================================================ FILE: lab-43/lab-43-demo-configname/src/main/resources/rpc.yaml ================================================ rpc-test: yeah ================================================ FILE: lab-43/lab-43-demo-jasypt/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-43-demo-jasypt org.springframework.boot spring-boot-starter com.github.ulisesbocchio jasypt-spring-boot-starter 3.0.2 org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-43/lab-43-demo-jasypt/src/main/java/cn/iocoder/springboot/lab43/propertydemo/Application.java ================================================ package cn.iocoder.springboot.lab43.propertydemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-43/lab-43-demo-jasypt/src/main/resources/application.yaml ================================================ spring: application: # name: ENC(xQZuD8KnkqzIGep0FFH0DYJ3Re9TrKTdvu2fxIlWNpwFcdNGhkpCag==) # name: ENC(KoaHnIhRGiCdWh0T2lby899Cov6MyiAXrW5PadJ3XFY=) name: demo-application jasypt: # jasypt 配置项,对应 JasyptEncryptorConfigurationProperties 配置类 encryptor: algorithm: PBEWithMD5AndDES # 加密算法 password: ${JASYPT_PASSWORD} # 加密秘钥 ================================================ FILE: lab-43/lab-43-demo-jasypt/src/test/java/cn/iocoder/springboot/lab43/propertydemo/JasyptTest.java ================================================ package cn.iocoder.springboot.lab43.propertydemo; import org.jasypt.encryption.StringEncryptor; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest public class JasyptTest { @Autowired private StringEncryptor encryptor; @Test public void encode() { String applicationName = "demo-application"; System.out.println(encryptor.encrypt(applicationName)); } @Value("${spring.application.name}") private String applicationName; @Test public void print() { System.out.println(applicationName); } @Value("${jasypt.encryptor.password}") private String password; } ================================================ FILE: lab-43/lab-43-demo-profiles/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-43-demo-profiles org.springframework.boot spring-boot-starter-web ================================================ FILE: lab-43/lab-43-demo-profiles/src/main/java/cn/iocoder/springboot/lab43/propertydemo/ProfilesApplication.java ================================================ package cn.iocoder.springboot.lab43.propertydemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ProfilesApplication { public static void main(String[] args) { SpringApplication.run(ProfilesApplication.class, args); } } ================================================ FILE: lab-43/lab-43-demo-profiles/src/main/resources/application-dev.yaml ================================================ server: port: 8081 ================================================ FILE: lab-43/lab-43-demo-profiles/src/main/resources/application-local.yaml ================================================ server: port: 8080 ================================================ FILE: lab-43/lab-43-demo-profiles/src/main/resources/application-pre.yaml ================================================ server: port: 8083 ================================================ FILE: lab-43/lab-43-demo-profiles/src/main/resources/application-prod.yaml ================================================ server: port: 8084 ================================================ FILE: lab-43/lab-43-demo-profiles/src/main/resources/application-uat.yaml ================================================ server: port: 8082 ================================================ FILE: lab-43/lab-43-demo-profiles/src/main/resources/application.yaml ================================================ server: port: 7070 spring: application: name: demo-application ================================================ FILE: lab-43/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-43 pom lab-43-demo lab-43-demo-profiles lab-43-demo-jasypt lab-43-demo-configname ================================================ FILE: lab-43/《芋道 Spring Boot 配置文件入门》.md ================================================ ================================================ FILE: lab-44/lab-44-nacos-config-demo/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-44-nacos-config-demo org.springframework.boot spring-boot-starter com.alibaba.boot nacos-config-spring-boot-starter 0.2.4 ================================================ FILE: lab-44/lab-44-nacos-config-demo/src/main/java/cn/iocoder/springboot/lab44/nacosdemo/Application.java ================================================ package cn.iocoder.springboot.lab44.nacosdemo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.stereotype.Component; @SpringBootApplication // @NacosPropertySource(dataId = "example", type = ConfigType.YAML) public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } @Component public class OrderPropertiesCommandLineRunner implements CommandLineRunner { private final Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private OrderProperties orderProperties; @Override public void run(String... args) { logger.info("payTimeoutSeconds:" + orderProperties.getPayTimeoutSeconds()); logger.info("createFrequencySeconds:" + orderProperties.getCreateFrequencySeconds()); } } @Component public class ValueCommandLineRunner implements CommandLineRunner { private final Logger logger = LoggerFactory.getLogger(getClass()); // @NacosValue(value = "${order.pay-timeout-seconds}") @Value(value = "${order.pay-timeout-seconds}") private Integer payTimeoutSeconds; // @NacosValue(value = "${order.create-frequency-seconds}") @Value(value = "${order.create-frequency-seconds}") private Integer createFrequencySeconds; @Override public void run(String... args) { logger.info("payTimeoutSeconds:" + payTimeoutSeconds); logger.info("createFrequencySeconds:" + createFrequencySeconds); } } } ================================================ FILE: lab-44/lab-44-nacos-config-demo/src/main/java/cn/iocoder/springboot/lab44/nacosdemo/OrderProperties.java ================================================ package cn.iocoder.springboot.lab44.nacosdemo; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component //@NacosConfigurationProperties(prefix = "order", dataId = "${nacos.config.data-id}", type = ConfigType.YAML) @ConfigurationProperties(prefix = "order") public class OrderProperties { /** * 订单支付超时时长,单位:秒。 */ private Integer payTimeoutSeconds; /** * 订单创建频率,单位:秒 */ private Integer createFrequencySeconds; // /** // * 配置描述 // */ // private String desc; public Integer getPayTimeoutSeconds() { return payTimeoutSeconds; } public OrderProperties setPayTimeoutSeconds(Integer payTimeoutSeconds) { this.payTimeoutSeconds = payTimeoutSeconds; return this; } public Integer getCreateFrequencySeconds() { return createFrequencySeconds; } public OrderProperties setCreateFrequencySeconds(Integer createFrequencySeconds) { this.createFrequencySeconds = createFrequencySeconds; return this; } // public String getDesc() { // return desc; // } // // public OrderProperties setDesc(String desc) { // this.desc = desc; // return this; // } } ================================================ FILE: lab-44/lab-44-nacos-config-demo/src/main/resources/application.yaml ================================================ nacos: # Nacos 配置中心的配置项,对应 NacosConfigProperties 配置类 config: server-addr: 127.0.0.1:18848 # Nacos 服务器地址 bootstrap: enable: true # 是否开启 Nacos 配置预加载功能。默认为 false。 log-enable: true # 是否开启 Nacos 支持日志级别的加载时机。默认为 false。 data-id: example # 使用的 Nacos 配置集的 dataId。 type: YAML # 使用的 Nacos 配置集的配置格式。默认为 PROPERTIES。 group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP。 namespace: # 使用的 Nacos 的命名空间,默认为 null。 ================================================ FILE: lab-44/lab-44-nacos-config-demo-actuator/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-44-nacos-config-demo-actuator org.springframework.boot spring-boot-starter-web com.alibaba.boot nacos-config-spring-boot-starter 0.2.4 org.springframework.boot spring-boot-starter-actuator com.alibaba.boot nacos-config-spring-boot-actuator 0.2.4 ================================================ FILE: lab-44/lab-44-nacos-config-demo-actuator/src/main/java/cn/iocoder/springboot/lab44/nacosdemo/Application.java ================================================ package cn.iocoder.springboot.lab44.nacosdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication // @NacosPropertySource(dataId = "example", type = ConfigType.YAML) public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-44/lab-44-nacos-config-demo-actuator/src/main/java/cn/iocoder/springboot/lab44/nacosdemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab44.nacosdemo.controller; import cn.iocoder.springboot.lab44.nacosdemo.properties.TestProperties; import com.alibaba.nacos.api.config.annotation.NacosValue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { // @Value("${test}") @NacosValue(value = "${test}", autoRefreshed = true) private String test; @GetMapping("/test") public String test() { return test; } @Autowired private TestProperties testProperties; @GetMapping("/test_properties") public TestProperties testProperties() { return testProperties; } private Logger logger = LoggerFactory.getLogger(getClass()); @GetMapping("/logger") public void logger() { logger.debug("[logger][测试一下]"); } } ================================================ FILE: lab-44/lab-44-nacos-config-demo-actuator/src/main/java/cn/iocoder/springboot/lab44/nacosdemo/listener/LoggingSystemConfigListener.java ================================================ package cn.iocoder.springboot.lab44.nacosdemo.listener; import com.alibaba.nacos.api.config.ConfigType; import com.alibaba.nacos.api.config.annotation.NacosConfigListener; import com.alibaba.nacos.spring.util.parse.DefaultYamlConfigParse; import org.springframework.boot.logging.LogLevel; import org.springframework.boot.logging.LoggingSystem; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.util.Properties; @Component public class LoggingSystemConfigListener { /** * 日志配置项的前缀 */ private static final String LOGGER_TAG = "logging.level."; @Resource private LoggingSystem loggingSystem; @NacosConfigListener(dataId = "${nacos.config.data-id}", type = ConfigType.YAML, timeout = 5000) public void onChange(String newLog) throws Exception { // 使用 DefaultYamlConfigParse 工具类,解析配置 Properties properties = new DefaultYamlConfigParse().parse(newLog); // 遍历配置集的每个配置项,判断是否是 logging.level 配置项 for (Object t : properties.keySet()) { String key = String.valueOf(t); // 如果是 logging.level 配置项,则设置其对应的日志级别 if (key.startsWith(LOGGER_TAG)) { // 获得日志级别 String strLevel = properties.getProperty(key, "info"); LogLevel level = LogLevel.valueOf(strLevel.toUpperCase()); // 设置日志级别到 LoggingSystem 中 loggingSystem.setLogLevel(key.replace(LOGGER_TAG, ""), level); } } } } ================================================ FILE: lab-44/lab-44-nacos-config-demo-actuator/src/main/java/cn/iocoder/springboot/lab44/nacosdemo/properties/TestProperties.java ================================================ package cn.iocoder.springboot.lab44.nacosdemo.properties; import com.alibaba.nacos.api.config.ConfigType; import com.alibaba.nacos.api.config.annotation.NacosConfigurationProperties; import org.springframework.stereotype.Component; @Component @NacosConfigurationProperties(prefix = "", dataId = "${nacos.config.data-id}", type = ConfigType.YAML, autoRefreshed = true) public class TestProperties { /** * 测试属性 */ private String test; public String getTest() { return test; } public TestProperties setTest(String test) { this.test = test; return this; } } ================================================ FILE: lab-44/lab-44-nacos-config-demo-actuator/src/main/resources/application.yaml ================================================ nacos: # Nacos 配置中心的配置项,对应 NacosConfigProperties 配置类 config: server-addr: 127.0.0.1:18848 # Nacos 服务器地址 bootstrap: enable: true # 是否开启 Nacos 配置预加载功能。默认为 false。 log-enable: true # 是否开启 Nacos 支持日志级别的加载时机。默认为 false。 data-id: example-auto-refresh # 使用的 Nacos 配置集的 dataId。 type: YAML # 使用的 Nacos 配置集的配置格式。默认为 PROPERTIES。 group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP。 namespace: # 使用的 Nacos 的命名空间,默认为 null。 auto-refresh: true # 是否自动刷新,默认为 false。 management: endpoint: # Health 端点配置项,对应 HealthProperties 配置类 health: show-details: ALWAYS # 何时显示完整的健康信息。默认为 NEVER 都不展示。可选 WHEN_AUTHORIZED 当经过授权的用户;可选 ALWAYS 总是展示。 endpoints: # Actuator HTTP 配置项,对应 WebEndpointProperties 配置类 web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 ================================================ FILE: lab-44/lab-44-nacos-config-demo-auto-refresh/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-44-nacos-config-demo-auto-refresh org.springframework.boot spring-boot-starter-web com.alibaba.boot nacos-config-spring-boot-starter 0.2.4 ================================================ FILE: lab-44/lab-44-nacos-config-demo-auto-refresh/src/main/java/cn/iocoder/springboot/lab44/nacosdemo/Application.java ================================================ package cn.iocoder.springboot.lab44.nacosdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication // @NacosPropertySource(dataId = "example", type = ConfigType.YAML) public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-44/lab-44-nacos-config-demo-auto-refresh/src/main/java/cn/iocoder/springboot/lab44/nacosdemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab44.nacosdemo.controller; import cn.iocoder.springboot.lab44.nacosdemo.properties.TestProperties; import com.alibaba.nacos.api.config.annotation.NacosValue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { // @Value("${test}") @NacosValue(value = "${test}", autoRefreshed = true) private String test; @GetMapping("/test") public String test() { return test; } @Autowired private TestProperties testProperties; @GetMapping("/test_properties") public TestProperties testProperties() { return testProperties; } private Logger logger = LoggerFactory.getLogger(getClass()); @GetMapping("/logger") public void logger() { logger.debug("[logger][测试一下]"); } } ================================================ FILE: lab-44/lab-44-nacos-config-demo-auto-refresh/src/main/java/cn/iocoder/springboot/lab44/nacosdemo/listener/LoggingSystemConfigListener.java ================================================ package cn.iocoder.springboot.lab44.nacosdemo.listener; import com.alibaba.nacos.api.config.ConfigType; import com.alibaba.nacos.api.config.annotation.NacosConfigListener; import com.alibaba.nacos.spring.util.parse.DefaultYamlConfigParse; import org.springframework.boot.logging.LogLevel; import org.springframework.boot.logging.LoggingSystem; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.util.Properties; @Component public class LoggingSystemConfigListener { /** * 日志配置项的前缀 */ private static final String LOGGER_TAG = "logging.level."; @Resource private LoggingSystem loggingSystem; @NacosConfigListener(dataId = "${nacos.config.data-id}", type = ConfigType.YAML, timeout = 5000) public void onChange(String newLog) throws Exception { // 使用 DefaultYamlConfigParse 工具类,解析配置 Properties properties = new DefaultYamlConfigParse().parse(newLog); // 遍历配置集的每个配置项,判断是否是 logging.level 配置项 for (Object t : properties.keySet()) { String key = String.valueOf(t); // 如果是 logging.level 配置项,则设置其对应的日志级别 if (key.startsWith(LOGGER_TAG)) { // 获得日志级别 String strLevel = properties.getProperty(key, "info"); LogLevel level = LogLevel.valueOf(strLevel.toUpperCase()); // 设置日志级别到 LoggingSystem 中 loggingSystem.setLogLevel(key.replace(LOGGER_TAG, ""), level); } } } } ================================================ FILE: lab-44/lab-44-nacos-config-demo-auto-refresh/src/main/java/cn/iocoder/springboot/lab44/nacosdemo/properties/TestProperties.java ================================================ package cn.iocoder.springboot.lab44.nacosdemo.properties; import com.alibaba.nacos.api.config.ConfigType; import com.alibaba.nacos.api.config.annotation.NacosConfigurationProperties; import org.springframework.stereotype.Component; @Component @NacosConfigurationProperties(prefix = "", dataId = "${nacos.config.data-id}", type = ConfigType.YAML, autoRefreshed = true) public class TestProperties { /** * 测试属性 */ private String test; public String getTest() { return test; } public TestProperties setTest(String test) { this.test = test; return this; } } ================================================ FILE: lab-44/lab-44-nacos-config-demo-auto-refresh/src/main/resources/application.yaml ================================================ nacos: # Nacos 配置中心的配置项,对应 NacosConfigProperties 配置类 config: server-addr: 127.0.0.1:18848 # Nacos 服务器地址 bootstrap: enable: true # 是否开启 Nacos 配置预加载功能。默认为 false。 log-enable: true # 是否开启 Nacos 支持日志级别的加载时机。默认为 false。 data-id: example-auto-refresh # 使用的 Nacos 配置集的 dataId。 type: YAML # 使用的 Nacos 配置集的配置格式。默认为 PROPERTIES。 group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP。 namespace: # 使用的 Nacos 的命名空间,默认为 null。 auto-refresh: true # 是否自动刷新,默认为 false。 ================================================ FILE: lab-44/lab-44-nacos-config-demo-jasypt/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-44-nacos-config-demo-jasypt org.springframework.boot spring-boot-starter-web com.alibaba.boot nacos-config-spring-boot-starter 0.2.4 com.github.ulisesbocchio jasypt-spring-boot-starter 3.0.2 org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-44/lab-44-nacos-config-demo-jasypt/src/main/java/cn/iocoder/springboot/lab44/nacosdemo/Application.java ================================================ package cn.iocoder.springboot.lab44.nacosdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-44/lab-44-nacos-config-demo-jasypt/src/main/java/cn/iocoder/springboot/lab44/nacosdemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab44.nacosdemo.controller; import com.alibaba.nacos.api.config.annotation.NacosValue; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { // @Value("${spring.application.name}") @NacosValue(value = "${spring.application.name}", autoRefreshed = true) private String applicationName; @GetMapping("/test") public String test() { return applicationName; } } ================================================ FILE: lab-44/lab-44-nacos-config-demo-jasypt/src/main/resources/application.yaml ================================================ nacos: # Nacos 配置中心的配置项,对应 NacosConfigProperties 配置类 config: server-addr: 127.0.0.1:18848 # Nacos 服务器地址 bootstrap: enable: true # 是否开启 Nacos 配置预加载功能。默认为 false。 log-enable: true # 是否开启 Nacos 支持日志级别的加载时机。默认为 false。 data-id: example-jasypt # 使用的 Nacos 配置集的 dataId。 type: YAML # 使用的 Nacos 配置集的配置格式。默认为 PROPERTIES。 group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP。 namespace: # 使用的 Nacos 的命名空间,默认为 null。 auto-refresh: true # 是否自动刷新,默认为 false。 ================================================ FILE: lab-44/lab-44-nacos-config-demo-jasypt/src/test/java/cn/iocoder/springboot/lab44/nacosdemo/JasyptTest.java ================================================ package cn.iocoder.springboot.lab44.nacosdemo; import org.jasypt.encryption.StringEncryptor; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest public class JasyptTest { @Autowired private StringEncryptor encryptor; @Test public void encode() { // 第一个加密 String applicationName = "demo-application"; System.out.println(encryptor.encrypt(applicationName)); // // 第二个加密 // applicationName = "demo-app"; // System.out.println(encryptor.encrypt(applicationName)); } @Value("${spring.application.name}") // @NacosValue("${spring.application.name}") private String applicationName; @Test public void print() { System.out.println(applicationName); } @Value("${jasypt.encryptor.password}") private String password; } ================================================ FILE: lab-44/lab-44-nacos-config-demo-multi/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-44-nacos-config-demo-multi org.springframework.boot spring-boot-starter-web com.alibaba.boot nacos-config-spring-boot-starter 0.2.4 ================================================ FILE: lab-44/lab-44-nacos-config-demo-multi/src/main/java/cn/iocoder/springboot/lab44/nacosdemo/Application.java ================================================ package cn.iocoder.springboot.lab44.nacosdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.env.Environment; @SpringBootApplication // @NacosPropertySource(dataId = "example", type = ConfigType.YAML) public class Application { public static void main(String[] args) { // 启动 Spring Boot 应用 ConfigurableApplicationContext context = SpringApplication.run(Application.class, args); // 查看 Environment Environment environment = context.getEnvironment(); System.out.println(environment); } } ================================================ FILE: lab-44/lab-44-nacos-config-demo-multi/src/main/java/cn/iocoder/springboot/lab44/nacosdemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab44.nacosdemo.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @Autowired private Environment environment; @GetMapping("/test") public void test() { System.out.println(environment); } } ================================================ FILE: lab-44/lab-44-nacos-config-demo-multi/src/main/resources/application.yaml ================================================ nacos: # Nacos 配置中心的配置项,对应 NacosConfigProperties 配置类 config: server-addr: 127.0.0.1:18848 # Nacos 服务器地址 bootstrap: enable: true # 是否开启 Nacos 配置预加载功能。默认为 false。 log-enable: true # 是否开启 Nacos 支持日志级别的加载时机。默认为 false。 data-id: example-multi-01 # 使用的 Nacos 配置集的 dataId。 # data-ids: example-multi-02 type: YAML # 使用的 Nacos 配置集的配置格式。默认为 PROPERTIES。 group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP。 namespace: # 使用的 Nacos 的命名空间,默认为 null。 auto-refresh: true # 是否自动刷新,默认为 false。 ext-config: - server-addr: 127.0.0.1:18848 # Nacos 服务器地址 # data-id: example-multi-11 # 使用的 Nacos 配置集的 dataId。 data-ids: example-multi-11, example-multi-12 type: YAML # 使用的 Nacos 配置集的配置格式。默认为 PROPERTIES。 group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP。 namespace: # 使用的 Nacos 的命名空间,默认为 null。 auto-refresh: true # 是否自动刷新,默认为 false。 # - # 这里,可以继续添加。 ================================================ FILE: lab-44/lab-44-nacos-config-demo-profiles/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-44-nacos-config-demo-profiles org.springframework.boot spring-boot-starter-web com.alibaba.boot nacos-config-spring-boot-starter 0.2.4 ================================================ FILE: lab-44/lab-44-nacos-config-demo-profiles/src/main/java/cn/iocoder/springboot/lab44/nacosdemo/ProfilesApplication.java ================================================ package cn.iocoder.springboot.lab44.nacosdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ProfilesApplication { public static void main(String[] args) { SpringApplication.run(ProfilesApplication.class, args); } } ================================================ FILE: lab-44/lab-44-nacos-config-demo-profiles/src/main/resources/application-dev.yaml ================================================ nacos: # Nacos 配置中心的配置项,对应 NacosConfigProperties 配置类 config: server-addr: 127.0.0.1:18848 # Nacos 服务器地址 bootstrap: enable: true # 是否开启 Nacos 配置预加载功能。默认为 false。 log-enable: true # 是否开启 Nacos 支持日志级别的加载时机。默认为 false。 data-id: example # 使用的 Nacos 配置集的 dataId。 type: YAML # 使用的 Nacos 配置集的配置格式。默认为 PROPERTIES。 group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP。 namespace: 14226a0d-799f-424d-8905-162f6a8bf409 # 使用的 Nacos 的命名空间,默认为 null。 ================================================ FILE: lab-44/lab-44-nacos-config-demo-profiles/src/main/resources/application-prod.yaml ================================================ nacos: # Nacos 配置中心的配置项,对应 NacosConfigProperties 配置类 config: server-addr: 127.0.0.1:18848 # Nacos 服务器地址 bootstrap: enable: true # 是否开启 Nacos 配置预加载功能。默认为 false。 log-enable: true # 是否开启 Nacos 支持日志级别的加载时机。默认为 false。 data-id: example # 使用的 Nacos 配置集的 dataId。 type: YAML # 使用的 Nacos 配置集的配置格式。默认为 PROPERTIES。 group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP。 namespace: f1686f3b-a984-4cdf-8298-7caee3455d14 # 使用的 Nacos 的命名空间,默认为 null。 ================================================ FILE: lab-44/lab-44-nacos-config-demo-profiles/src/main/resources/application.yaml ================================================ #server: # port: 7070 spring: application: name: demo-application ================================================ FILE: lab-44/lab-44-nacos-discovery-demo/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-44-nacos-discovery-demo org.springframework.boot spring-boot-starter-web com.alibaba.boot nacos-discovery-spring-boot-starter 0.2.4 ================================================ FILE: lab-44/lab-44-nacos-discovery-demo/src/main/java/cn/iocoder/springboot/lab44/nacosdemo/Application.java ================================================ package cn.iocoder.springboot.lab44.nacosdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-44/lab-44-nacos-discovery-demo/src/main/java/cn/iocoder/springboot/lab44/nacosdemo/controller/ConsumerController.java ================================================ package cn.iocoder.springboot.lab44.nacosdemo.controller; import com.alibaba.nacos.api.annotation.NacosInjected; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.NamingService; import com.alibaba.nacos.api.naming.pojo.Instance; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import java.util.List; @RestController @RequestMapping("/consumer") public class ConsumerController { @NacosInjected private NamingService namingService; private RestTemplate restTemplate = new RestTemplate(); @GetMapping("/demo") public String consumer() throws IllegalStateException, NacosException { // 获得实例 Instance instance = null; if (false) { List instances = namingService.getAllInstances("demo-application"); // 获得首个实例,进行调用 instance = instances.stream().findFirst() .orElseThrow(() -> new IllegalStateException("未找到对应的 Instance")); } else { instance = namingService.selectOneHealthyInstance("demo-application"); } // 执行请求 return restTemplate.getForObject("http://" + instance.toInetAddr() + "/provider/demo", String.class); } } ================================================ FILE: lab-44/lab-44-nacos-discovery-demo/src/main/java/cn/iocoder/springboot/lab44/nacosdemo/controller/ProviderController.java ================================================ package cn.iocoder.springboot.lab44.nacosdemo.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/provider") public class ProviderController { @GetMapping("/demo") public String provider() { return "echo"; } } ================================================ FILE: lab-44/lab-44-nacos-discovery-demo/src/main/resources/application.yaml ================================================ spring: application: name: demo-application # 应用名 nacos: # Nacos 配置中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:18848 # Nacos 服务器地址 auto-register: true # 是否自动注册到 Nacos 中。默认为 false。 namespace: # 使用的 Nacos 的命名空间,默认为 null。 register: service-name: ${spring.application.name} # 注册到 Nacos 的服务名 group-name: DEFAULT_GROUP # 使用的 Nacos 服务分组,默认为 DEFAULT_GROUP。 cluster-name: # 集群名,默认为空。 ================================================ FILE: lab-44/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-44 pom lab-44-nacos-config-demo lab-44-nacos-config-demo-profiles lab-44-nacos-config-demo-auto-refresh lab-44-nacos-config-demo-jasypt lab-44-nacos-config-demo-actuator lab-44-nacos-config-demo-multi lab-44-nacos-discovery-demo ================================================ FILE: lab-44/《芋道 Spring Boot 注册中心 Nacos 入门》.md ================================================ ================================================ FILE: lab-44/《芋道 Spring Boot 配置中心 Nacos 入门》.md ================================================ ================================================ FILE: lab-45/lab-45-apollo-demo/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-45-apollo-demo org.springframework.boot spring-boot-starter com.ctrip.framework.apollo apollo-client 1.5.1 ================================================ FILE: lab-45/lab-45-apollo-demo/src/main/java/cn/iocoder/springboot/lab45/apollodemo/Application.java ================================================ package cn.iocoder.springboot.lab45.apollodemo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.stereotype.Component; @SpringBootApplication // @NacosPropertySource(dataId = "example", type = ConfigType.YAML) public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } @Component public class OrderPropertiesCommandLineRunner implements CommandLineRunner { private final Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private OrderProperties orderProperties; @Override public void run(String... args) { logger.info("payTimeoutSeconds:" + orderProperties.getPayTimeoutSeconds()); logger.info("createFrequencySeconds:" + orderProperties.getCreateFrequencySeconds()); } } @Component public class ValueCommandLineRunner implements CommandLineRunner { private final Logger logger = LoggerFactory.getLogger(getClass()); @Value(value = "${order.pay-timeout-seconds}") private Integer payTimeoutSeconds; @Value(value = "${order.create-frequency-seconds}") private Integer createFrequencySeconds; @Override public void run(String... args) { logger.info("payTimeoutSeconds:" + payTimeoutSeconds); logger.info("createFrequencySeconds:" + createFrequencySeconds); } } } ================================================ FILE: lab-45/lab-45-apollo-demo/src/main/java/cn/iocoder/springboot/lab45/apollodemo/OrderProperties.java ================================================ package cn.iocoder.springboot.lab45.apollodemo; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component //@NacosConfigurationProperties(prefix = "order", dataId = "${nacos.config.data-id}", type = ConfigType.YAML) @ConfigurationProperties(prefix = "order") public class OrderProperties { /** * 订单支付超时时长,单位:秒。 */ private Integer payTimeoutSeconds; /** * 订单创建频率,单位:秒 */ private Integer createFrequencySeconds; // /** // * 配置描述 // */ // private String desc; public Integer getPayTimeoutSeconds() { return payTimeoutSeconds; } public OrderProperties setPayTimeoutSeconds(Integer payTimeoutSeconds) { this.payTimeoutSeconds = payTimeoutSeconds; return this; } public Integer getCreateFrequencySeconds() { return createFrequencySeconds; } public OrderProperties setCreateFrequencySeconds(Integer createFrequencySeconds) { this.createFrequencySeconds = createFrequencySeconds; return this; } // public String getDesc() { // return desc; // } // // public OrderProperties setDesc(String desc) { // this.desc = desc; // return this; // } } ================================================ FILE: lab-45/lab-45-apollo-demo/src/main/resources/application.yaml ================================================ server: port: 7070 # 避免和本地的 Apollo Portal 端口冲突 app: id: demo-application # 使用的 Apollo 的项目(应用)编号 apollo: meta: http://127.0.0.1:8080 # Apollo Meta Server 地址 bootstrap: enabled: true # 是否开启 Apollo 配置预加载功能。默认为 false。 eagerLoad: enable: true # 是否开启 Apollo 支持日志级别的加载时机。默认为 false。 namespaces: application # 使用的 Apollo 的命名空间,默认为 application。 ================================================ FILE: lab-45/lab-45-apollo-demo-auto-refresh/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-45-apollo-demo-auto-refresh org.springframework.boot spring-boot-starter-web com.ctrip.framework.apollo apollo-client 1.5.1 ================================================ FILE: lab-45/lab-45-apollo-demo-auto-refresh/src/main/java/cn/iocoder/springboot/lab45/apollodemo/Application.java ================================================ package cn.iocoder.springboot.lab45.apollodemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-45/lab-45-apollo-demo-auto-refresh/src/main/java/cn/iocoder/springboot/lab45/apollodemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab45.apollodemo.controller; import cn.iocoder.springboot.lab45.apollodemo.properties.TestProperties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @Value("${test.test}") private String test; @GetMapping("/test") public String test() { return test; } @Autowired private TestProperties testProperties; @GetMapping("/test_properties") public TestProperties testProperties() { return testProperties; } private Logger logger = LoggerFactory.getLogger(getClass()); @GetMapping("/logger") public void logger() { logger.debug("[logger][测试一下]"); } } ================================================ FILE: lab-45/lab-45-apollo-demo-auto-refresh/src/main/java/cn/iocoder/springboot/lab45/apollodemo/listener/LoggingSystemConfigListener.java ================================================ package cn.iocoder.springboot.lab45.apollodemo.listener; import com.ctrip.framework.apollo.Config; import com.ctrip.framework.apollo.model.ConfigChangeEvent; import com.ctrip.framework.apollo.spring.annotation.ApolloConfig; import com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener; import org.springframework.boot.logging.LogLevel; import org.springframework.boot.logging.LoggingSystem; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.util.Set; @Component public class LoggingSystemConfigListener { /** * 日志配置项的前缀 */ private static final String LOGGER_TAG = "logging.level."; @Resource private LoggingSystem loggingSystem; @ApolloConfig private Config config; @ApolloConfigChangeListener public void onChange(ConfigChangeEvent changeEvent) throws Exception { // 获得 Apollo 所有配置项 Set keys = config.getPropertyNames(); // 遍历配置集的每个配置项,判断是否是 logging.level 配置项 for (String key : keys) { // 如果是 logging.level 配置项,则设置其对应的日志级别 if (key.startsWith(LOGGER_TAG)) { // 获得日志级别 String strLevel = config.getProperty(key, "info"); LogLevel level = LogLevel.valueOf(strLevel.toUpperCase()); // 设置日志级别到 LoggingSystem 中 loggingSystem.setLogLevel(key.replace(LOGGER_TAG, ""), level); } } } } ================================================ FILE: lab-45/lab-45-apollo-demo-auto-refresh/src/main/java/cn/iocoder/springboot/lab45/apollodemo/properties/TestProperties.java ================================================ package cn.iocoder.springboot.lab45.apollodemo.properties; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component @ConfigurationProperties(prefix = "test") public class TestProperties { /** * 测试属性 */ private String test; public String getTest() { return test; } public TestProperties setTest(String test) { this.test = test; return this; } } ================================================ FILE: lab-45/lab-45-apollo-demo-auto-refresh/src/main/resources/application.yaml ================================================ server: port: 7070 # 避免和本地的 Apollo Portal 端口冲突 app: id: demo-application # 使用的 Apollo 的项目(应用)编号 apollo: meta: http://127.0.0.1:8080 # Apollo Meta Server 地址 bootstrap: enabled: true # 是否开启 Apollo 配置预加载功能。默认为 false。 eagerLoad: enable: true # 是否开启 Apollo 支持日志级别的加载时机。默认为 false。 namespaces: application # 使用的 Apollo 的命名空间,默认为 application。 ================================================ FILE: lab-45/lab-45-apollo-demo-jasypt/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-45-apollo-demo-jasypt org.springframework.boot spring-boot-starter-web com.ctrip.framework.apollo apollo-client 1.5.1 com.github.ulisesbocchio jasypt-spring-boot-starter 3.0.2 org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-45/lab-45-apollo-demo-jasypt/src/main/java/cn/iocoder/springboot/lab45/apollodemo/Application.java ================================================ package cn.iocoder.springboot.lab45.apollodemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-45/lab-45-apollo-demo-jasypt/src/main/java/cn/iocoder/springboot/lab45/apollodemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab45.apollodemo.controller; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @Value("${spring.application.name}") private String applicationName; @GetMapping("/test") public String test() { return applicationName; } } ================================================ FILE: lab-45/lab-45-apollo-demo-jasypt/src/main/resources/application.yaml ================================================ server: port: 7070 # 避免和本地的 Apollo Portal 端口冲突 app: id: demo-application-jasypt # 使用的 Apollo 的项目(应用)编号 apollo: meta: http://127.0.0.1:8080 # Apollo Meta Server 地址 bootstrap: enabled: true # 是否开启 Apollo 配置预加载功能。默认为 false。 eagerLoad: enable: true # 是否开启 Apollo 支持日志级别的加载时机。默认为 false。 namespaces: application # 使用的 Apollo 的命名空间,默认为 application。 ================================================ FILE: lab-45/lab-45-apollo-demo-jasypt/src/test/java/cn/iocoder/springboot/lab45/apollodemo/JasyptTest.java ================================================ package cn.iocoder.springboot.lab45.apollodemo; import org.jasypt.encryption.StringEncryptor; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest public class JasyptTest { @Autowired private StringEncryptor encryptor; @Test public void encode() { // 第一个加密 String applicationName = "demo-application"; System.out.println(encryptor.encrypt(applicationName)); // // 第二个加密 // applicationName = "demo-app"; // System.out.println(encryptor.encrypt(applicationName)); } @Value("${spring.application.name}") private String applicationName; @Test public void print() { System.out.println(applicationName); } @Value("${jasypt.encryptor.password}") private String password; } ================================================ FILE: lab-45/lab-45-apollo-demo-multi/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-45-apollo-demo-multi org.springframework.boot spring-boot-starter-web com.ctrip.framework.apollo apollo-client 1.5.1 ================================================ FILE: lab-45/lab-45-apollo-demo-multi/src/main/java/cn/iocoder/springboot/lab45/apollodemo/Application.java ================================================ package cn.iocoder.springboot.lab45.apollodemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.env.Environment; @SpringBootApplication public class Application { public static void main(String[] args) { // 启动 Spring Boot 应用 ConfigurableApplicationContext context = SpringApplication.run(Application.class, args); // 查看 Environment Environment environment = context.getEnvironment(); System.out.println(environment); } } ================================================ FILE: lab-45/lab-45-apollo-demo-multi/src/main/java/cn/iocoder/springboot/lab45/apollodemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab45.apollodemo.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @Autowired private Environment environment; @GetMapping("/test") public void test() { System.out.println(environment); } } ================================================ FILE: lab-45/lab-45-apollo-demo-multi/src/main/resources/application.yaml ================================================ server: port: 7070 # 避免和本地的 Apollo Portal 端口冲突 app: id: demo-application-multi # 使用的 Apollo 的项目(应用)编号 apollo: meta: http://127.0.0.1:8080 # Apollo Meta Server 地址 bootstrap: enabled: true # 是否开启 Apollo 配置预加载功能。默认为 false。 eagerLoad: enable: true # 是否开启 Apollo 支持日志级别的加载时机。默认为 false。 namespaces: application, db # 使用的 Apollo 的命名空间,默认为 application。 ================================================ FILE: lab-45/lab-45-apollo-demo-profiles/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-45-apollo-demo-profiles org.springframework.boot spring-boot-starter-web com.ctrip.framework.apollo apollo-client 1.5.1 ================================================ FILE: lab-45/lab-45-apollo-demo-profiles/src/main/java/cn/iocoder/springboot/lab45/apollodemo/ProfilesApplication.java ================================================ package cn.iocoder.springboot.lab45.apollodemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ProfilesApplication { public static void main(String[] args) { SpringApplication.run(ProfilesApplication.class, args); } } ================================================ FILE: lab-45/lab-45-apollo-demo-profiles/src/main/resources/application-dev.yaml ================================================ app: id: demo-application-profiles # 使用的 Apollo 的项目(应用)编号 apollo: meta: http://127.0.0.1:8080 # Apollo Meta Server 地址 bootstrap: enabled: true # 是否开启 Apollo 配置预加载功能。默认为 false。 eagerLoad: enable: true # 是否开启 Apollo 支持日志级别的加载时机。默认为 false。 namespaces: application # 使用的 Apollo 的命名空间,默认为 application。 ================================================ FILE: lab-45/lab-45-apollo-demo-profiles/src/main/resources/application-prod.yaml ================================================ app: id: demo-application-profiles # 使用的 Apollo 的项目(应用)编号 apollo: meta: http://127.0.0.1:18080 # Apollo Meta Server 地址 bootstrap: enabled: true # 是否开启 Apollo 配置预加载功能。默认为 false。 eagerLoad: enable: true # 是否开启 Apollo 支持日志级别的加载时机。默认为 false。 namespaces: application # 使用的 Apollo 的命名空间,默认为 application。 ================================================ FILE: lab-45/lab-45-apollo-demo-profiles/src/main/resources/application.yaml ================================================ #server: # port: 7070 spring: application: name: demo-application ================================================ FILE: lab-45/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-45 pom lab-45-apollo-demo lab-45-apollo-demo-profiles lab-45-apollo-demo-auto-refresh lab-45-apollo-demo-jasypt lab-45-apollo-demo-multi ================================================ FILE: lab-45/《芋道 Spring Boot 配置中心 Apollo 入门》.md ================================================ ================================================ FILE: lab-46/lab-46-sentinel-demo/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-46-sentinel-demo org.springframework.boot spring-boot-starter-web com.alibaba.csp sentinel-core 1.7.1 com.alibaba.csp sentinel-transport-simple-http 1.7.1 com.alibaba.csp sentinel-spring-webmvc-adapter 1.7.1 com.alibaba.csp sentinel-parameter-flow-control 1.7.1 com.alibaba.csp sentinel-annotation-aspectj 1.7.1 ================================================ FILE: lab-46/lab-46-sentinel-demo/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/Application.java ================================================ package cn.iocoder.springboot.lab46.sentineldemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { // 设置系统属性 project.name,提供给 Sentinel 读取 System.setProperty("project.name", "demo-application"); // 启动 Spring Boot 应用 SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-46/lab-46-sentinel-demo/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/config/SentinelConfiguration.java ================================================ package cn.iocoder.springboot.lab46.sentineldemo.config; import com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class SentinelConfiguration { @Bean public SentinelResourceAspect sentinelResourceAspect() { return new SentinelResourceAspect(); } } ================================================ FILE: lab-46/lab-46-sentinel-demo/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/config/SpringMvcConfiguration.java ================================================ package cn.iocoder.springboot.lab46.sentineldemo.config; import com.alibaba.csp.sentinel.adapter.spring.webmvc.SentinelWebInterceptor; import com.alibaba.csp.sentinel.adapter.spring.webmvc.SentinelWebTotalInterceptor; import com.alibaba.csp.sentinel.adapter.spring.webmvc.config.SentinelWebMvcConfig; import com.alibaba.csp.sentinel.adapter.spring.webmvc.config.SentinelWebMvcTotalConfig; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class SpringMvcConfiguration implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { // Add Sentinel interceptor // addSentinelWebTotalInterceptor(registry); addSentinelWebInterceptor(registry); } private void addSentinelWebInterceptor(InterceptorRegistry registry) { // 创建 SentinelWebMvcConfig 对象 SentinelWebMvcConfig config = new SentinelWebMvcConfig(); config.setHttpMethodSpecify(true); // 是否包含请求方法。即基于 URL 创建的资源,是否包含 Method。 // config.setBlockExceptionHandler(new DefaultBlockExceptionHandler()); // 设置 BlockException 处理器。 // config.setOriginParser(new RequestOriginParser() { // 设置请求来源解析器。用于黑白名单控制功能。 // // @Override // public String parseOrigin(HttpServletRequest request) { // // 从 Header 中,获得请求来源 // String origin = request.getHeader("s-user"); // // 如果为空,给一个默认的 // if (StringUtils.isEmpty(origin)) { // origin = "default"; // } // return origin; // } // // }); // 添加 SentinelWebInterceptor 拦截器 registry.addInterceptor(new SentinelWebInterceptor(config)).addPathPatterns("/**"); } private void addSentinelWebTotalInterceptor(InterceptorRegistry registry) { // 创建 SentinelWebMvcTotalConfig 对象 SentinelWebMvcTotalConfig config = new SentinelWebMvcTotalConfig(); // 添加 SentinelWebTotalInterceptor 拦截器 registry.addInterceptor(new SentinelWebTotalInterceptor(config)).addPathPatterns("/**"); } } ================================================ FILE: lab-46/lab-46-sentinel-demo/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab46.sentineldemo.controller; import com.alibaba.csp.sentinel.Entry; import com.alibaba.csp.sentinel.SphU; import com.alibaba.csp.sentinel.annotation.SentinelResource; import com.alibaba.csp.sentinel.slots.block.BlockException; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @GetMapping("/echo") public String echo() { return "echo"; } @GetMapping("/test") public String test() { return "test"; } @GetMapping("/sleep") public String sleep() throws InterruptedException { Thread.sleep(100L); return "sleep"; } // 测试热点参数限流 @GetMapping("/product_info") @SentinelResource("demo_product_info_hot") public String productInfo(Integer id) { return "商品编号:" + id; } // 手动使用 Sentinel 客户端 API @GetMapping("/entry_demo") public String entryDemo() { Entry entry = null; try { // 访问资源 entry = SphU.entry("entry_demo"); // ... 执行业务逻辑 return "执行成功"; } catch (BlockException ex) { return "被拒绝"; } finally { // 释放资源 if (entry != null) { entry.exit(); } } } // 测试 @SentinelResource 注解 @GetMapping("/annotations_demo") @SentinelResource(value = "annotations_demo_resource", blockHandler = "blockHandler", fallback = "fallback") public String annotationsDemo(@RequestParam(required = false) Integer id) throws InterruptedException { if (id == null) { throw new IllegalArgumentException("id 参数不允许为空"); } return "success..."; } // BlockHandler 处理函数,参数最后多一个 BlockException,其余与原函数一致. public String blockHandler(Integer id, BlockException ex) { return "block:" + ex.getClass().getSimpleName(); } // Fallback 处理函数,函数签名与原函数一致或加一个 Throwable 类型的参数. public String fallback(Integer id, Throwable throwable) { return "fallback:" + throwable.getMessage(); } } ================================================ FILE: lab-46/lab-46-sentinel-demo/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/web/GlobalExceptionHandler.java ================================================ package cn.iocoder.springboot.lab46.sentineldemo.web; import com.alibaba.csp.sentinel.slots.block.BlockException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; @ControllerAdvice(basePackages = "cn.iocoder.springboot.lab46.sentineldemo.controller") public class GlobalExceptionHandler { @ResponseBody @ExceptionHandler(value = BlockException.class) public String blockExceptionHandler(BlockException blockException) { return "请求过于频繁"; } } ================================================ FILE: lab-46/lab-46-sentinel-demo/src/main/resources/sentinel.properties ================================================ csp.sentinel.dashboard.server=127.0.0.1:7070 ================================================ FILE: lab-46/lab-46-sentinel-demo-apollo/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-46-sentinel-demo-apollo org.springframework.boot spring-boot-starter-web com.alibaba.csp sentinel-core 1.7.1 com.alibaba.csp sentinel-transport-simple-http 1.7.1 com.alibaba.csp sentinel-spring-webmvc-adapter 1.7.1 com.alibaba.csp sentinel-parameter-flow-control 1.7.1 com.alibaba.csp sentinel-annotation-aspectj 1.7.1 com.alibaba.csp sentinel-datasource-apollo 1.7.1 ================================================ FILE: lab-46/lab-46-sentinel-demo-apollo/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/Application.java ================================================ package cn.iocoder.springboot.lab46.sentineldemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { // 设置系统属性 project.name,提供给 Sentinel 读取 System.setProperty("project.name", "demo-application"); // 启动 Spring Boot 应用 SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-46/lab-46-sentinel-demo-apollo/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/config/SentinelConfiguration.java ================================================ package cn.iocoder.springboot.lab46.sentineldemo.config; import com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect; import com.alibaba.csp.sentinel.datasource.Converter; import com.alibaba.csp.sentinel.datasource.apollo.ApolloDataSource; import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.Arrays; import java.util.List; @Configuration public class SentinelConfiguration { @Value("${spring.application.name}") private String applicationName; @Bean public SentinelResourceAspect sentinelResourceAspect() { return new SentinelResourceAspect(); } @Bean public ApolloDataSource apolloDataSource(ObjectMapper objectMapper) { // Apollo 配置。这里先写死,推荐后面写到 application.yaml 配置文件中。 String appId = applicationName; // Apollo 项目编号。一般情况下,推荐和 spring.application.name 保持一致 String serverAddress = "http://localhost:8080"; // Apollo Meta 服务器地址 String namespace = "application"; // Apollo 命名空间 String flowRuleKey = "sentinel.flow-rule"; // Apollo 配置项的 KEY // 创建 ApolloDataSource 对象 System.setProperty("app.id", appId); System.setProperty("apollo.meta", serverAddress); ApolloDataSource> apolloDataSource = new ApolloDataSource<>(namespace, flowRuleKey, "", new Converter>() { // 转换器,将读取的 Apollo 配置,转换成 FlowRule 数组 @Override public List convert(String value) { try { return Arrays.asList(objectMapper.readValue(value, FlowRule[].class)); } catch (JsonProcessingException e) { throw new RuntimeException(e); } } }); // 注册到 FlowRuleManager 中 FlowRuleManager.register2Property(apolloDataSource.getProperty()); return apolloDataSource; } } ================================================ FILE: lab-46/lab-46-sentinel-demo-apollo/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/config/SpringMvcConfiguration.java ================================================ package cn.iocoder.springboot.lab46.sentineldemo.config; import com.alibaba.csp.sentinel.adapter.spring.webmvc.SentinelWebInterceptor; import com.alibaba.csp.sentinel.adapter.spring.webmvc.SentinelWebTotalInterceptor; import com.alibaba.csp.sentinel.adapter.spring.webmvc.config.SentinelWebMvcConfig; import com.alibaba.csp.sentinel.adapter.spring.webmvc.config.SentinelWebMvcTotalConfig; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class SpringMvcConfiguration implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { // Add Sentinel interceptor // addSentinelWebTotalInterceptor(registry); addSentinelWebInterceptor(registry); } private void addSentinelWebInterceptor(InterceptorRegistry registry) { // 创建 SentinelWebMvcConfig 对象 SentinelWebMvcConfig config = new SentinelWebMvcConfig(); config.setHttpMethodSpecify(true); // 是否包含请求方法。即基于 URL 创建的资源,是否包含 Method。 // config.setBlockExceptionHandler(new DefaultBlockExceptionHandler()); // 设置 BlockException 处理器。 // config.setOriginParser(new RequestOriginParser() { // 设置请求来源解析器。用于黑白名单控制功能。 // // @Override // public String parseOrigin(HttpServletRequest request) { // // 从 Header 中,获得请求来源 // String origin = request.getHeader("s-user"); // // 如果为空,给一个默认的 // if (StringUtils.isEmpty(origin)) { // origin = "default"; // } // return origin; // } // // }); // 添加 SentinelWebInterceptor 拦截器 registry.addInterceptor(new SentinelWebInterceptor(config)).addPathPatterns("/**"); } private void addSentinelWebTotalInterceptor(InterceptorRegistry registry) { // 创建 SentinelWebMvcTotalConfig 对象 SentinelWebMvcTotalConfig config = new SentinelWebMvcTotalConfig(); // 添加 SentinelWebTotalInterceptor 拦截器 registry.addInterceptor(new SentinelWebTotalInterceptor(config)).addPathPatterns("/**"); } } ================================================ FILE: lab-46/lab-46-sentinel-demo-apollo/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab46.sentineldemo.controller; import com.alibaba.csp.sentinel.Entry; import com.alibaba.csp.sentinel.SphU; import com.alibaba.csp.sentinel.annotation.SentinelResource; import com.alibaba.csp.sentinel.slots.block.BlockException; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @GetMapping("/echo") public String echo() { return "echo"; } @GetMapping("/test") public String test() { return "test"; } @GetMapping("/sleep") public String sleep() throws InterruptedException { Thread.sleep(100L); return "sleep"; } // 测试热点参数限流 @GetMapping("/product_info") @SentinelResource("demo_product_info_hot") public String productInfo(Integer id) { return "商品编号:" + id; } // 手动使用 Sentinel 客户端 API @GetMapping("/entry_demo") public String entryDemo() { Entry entry = null; try { // 访问资源 entry = SphU.entry("entry_demo"); // ... 执行业务逻辑 return "执行成功"; } catch (BlockException ex) { return "被拒绝"; } finally { // 释放资源 if (entry != null) { entry.exit(); } } } // 测试 @SentinelResource 注解 @GetMapping("/annotations_demo") @SentinelResource(value = "annotations_demo_resource", blockHandler = "blockHandler", fallback = "fallback") public String annotationsDemo(@RequestParam(required = false) Integer id) throws InterruptedException { if (id == null) { throw new IllegalArgumentException("id 参数不允许为空"); } return "success..."; } // BlockHandler 处理函数,参数最后多一个 BlockException,其余与原函数一致. public String blockHandler(Integer id, BlockException ex) { return "block:" + ex.getClass().getSimpleName(); } // Fallback 处理函数,函数签名与原函数一致或加一个 Throwable 类型的参数. public String fallback(Integer id, Throwable throwable) { return "fallback:" + throwable.getMessage(); } } ================================================ FILE: lab-46/lab-46-sentinel-demo-apollo/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/web/GlobalExceptionHandler.java ================================================ package cn.iocoder.springboot.lab46.sentineldemo.web; import com.alibaba.csp.sentinel.slots.block.BlockException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; @ControllerAdvice(basePackages = "cn.iocoder.springboot.lab46.sentineldemo.controller") public class GlobalExceptionHandler { @ResponseBody @ExceptionHandler(value = BlockException.class) public String blockExceptionHandler(BlockException blockException) { return "请求过于频繁"; } } ================================================ FILE: lab-46/lab-46-sentinel-demo-apollo/src/main/resources/application.yaml ================================================ spring: application: name: demo-application server: port: 18080 # 避免和 Apollo 使用到的 8080 端口冲突 ================================================ FILE: lab-46/lab-46-sentinel-demo-apollo/src/main/resources/sentinel.properties ================================================ csp.sentinel.dashboard.server=127.0.0.1:7070 ================================================ FILE: lab-46/lab-46-sentinel-demo-file/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-46-sentinel-demo-file org.springframework.boot spring-boot-starter-web com.alibaba.csp sentinel-core 1.7.1 com.alibaba.csp sentinel-transport-simple-http 1.7.1 com.alibaba.csp sentinel-spring-webmvc-adapter 1.7.1 com.alibaba.csp sentinel-parameter-flow-control 1.7.1 com.alibaba.csp sentinel-annotation-aspectj 1.7.1 com.alibaba.csp sentinel-datasource-extension 1.7.1 ================================================ FILE: lab-46/lab-46-sentinel-demo-file/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/Application.java ================================================ package cn.iocoder.springboot.lab46.sentineldemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { // 设置系统属性 project.name,提供给 Sentinel 读取 System.setProperty("project.name", "demo-application"); // 启动 Spring Boot 应用 SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-46/lab-46-sentinel-demo-file/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/config/SentinelConfiguration.java ================================================ package cn.iocoder.springboot.lab46.sentineldemo.config; import com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect; import com.alibaba.csp.sentinel.datasource.Converter; import com.alibaba.csp.sentinel.datasource.FileRefreshableDataSource; import com.alibaba.csp.sentinel.datasource.FileWritableDataSource; import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager; import com.alibaba.csp.sentinel.transport.util.WritableDataSourceRegistry; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; import java.io.File; import java.io.IOException; import java.util.Arrays; import java.util.List; @Configuration public class SentinelConfiguration { @Bean public SentinelResourceAspect sentinelResourceAspect() { return new SentinelResourceAspect(); } // @Bean public FileRefreshableDataSource> refreshableDataSource(ObjectMapper objectMapper) throws IOException { // File 配置。这里先写死,推荐后面写到 application.yaml 配置文件中。 ClassPathResource resource = new ClassPathResource("/flow-rule.json"); // 创建 FileRefreshableDataSource 对象 FileRefreshableDataSource> refreshableDataSource = new FileRefreshableDataSource<>(resource.getFile(), new Converter>() { // 转换器,将读取的文本配置,转换成 FlowRule 数组 @Override public List convert(String value) { try { return Arrays.asList(objectMapper.readValue(value, FlowRule[].class)); } catch (JsonProcessingException e) { throw new RuntimeException(e); } } }); // 注册到 FlowRuleManager 中 FlowRuleManager.register2Property(refreshableDataSource.getProperty()); return refreshableDataSource; } @Bean public FileWritableDataSource writableDataSource(ObjectMapper objectMapper) throws IOException { // File 配置。这里先写死,推荐后面写到 application.yaml 配置文件中。 String directory = System.getProperty("user.home") + File.separator + "sentinel" + File.separator + System.getProperty("project.name"); mkdirs(directory); String path = directory + File.separator + "flow-rule.json"; creteFile(path); // 创建 FileRefreshableDataSource 对象 FileRefreshableDataSource> refreshableDataSource = new FileRefreshableDataSource<>(path, new Converter>() { // 转换器,将读取的文本配置,转换成 FlowRule 数组 @Override public List convert(String value) { try { return Arrays.asList(objectMapper.readValue(value, FlowRule[].class)); } catch (JsonProcessingException e) { throw new RuntimeException(e); } } }); // 注册到 FlowRuleManager 中 FlowRuleManager.register2Property(refreshableDataSource.getProperty()); // 创建 FileWritableDataSource 对象 FileWritableDataSource> fileWritableDataSource = new FileWritableDataSource<>(path, new Converter, String>() { @Override public String convert(List source) { try { return objectMapper.writeValueAsString(source); } catch (JsonProcessingException e) { throw new RuntimeException(e); } } }); // 注册到 WritableDataSourceRegistry 中 WritableDataSourceRegistry.registerFlowDataSource(fileWritableDataSource); return fileWritableDataSource; } private void mkdirs(String path) { File file = new File(path); if (file.exists()) { return; } file.mkdirs(); } private void creteFile(String path) throws IOException { File file = new File(path); if (file.exists()) { return; } file.createNewFile(); } } ================================================ FILE: lab-46/lab-46-sentinel-demo-file/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/config/SpringMvcConfiguration.java ================================================ package cn.iocoder.springboot.lab46.sentineldemo.config; import com.alibaba.csp.sentinel.adapter.spring.webmvc.SentinelWebInterceptor; import com.alibaba.csp.sentinel.adapter.spring.webmvc.SentinelWebTotalInterceptor; import com.alibaba.csp.sentinel.adapter.spring.webmvc.config.SentinelWebMvcConfig; import com.alibaba.csp.sentinel.adapter.spring.webmvc.config.SentinelWebMvcTotalConfig; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class SpringMvcConfiguration implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { // Add Sentinel interceptor // addSentinelWebTotalInterceptor(registry); addSentinelWebInterceptor(registry); } private void addSentinelWebInterceptor(InterceptorRegistry registry) { // 创建 SentinelWebMvcConfig 对象 SentinelWebMvcConfig config = new SentinelWebMvcConfig(); config.setHttpMethodSpecify(true); // 是否包含请求方法。即基于 URL 创建的资源,是否包含 Method。 // config.setBlockExceptionHandler(new DefaultBlockExceptionHandler()); // 设置 BlockException 处理器。 // config.setOriginParser(new RequestOriginParser() { // 设置请求来源解析器。用于黑白名单控制功能。 // // @Override // public String parseOrigin(HttpServletRequest request) { // // 从 Header 中,获得请求来源 // String origin = request.getHeader("s-user"); // // 如果为空,给一个默认的 // if (StringUtils.isEmpty(origin)) { // origin = "default"; // } // return origin; // } // // }); // 添加 SentinelWebInterceptor 拦截器 registry.addInterceptor(new SentinelWebInterceptor(config)).addPathPatterns("/**"); } private void addSentinelWebTotalInterceptor(InterceptorRegistry registry) { // 创建 SentinelWebMvcTotalConfig 对象 SentinelWebMvcTotalConfig config = new SentinelWebMvcTotalConfig(); // 添加 SentinelWebTotalInterceptor 拦截器 registry.addInterceptor(new SentinelWebTotalInterceptor(config)).addPathPatterns("/**"); } } ================================================ FILE: lab-46/lab-46-sentinel-demo-file/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab46.sentineldemo.controller; import com.alibaba.csp.sentinel.Entry; import com.alibaba.csp.sentinel.SphU; import com.alibaba.csp.sentinel.annotation.SentinelResource; import com.alibaba.csp.sentinel.slots.block.BlockException; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @GetMapping("/echo") public String echo() { return "echo"; } @GetMapping("/test") public String test() { return "test"; } @GetMapping("/sleep") public String sleep() throws InterruptedException { Thread.sleep(100L); return "sleep"; } // 测试热点参数限流 @GetMapping("/product_info") @SentinelResource("demo_product_info_hot") public String productInfo(Integer id) { return "商品编号:" + id; } // 手动使用 Sentinel 客户端 API @GetMapping("/entry_demo") public String entryDemo() { Entry entry = null; try { // 访问资源 entry = SphU.entry("entry_demo"); // ... 执行业务逻辑 return "执行成功"; } catch (BlockException ex) { return "被拒绝"; } finally { // 释放资源 if (entry != null) { entry.exit(); } } } // 测试 @SentinelResource 注解 @GetMapping("/annotations_demo") @SentinelResource(value = "annotations_demo_resource", blockHandler = "blockHandler", fallback = "fallback") public String annotationsDemo(@RequestParam(required = false) Integer id) throws InterruptedException { if (id == null) { throw new IllegalArgumentException("id 参数不允许为空"); } return "success..."; } // BlockHandler 处理函数,参数最后多一个 BlockException,其余与原函数一致. public String blockHandler(Integer id, BlockException ex) { return "block:" + ex.getClass().getSimpleName(); } // Fallback 处理函数,函数签名与原函数一致或加一个 Throwable 类型的参数. public String fallback(Integer id, Throwable throwable) { return "fallback:" + throwable.getMessage(); } } ================================================ FILE: lab-46/lab-46-sentinel-demo-file/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/web/GlobalExceptionHandler.java ================================================ package cn.iocoder.springboot.lab46.sentineldemo.web; import com.alibaba.csp.sentinel.slots.block.BlockException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; @ControllerAdvice(basePackages = "cn.iocoder.springboot.lab46.sentineldemo.controller") public class GlobalExceptionHandler { @ResponseBody @ExceptionHandler(value = BlockException.class) public String blockExceptionHandler(BlockException blockException) { return "请求过于频繁"; } } ================================================ FILE: lab-46/lab-46-sentinel-demo-file/src/main/resources/flow-rule.json ================================================ [ { "resource": "GET:/demo/echo", "limitApp": "default", "grade": 1, "count": 5, "strategy": 0, "controlBehavior": 0, "clusterMode": false } ] ================================================ FILE: lab-46/lab-46-sentinel-demo-file/src/main/resources/sentinel.properties ================================================ csp.sentinel.dashboard.server=127.0.0.1:7070 ================================================ FILE: lab-46/lab-46-sentinel-demo-nacos/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-46-sentinel-demo-nacos org.springframework.boot spring-boot-starter-web com.alibaba.csp sentinel-core 1.7.1 com.alibaba.csp sentinel-transport-simple-http 1.7.1 com.alibaba.csp sentinel-spring-webmvc-adapter 1.7.1 com.alibaba.csp sentinel-parameter-flow-control 1.7.1 com.alibaba.csp sentinel-annotation-aspectj 1.7.1 com.alibaba.csp sentinel-datasource-nacos 1.7.1 ================================================ FILE: lab-46/lab-46-sentinel-demo-nacos/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/Application.java ================================================ package cn.iocoder.springboot.lab46.sentineldemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { // 设置系统属性 project.name,提供给 Sentinel 读取 System.setProperty("project.name", "demo-application"); // 启动 Spring Boot 应用 SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-46/lab-46-sentinel-demo-nacos/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/config/SentinelConfiguration.java ================================================ package cn.iocoder.springboot.lab46.sentineldemo.config; import com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect; import com.alibaba.csp.sentinel.datasource.Converter; import com.alibaba.csp.sentinel.datasource.nacos.NacosDataSource; import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager; import com.alibaba.nacos.api.PropertyKeyConst; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.Arrays; import java.util.List; import java.util.Properties; @Configuration public class SentinelConfiguration { @Bean public SentinelResourceAspect sentinelResourceAspect() { return new SentinelResourceAspect(); } @Bean public NacosDataSource nacosDataSource(ObjectMapper objectMapper) { // Nacos 配置。这里先写死,推荐后面写到 application.yaml 配置文件中。 String serverAddress = "127.0.0.1:8848"; // Nacos 服务器地址 String namespace = ""; // Nacos 命名空间 String dataId = "demo-application-flow-rule"; // Nacos 配置集编号 // String dataId = "example-sentinel"; // Nacos 配置集编号 String group = "DEFAULT_GROUP"; // Nacos 配置分组 // 创建 NacosDataSource 对象 Properties properties = new Properties(); properties.setProperty(PropertyKeyConst.SERVER_ADDR, serverAddress); properties.setProperty(PropertyKeyConst.NAMESPACE, namespace); NacosDataSource> nacosDataSource = new NacosDataSource<>(properties, group, dataId, new Converter>() { // 转换器,将读取的 Nacos 配置,转换成 FlowRule 数组 @Override public List convert(String value) { try { return Arrays.asList(objectMapper.readValue(value, FlowRule[].class)); } catch (JsonProcessingException e) { throw new RuntimeException(e); } } }); // 注册到 FlowRuleManager 中 FlowRuleManager.register2Property(nacosDataSource.getProperty()); return nacosDataSource; } } ================================================ FILE: lab-46/lab-46-sentinel-demo-nacos/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/config/SpringMvcConfiguration.java ================================================ package cn.iocoder.springboot.lab46.sentineldemo.config; import com.alibaba.csp.sentinel.adapter.spring.webmvc.SentinelWebInterceptor; import com.alibaba.csp.sentinel.adapter.spring.webmvc.SentinelWebTotalInterceptor; import com.alibaba.csp.sentinel.adapter.spring.webmvc.config.SentinelWebMvcConfig; import com.alibaba.csp.sentinel.adapter.spring.webmvc.config.SentinelWebMvcTotalConfig; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class SpringMvcConfiguration implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { // Add Sentinel interceptor // addSentinelWebTotalInterceptor(registry); addSentinelWebInterceptor(registry); } private void addSentinelWebInterceptor(InterceptorRegistry registry) { // 创建 SentinelWebMvcConfig 对象 SentinelWebMvcConfig config = new SentinelWebMvcConfig(); config.setHttpMethodSpecify(true); // 是否包含请求方法。即基于 URL 创建的资源,是否包含 Method。 // config.setBlockExceptionHandler(new DefaultBlockExceptionHandler()); // 设置 BlockException 处理器。 // config.setOriginParser(new RequestOriginParser() { // 设置请求来源解析器。用于黑白名单控制功能。 // // @Override // public String parseOrigin(HttpServletRequest request) { // // 从 Header 中,获得请求来源 // String origin = request.getHeader("s-user"); // // 如果为空,给一个默认的 // if (StringUtils.isEmpty(origin)) { // origin = "default"; // } // return origin; // } // // }); // 添加 SentinelWebInterceptor 拦截器 registry.addInterceptor(new SentinelWebInterceptor(config)).addPathPatterns("/**"); } private void addSentinelWebTotalInterceptor(InterceptorRegistry registry) { // 创建 SentinelWebMvcTotalConfig 对象 SentinelWebMvcTotalConfig config = new SentinelWebMvcTotalConfig(); // 添加 SentinelWebTotalInterceptor 拦截器 registry.addInterceptor(new SentinelWebTotalInterceptor(config)).addPathPatterns("/**"); } } ================================================ FILE: lab-46/lab-46-sentinel-demo-nacos/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab46.sentineldemo.controller; import com.alibaba.csp.sentinel.Entry; import com.alibaba.csp.sentinel.SphU; import com.alibaba.csp.sentinel.annotation.SentinelResource; import com.alibaba.csp.sentinel.slots.block.BlockException; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @GetMapping("/echo") public String echo() { return "echo"; } @GetMapping("/test") public String test() { return "test"; } @GetMapping("/sleep") public String sleep() throws InterruptedException { Thread.sleep(100L); return "sleep"; } // 测试热点参数限流 @GetMapping("/product_info") @SentinelResource("demo_product_info_hot") public String productInfo(Integer id) { return "商品编号:" + id; } // 手动使用 Sentinel 客户端 API @GetMapping("/entry_demo") public String entryDemo() { Entry entry = null; try { // 访问资源 entry = SphU.entry("entry_demo"); // ... 执行业务逻辑 return "执行成功"; } catch (BlockException ex) { return "被拒绝"; } finally { // 释放资源 if (entry != null) { entry.exit(); } } } // 测试 @SentinelResource 注解 @GetMapping("/annotations_demo") @SentinelResource(value = "annotations_demo_resource", blockHandler = "blockHandler", fallback = "fallback") public String annotationsDemo(@RequestParam(required = false) Integer id) throws InterruptedException { if (id == null) { throw new IllegalArgumentException("id 参数不允许为空"); } return "success..."; } // BlockHandler 处理函数,参数最后多一个 BlockException,其余与原函数一致. public String blockHandler(Integer id, BlockException ex) { return "block:" + ex.getClass().getSimpleName(); } // Fallback 处理函数,函数签名与原函数一致或加一个 Throwable 类型的参数. public String fallback(Integer id, Throwable throwable) { return "fallback:" + throwable.getMessage(); } } ================================================ FILE: lab-46/lab-46-sentinel-demo-nacos/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/web/GlobalExceptionHandler.java ================================================ package cn.iocoder.springboot.lab46.sentineldemo.web; import com.alibaba.csp.sentinel.slots.block.BlockException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; @ControllerAdvice(basePackages = "cn.iocoder.springboot.lab46.sentineldemo.controller") public class GlobalExceptionHandler { @ResponseBody @ExceptionHandler(value = BlockException.class) public String blockExceptionHandler(BlockException blockException) { return "请求过于频繁"; } } ================================================ FILE: lab-46/lab-46-sentinel-demo-nacos/src/main/resources/sentinel.properties ================================================ csp.sentinel.dashboard.server=127.0.0.1:7070 ================================================ FILE: lab-46/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-46 pom lab-46-sentinel-demo lab-46-sentinel-demo-nacos lab-46-sentinel-demo-apollo lab-46-sentinel-demo-file ================================================ FILE: lab-46/《芋道 Spring Boot 服务容错 Sentinel 入门》.md ================================================ ================================================ FILE: lab-47/lab-47-demo/pom.xml ================================================ lab-47 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-47-demo cn.iocoder.springboot.labs yunai-server-spring-boot-starter 1.0-SNAPSHOT ================================================ FILE: lab-47/lab-47-demo/src/main/java/cn/iocoder/springboot/lab47/demo/DemoApplication.java ================================================ package cn.iocoder.springboot.lab47.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } ================================================ FILE: lab-47/lab-47-demo/src/main/resources/application.yaml ================================================ yunai: server: port: 8888 # 自定义 HttpServer 端口 ================================================ FILE: lab-47/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-47 pom yunai-server-spring-boot-starter lab-47-demo ================================================ FILE: lab-47/yunai-server-spring-boot-starter/pom.xml ================================================ lab-47 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 yunai-server-spring-boot-starter org.springframework.boot spring-boot-starter 2.2.2.RELEASE ================================================ FILE: lab-47/yunai-server-spring-boot-starter/src/main/java/cn/iocoder/springboot/lab47/yunaiserver/autoconfigure/YunaiServerAutoConfiguration.java ================================================ package cn.iocoder.springboot.lab47.yunaiserver.autoconfigure; import com.sun.net.httpserver.HttpServer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.io.IOException; import java.net.InetSocketAddress; @Configuration // 声明配置类 @EnableConfigurationProperties(YunaiServerProperties.class) // 使 YunaiServerProperties 配置属性类生效 public class YunaiServerAutoConfiguration { private Logger logger = LoggerFactory.getLogger(YunaiServerAutoConfiguration.class); @Bean // 声明创建 Bean @ConditionalOnClass(HttpServer.class) // 需要项目中存在 com.sun.net.httpserver.HttpServer 类。该类为 JDK 自带,所以一定成立。 public HttpServer httpServer(YunaiServerProperties serverProperties) throws IOException { // 创建 HttpServer 对象,并启动 HttpServer server = HttpServer.create(new InetSocketAddress(serverProperties.getPort()), 0); server.start(); logger.info("[httpServer][启动服务器成功,端口为:{}]", serverProperties.getPort()); // 返回 return server; } } ================================================ FILE: lab-47/yunai-server-spring-boot-starter/src/main/java/cn/iocoder/springboot/lab47/yunaiserver/autoconfigure/YunaiServerProperties.java ================================================ package cn.iocoder.springboot.lab47.yunaiserver.autoconfigure; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = "yunai.server") public class YunaiServerProperties { /** * 默认端口 */ private static final Integer DEFAULT_PORT = 8000; /** * 端口 */ private Integer port = DEFAULT_PORT; public static Integer getDefaultPort() { return DEFAULT_PORT; } public Integer getPort() { return port; } public YunaiServerProperties setPort(Integer port) { this.port = port; return this; } } ================================================ FILE: lab-47/yunai-server-spring-boot-starter/src/main/resources/META-INF/spring.factories ================================================ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ cn.iocoder.springboot.lab47.yunaiserver.autoconfigure.YunaiServerAutoConfiguration ================================================ FILE: lab-47/《芋道 Spring Boot 自动配置原理》.md ================================================ ================================================ FILE: lab-48-hot-swap/lab-48-demo/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-48-demo org.springframework.boot spring-boot-starter-web ================================================ FILE: lab-48-hot-swap/lab-48-demo/src/main/java/cn/iocoder/springboot/lab48/demo/DemoApplication.java ================================================ package cn.iocoder.springboot.lab48.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } ================================================ FILE: lab-48-hot-swap/lab-48-demo/src/main/java/cn/iocoder/springboot/lab48/demo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab48.demo.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @GetMapping("/echo") public String echo() { return "echo"; } } ================================================ FILE: lab-48-hot-swap/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-48-hot-swap pom lab-48-demo ================================================ FILE: lab-48-hot-swap/《芋道 Spring Boot 热部署入门》.md ================================================ ================================================ FILE: lab-49/lab-49-lombok-demo/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-49-lombok-demo org.springframework.boot spring-boot-starter org.projectlombok lombok org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-49/lab-49-lombok-demo/src/main/java/cn/iocoder/springboot/lab49/lombokdemo/LombokApplication.java ================================================ package cn.iocoder.springboot.lab49.lombokdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class LombokApplication { public static void main(String[] args) { // 启动 Spring Boot 应用 SpringApplication.run(LombokApplication.class, args); } } ================================================ FILE: lab-49/lab-49-lombok-demo/src/main/java/cn/iocoder/springboot/lab49/lombokdemo/dataobject/UserDO.java ================================================ package cn.iocoder.springboot.lab49.lombokdemo.dataobject; import lombok.Getter; import lombok.Setter; /** * 用户 DO */ @Setter @Getter public class UserDO { private String username; private String password; } ================================================ FILE: lab-49/lab-49-lombok-demo/src/main/java/cn/iocoder/springboot/lab49/lombokdemo/dataobject/UserDO01.java ================================================ package cn.iocoder.springboot.lab49.lombokdemo.dataobject; import lombok.Data; /** * 用户 DO */ @Data public class UserDO01 { private String username; private String password; } ================================================ FILE: lab-49/lab-49-lombok-demo/src/main/java/cn/iocoder/springboot/lab49/lombokdemo/service/UserService.java ================================================ package cn.iocoder.springboot.lab49.lombokdemo.service; import lombok.extern.slf4j.Slf4j; @Slf4j public class UserService { public static void staticMethod() { log.info("静态方法示例"); } public void normalMethod() { log.info("普通方法示例"); } } ================================================ FILE: lab-49/lab-49-lombok-demo/src/main/java/cn/iocoder/springboot/lab49/lombokdemo/service/UserService01.java ================================================ package cn.iocoder.springboot.lab49.lombokdemo.service; import lombok.NonNull; import org.springframework.stereotype.Service; @Service public class UserService01 { public void test(@NonNull String username, String password) { } } ================================================ FILE: lab-49/lab-49-lombok-demo/src/test/java/cn/iocoder/springboot/lab49/lombokdemo/dataobject/UserDOTest.java ================================================ package cn.iocoder.springboot.lab49.lombokdemo.dataobject; import org.junit.Test; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest public class UserDOTest { @Test public void demo01() { UserDO user = new UserDO(); user.setUsername("username:1"); user.setPassword("password:1"); System.out.println(user); } } ================================================ FILE: lab-49/lab-49-lombok-demo/src/test/java/cn/iocoder/springboot/lab49/lombokdemo/package-info.java ================================================ package cn.iocoder.springboot.lab49.lombokdemo; ================================================ FILE: lab-49/lab-49-lombok-demo/src/test/java/cn/iocoder/springboot/lab49/lombokdemo/service/UserService01Test.java ================================================ package cn.iocoder.springboot.lab49.lombokdemo.service; import org.junit.Test; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest public class UserService01Test { private UserService01 userService01 = new UserService01(); @Test public void test() { userService01.test(null, null); } } ================================================ FILE: lab-49/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-49 pom lab-49-lombok-demo ================================================ FILE: lab-49/《芋道 Spring Boot 消除冗余代码 Lombok 入门》.md ================================================ ================================================ FILE: lab-50/lab-50-demo/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-50-demo org.springframework.boot spring-boot-starter-mail org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-50/lab-50-demo/src/main/java/cn/iocoder/springboot/lab50/maildemo/Application.java ================================================ package cn.iocoder.springboot.lab50.maildemo; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { } ================================================ FILE: lab-50/lab-50-demo/src/main/resources/application.yml ================================================ #spring: # mail: # 配置发送告警的邮箱 # host: smtp.163.com # port: 465 # protocol: smtps # username: yudaoyuanma@163.com # password: '***' # default-encoding: UTF-8 ## properties: ## smtp: ## auth: true ## starttls: ## enable: true ## required: true spring: mail: # 配置发送告警的邮箱 host: smtp.exmail.qq.com port: 465 protocol: smtps username: yunai.wang@medcloud.cn password: '***' default-encoding: UTF-8 properties: smtp: auth: true starttls: enable: true required: true ================================================ FILE: lab-50/lab-50-demo/src/test/java/cn/iocoder/springboot/lab50/maildemo/ApplicationTests.java ================================================ package cn.iocoder.springboot.lab50.maildemo; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.mail.SimpleMailMessage; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class ApplicationTests { @Autowired private JavaMailSender mailSender; @Value("${spring.mail.username}") private String username; @Test public void testSend() { SimpleMailMessage message = new SimpleMailMessage(); message.setFrom(username); message.setTo("7685413@qq.com"); message.setSubject("我是测试主题"); message.setText("我是测试内容"); mailSender.send(message); } } ================================================ FILE: lab-50/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-50 pom lab-50-demo ================================================ FILE: lab-50/计划中 ================================================ ================================================ FILE: lab-51/lab-51-sentry-logback/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-51-sentry-logback org.springframework.boot spring-boot-starter-web io.sentry sentry-logback 1.7.30 ================================================ FILE: lab-51/lab-51-sentry-logback/src/main/java/cn/iocoder/springboot/lab51/sentrydemo/DemoApplication.java ================================================ package cn.iocoder.springboot.lab51.sentrydemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } ================================================ FILE: lab-51/lab-51-sentry-logback/src/main/java/cn/iocoder/springboot/lab51/sentrydemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab51.sentrydemo.controller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { private Logger logger = LoggerFactory.getLogger(getClass()); @GetMapping("/sentry") public String sentry() { logger.error("[main][我就是展示下异常!]"); return "success"; } @GetMapping("/exception") public String exception() { throw new RuntimeException("直接抛出异常"); } } ================================================ FILE: lab-51/lab-51-sentry-logback/src/main/java/cn/iocoder/springboot/lab51/sentrydemo/core/package-info.java ================================================ package cn.iocoder.springboot.lab51.sentrydemo.core; ================================================ FILE: lab-51/lab-51-sentry-logback/src/main/java/cn/iocoder/springboot/lab51/sentrydemo/core/web/GlobalExceptionHandler.java ================================================ package cn.iocoder.springboot.lab51.sentrydemo.core.web; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletRequest; @ControllerAdvice(basePackages = "cn.iocoder.springboot.lab51.sentrydemo.controller") public class GlobalExceptionHandler { private Logger logger = LoggerFactory.getLogger(getClass()); // ... 省略其它类型异常的处理 /** * 处理其它 Exception 异常 */ @ResponseBody @ExceptionHandler(value = Exception.class) public String exceptionHandler(HttpServletRequest req, Exception e) { // 记录异常日志 logger.error("[exceptionHandler]", e); // 返回 ERROR CommonResult return "系统异常"; } } ================================================ FILE: lab-51/lab-51-sentry-logback/src/main/resources/logback-spring.xml ================================================ WARN %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n ================================================ FILE: lab-51/lab-51-sentry-logback/src/main/resources/sentry.properties ================================================ # Sentry 配置文件。每个配置 key,从 DefaultSentryClientFactory 类中寻找 # DSN dsn=http://b10468b71cd945358ff88ed975287194@47.56.150.64:9000/3 # HTTP 请求建立连接超时时间 timeout=60000 # HTTP 请求等待结果超时时间 readtimeout=60000 ================================================ FILE: lab-51/lab-51-sentry-spring/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-51-sentry-spring io.sentry sentry-spring-boot-starter 1.7.30 ================================================ FILE: lab-51/lab-51-sentry-spring/src/main/java/cn/iocoder/springboot/lab51/sentrydemo/DemoApplication.java ================================================ package cn.iocoder.springboot.lab51.sentrydemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { public static void main(String[] args) throws InterruptedException { // 启动 Spring Boot 应用 SpringApplication.run(DemoApplication.class, args); } } ================================================ FILE: lab-51/lab-51-sentry-spring/src/main/java/cn/iocoder/springboot/lab51/sentrydemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab51.sentrydemo.controller; import io.sentry.SentryClient; import io.sentry.event.EventBuilder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @Autowired private SentryClient sentryClient; @GetMapping("/sentry") public String sentry() { // 上传消息到 Sentry 中 sentryClient.sendMessage("示例消息"); // 上传异常到 Sentry 中 sentryClient.sendException(new RuntimeException("测试异常")); // 上传事件到 Sentry 中 sentryClient.sendEvent(new EventBuilder().withMessage("示例事件").build()); return "success"; } @GetMapping("/exception") public String exception() { throw new RuntimeException("直接抛出异常"); } } ================================================ FILE: lab-51/lab-51-sentry-spring/src/main/resources/application.yml ================================================ # Sentry 配置项,对应 SentryProperties 配置类 sentry: dsn: http://b10468b71cd945358ff88ed975287194@47.56.150.64:9000/3 # DSN timeout: 60000 # HTTP 请求建立连接超时时间 server: port: 18080 ================================================ FILE: lab-51/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-51 pom lab-51-sentry-logback lab-51-sentry-spring ================================================ FILE: lab-51/《芋道 Spring Boot 异常管理平台 Sentry 入门》.md ================================================ ================================================ FILE: lab-52/lab-52-multiple-datasource/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-52-multiple-datasource org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 5.1.48 org.mybatis.spring.boot mybatis-spring-boot-starter 2.1.2 com.baomidou dynamic-datasource-spring-boot-starter 3.0.0 io.seata seata-spring-boot-starter 1.1.0 ================================================ FILE: lab-52/lab-52-multiple-datasource/src/main/java/cn/iocoder/springboot/lab52/seatademo/MultipleDatasourceApplication.java ================================================ package cn.iocoder.springboot.lab52.seatademo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class MultipleDatasourceApplication { public static void main(String[] args) { SpringApplication.run(MultipleDatasourceApplication.class, args); } } ================================================ FILE: lab-52/lab-52-multiple-datasource/src/main/java/cn/iocoder/springboot/lab52/seatademo/controller/OrderController.java ================================================ package cn.iocoder.springboot.lab52.seatademo.controller; import cn.iocoder.springboot.lab52.seatademo.service.OrderService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/order") public class OrderController { private Logger logger = LoggerFactory.getLogger(OrderController.class); @Autowired private OrderService orderService; @PostMapping("/create") public Integer createOrder(@RequestParam("userId") Long userId, @RequestParam("productId") Long productId, @RequestParam("price") Integer price) throws Exception { logger.info("[createOrder] 收到下单请求,用户:{}, 商品:{}, 价格:{}", userId, productId, price); return orderService.createOrder(userId, productId, price); } } ================================================ FILE: lab-52/lab-52-multiple-datasource/src/main/java/cn/iocoder/springboot/lab52/seatademo/dao/AccountDao.java ================================================ package cn.iocoder.springboot.lab52.seatademo.dao; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; import org.springframework.stereotype.Repository; @Mapper @Repository public interface AccountDao { /** * 获取账户余额 * * @param userId 用户 ID * @return 账户余额 */ @Select("SELECT balance FROM account WHERE id = #{userId}") Integer getBalance(@Param("userId") Long userId); /** * 扣减余额 * * @param price 需要扣减的数目 * @return 影响记录行数 */ @Update("UPDATE account SET balance = balance - #{price} WHERE id = 1 AND balance >= ${price}") int reduceBalance(@Param("price") Integer price); } ================================================ FILE: lab-52/lab-52-multiple-datasource/src/main/java/cn/iocoder/springboot/lab52/seatademo/dao/OrderDao.java ================================================ package cn.iocoder.springboot.lab52.seatademo.dao; import cn.iocoder.springboot.lab52.seatademo.entity.OrderDO; import org.apache.ibatis.annotations.*; import org.springframework.stereotype.Repository; @Mapper @Repository public interface OrderDao { /** * 插入订单记录 * * @param order 订单 * @return 影响记录数量 */ @Insert("INSERT INTO orders (user_id, product_id, pay_amount) VALUES (#{userId}, #{productId}, #{payAmount})") @Options(useGeneratedKeys = true, keyColumn = "id", keyProperty = "id") int saveOrder(OrderDO order); } ================================================ FILE: lab-52/lab-52-multiple-datasource/src/main/java/cn/iocoder/springboot/lab52/seatademo/dao/ProductDao.java ================================================ package cn.iocoder.springboot.lab52.seatademo.dao; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; import org.springframework.stereotype.Repository; @Mapper @Repository public interface ProductDao { /** * 获取库存 * * @param productId 商品编号 * @return 库存 */ @Select("SELECT stock FROM product WHERE id = #{productId}") Integer getStock(@Param("productId") Long productId); /** * 扣减库存 * * @param productId 商品编号 * @param amount 扣减数量 * @return 影响记录行数 */ @Update("UPDATE product SET stock = stock - #{amount} WHERE id = #{productId} AND stock >= #{amount}") int reduceStock(@Param("productId") Long productId, @Param("amount") Integer amount); } ================================================ FILE: lab-52/lab-52-multiple-datasource/src/main/java/cn/iocoder/springboot/lab52/seatademo/entity/OrderDO.java ================================================ package cn.iocoder.springboot.lab52.seatademo.entity; /** * 订单实体 */ public class OrderDO { /** 订单编号 **/ private Integer id; /** 用户编号 **/ private Long userId; /** 产品编号 **/ private Long productId; /** 支付金额 **/ private Integer payAmount; public Integer getId() { return id; } public OrderDO setId(Integer id) { this.id = id; return this; } public Long getUserId() { return userId; } public OrderDO setUserId(Long userId) { this.userId = userId; return this; } public Long getProductId() { return productId; } public OrderDO setProductId(Long productId) { this.productId = productId; return this; } public Integer getPayAmount() { return payAmount; } public OrderDO setPayAmount(Integer payAmount) { this.payAmount = payAmount; return this; } } ================================================ FILE: lab-52/lab-52-multiple-datasource/src/main/java/cn/iocoder/springboot/lab52/seatademo/service/AccountService.java ================================================ package cn.iocoder.springboot.lab52.seatademo.service; /** * 账户 Service */ public interface AccountService { /** * 扣除余额 * * @param userId 用户编号 * @param price 扣减金额 * @throws Exception 失败时抛出异常 */ void reduceBalance(Long userId, Integer price) throws Exception; } ================================================ FILE: lab-52/lab-52-multiple-datasource/src/main/java/cn/iocoder/springboot/lab52/seatademo/service/OrderService.java ================================================ package cn.iocoder.springboot.lab52.seatademo.service; /** * 订单 Service */ public interface OrderService { /** * 创建订单 * * @param userId 用户编号 * @param productId 产品编号 * @param price 价格 * @return 订单编号 * @throws Exception 创建订单失败,抛出异常 */ Integer createOrder(Long userId, Long productId, Integer price) throws Exception; } ================================================ FILE: lab-52/lab-52-multiple-datasource/src/main/java/cn/iocoder/springboot/lab52/seatademo/service/ProductService.java ================================================ package cn.iocoder.springboot.lab52.seatademo.service; /** * 商品 Service */ public interface ProductService { /** * 扣减库存 * * @param productId 商品 ID * @param amount 扣减数量 * @throws Exception 扣减失败时抛出异常 */ void reduceStock(Long productId, Integer amount) throws Exception; } ================================================ FILE: lab-52/lab-52-multiple-datasource/src/main/java/cn/iocoder/springboot/lab52/seatademo/service/impl/AccountServiceImpl.java ================================================ package cn.iocoder.springboot.lab52.seatademo.service.impl; import cn.iocoder.springboot.lab52.seatademo.dao.AccountDao; import cn.iocoder.springboot.lab52.seatademo.service.AccountService; import com.baomidou.dynamic.datasource.annotation.DS; import io.seata.core.context.RootContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @Service public class AccountServiceImpl implements AccountService { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private AccountDao accountDao; @Override @DS(value = "account-ds") @Transactional(propagation = Propagation.REQUIRES_NEW) // 开启新事物 public void reduceBalance(Long userId, Integer price) throws Exception { logger.info("[reduceBalance] 当前 XID: {}", RootContext.getXID()); // 检查余额 checkBalance(userId, price); logger.info("[reduceBalance] 开始扣减用户 {} 余额", userId); // 扣除余额 int updateCount = accountDao.reduceBalance(price); // 扣除成功 if (updateCount == 0) { logger.warn("[reduceBalance] 扣除用户 {} 余额失败", userId); throw new Exception("余额不足"); } logger.info("[reduceBalance] 扣除用户 {} 余额成功", userId); } private void checkBalance(Long userId, Integer price) throws Exception { logger.info("[checkBalance] 检查用户 {} 余额", userId); Integer balance = accountDao.getBalance(userId); if (balance < price) { logger.warn("[checkBalance] 用户 {} 余额不足,当前余额:{}", userId, balance); throw new Exception("余额不足"); } } } ================================================ FILE: lab-52/lab-52-multiple-datasource/src/main/java/cn/iocoder/springboot/lab52/seatademo/service/impl/OrderServiceImpl.java ================================================ package cn.iocoder.springboot.lab52.seatademo.service.impl; import cn.iocoder.springboot.lab52.seatademo.dao.OrderDao; import cn.iocoder.springboot.lab52.seatademo.entity.OrderDO; import cn.iocoder.springboot.lab52.seatademo.service.OrderService; import cn.iocoder.springboot.lab52.seatademo.service.AccountService; import cn.iocoder.springboot.lab52.seatademo.service.ProductService; import com.baomidou.dynamic.datasource.annotation.DS; import io.seata.core.context.RootContext; import io.seata.spring.annotation.GlobalTransactional; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class OrderServiceImpl implements OrderService { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private OrderDao orderDao; @Autowired private AccountService accountService; @Autowired private ProductService productService; @Override @DS(value = "order-ds") @GlobalTransactional public Integer createOrder(Long userId, Long productId, Integer price) throws Exception { Integer amount = 1; // 购买数量,暂时设置为 1。 logger.info("[createOrder] 当前 XID: {}", RootContext.getXID()); // 扣减库存 productService.reduceStock(productId, amount); // 扣减余额 accountService.reduceBalance(userId, price); // 保存订单 OrderDO order = new OrderDO().setUserId(userId).setProductId(productId).setPayAmount(amount * price); orderDao.saveOrder(order); logger.info("[createOrder] 保存订单: {}", order.getId()); // 返回订单编号 return order.getId(); } } ================================================ FILE: lab-52/lab-52-multiple-datasource/src/main/java/cn/iocoder/springboot/lab52/seatademo/service/impl/ProductServiceImpl.java ================================================ package cn.iocoder.springboot.lab52.seatademo.service.impl; import cn.iocoder.springboot.lab52.seatademo.dao.ProductDao; import cn.iocoder.springboot.lab52.seatademo.service.ProductService; import com.baomidou.dynamic.datasource.annotation.DS; import io.seata.core.context.RootContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @Service public class ProductServiceImpl implements ProductService { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private ProductDao productDao; @Override @DS(value = "product-ds") @Transactional(propagation = Propagation.REQUIRES_NEW) // 开启新事物 public void reduceStock(Long productId, Integer amount) throws Exception { logger.info("[reduceStock] 当前 XID: {}", RootContext.getXID()); // 检查库存 checkStock(productId, amount); logger.info("[reduceStock] 开始扣减 {} 库存", productId); // 扣减库存 int updateCount = productDao.reduceStock(productId, amount); // 扣除成功 if (updateCount == 0) { logger.warn("[reduceStock] 扣除 {} 库存失败", productId); throw new Exception("库存不足"); } // 扣除失败 logger.info("[reduceStock] 扣除 {} 库存成功", productId); } private void checkStock(Long productId, Integer requiredAmount) throws Exception { logger.info("[checkStock] 检查 {} 库存", productId); Integer stock = productDao.getStock(productId); if (stock < requiredAmount) { logger.warn("[checkStock] {} 库存不足,当前库存: {}", productId, stock); throw new Exception("库存不足"); } } } ================================================ FILE: lab-52/lab-52-multiple-datasource/src/main/resources/application.yaml ================================================ server: port: 8081 # 端口 spring: application: name: multi-datasource-service # 应用名 datasource: # dynamic-datasource-spring-boot-starter 动态数据源的配配项,对应 DynamicDataSourceProperties 类 dynamic: primary: order-ds # 设置默认的数据源或者数据源组,默认值即为 master datasource: # 订单 order 数据源配置 order-ds: url: jdbc:mysql://127.0.0.1:3306/seata_order?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: # 账户 pay 数据源配置 account-ds: url: jdbc:mysql://127.0.0.1:3306/seata_account?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: # 商品 product 数据源配置 product-ds: url: jdbc:mysql://127.0.0.1:3306/seata_product?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: seata: true # 是否启动对 Seata 的集成 # Seata 配置项,对应 SeataProperties 类 seata: application-id: ${spring.application.name} # Seata 应用编号,默认为 ${spring.application.name} tx-service-group: ${spring.application.name}-group # Seata 事务组编号,用于 TC 集群名 # 服务配置项,对应 ServiceProperties 类 service: # 虚拟组和分组的映射 vgroup-mapping: multi-datasource-service-group: default # 分组和 Seata 服务的映射 grouplist: default: 127.0.0.1:8091 ================================================ FILE: lab-52/lab-52-multiple-datasource/src/main/resources/data.sql ================================================ # Order DROP DATABASE IF EXISTS seata_order; CREATE DATABASE seata_order; CREATE TABLE seata_order.orders ( id INT(11) NOT NULL AUTO_INCREMENT, user_id INT(11) DEFAULT NULL, product_id INT(11) DEFAULT NULL, pay_amount DECIMAL(10, 0) DEFAULT NULL, add_time DATETIME DEFAULT CURRENT_TIMESTAMP, last_update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8; CREATE TABLE seata_order.undo_log ( id BIGINT(20) NOT NULL AUTO_INCREMENT, branch_id BIGINT(20) NOT NULL, xid VARCHAR(100) NOT NULL, context VARCHAR(128) NOT NULL, rollback_info LONGBLOB NOT NULL, log_status INT(11) NOT NULL, log_created DATETIME NOT NULL, log_modified DATETIME NOT NULL, PRIMARY KEY (id), UNIQUE KEY ux_undo_log (xid, branch_id) ) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8; # Product DROP DATABASE IF EXISTS seata_product; CREATE DATABASE seata_product; CREATE TABLE seata_product.product ( id INT(11) NOT NULL AUTO_INCREMENT, stock INT(11) DEFAULT NULL, last_update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8; INSERT INTO seata_product.product (id, stock) VALUES (1, 10); # 插入一条产品的库存 CREATE TABLE seata_product.undo_log ( id BIGINT(20) NOT NULL AUTO_INCREMENT, branch_id BIGINT(20) NOT NULL, xid VARCHAR(100) NOT NULL, context VARCHAR(128) NOT NULL, rollback_info LONGBLOB NOT NULL, log_status INT(11) NOT NULL, log_created DATETIME NOT NULL, log_modified DATETIME NOT NULL, PRIMARY KEY (id), UNIQUE KEY ux_undo_log (xid, branch_id) ) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8; # Account DROP DATABASE IF EXISTS seata_account; CREATE DATABASE seata_account; CREATE TABLE seata_account.account ( id INT(11) NOT NULL AUTO_INCREMENT, balance DOUBLE DEFAULT NULL, last_update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8; CREATE TABLE seata_account.undo_log ( id BIGINT(20) NOT NULL AUTO_INCREMENT, branch_id BIGINT(20) NOT NULL, xid VARCHAR(100) NOT NULL, context VARCHAR(128) NOT NULL, rollback_info LONGBLOB NOT NULL, log_status INT(11) NOT NULL, log_created DATETIME NOT NULL, log_modified DATETIME NOT NULL, PRIMARY KEY (id), UNIQUE KEY ux_undo_log (xid, branch_id) ) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8; INSERT INTO seata_account.account (id, balance) VALUES (1, 10); ================================================ FILE: lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-account-service/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-52-seata-at-httpclient-demo-account-service org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 5.1.48 org.mybatis.spring.boot mybatis-spring-boot-starter 2.1.2 io.seata seata-spring-boot-starter 1.1.0 io.seata seata-http 1.1.0 org.apache.httpcomponents httpclient 4.5.8 ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab52.accountservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class AccountServiceApplication { public static void main(String[] args) { SpringApplication.run(AccountServiceApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab52.accountservice.controller; import cn.iocoder.springboot.lab52.accountservice.dto.AccountReduceBalanceDTO; import cn.iocoder.springboot.lab52.accountservice.service.AccountService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/account") public class AccountController { private Logger logger = LoggerFactory.getLogger(AccountController.class); @Autowired private AccountService accountService; @PostMapping("/reduce-balance") public Boolean reduceBalance(@RequestBody AccountReduceBalanceDTO accountReduceBalanceDTO) { logger.info("[reduceBalance] 收到减少余额请求, 用户:{}, 金额:{}", accountReduceBalanceDTO.getUserId(), accountReduceBalanceDTO.getPrice()); try { accountService.reduceBalance(accountReduceBalanceDTO.getUserId(), accountReduceBalanceDTO.getPrice()); // 正常扣除余额,返回 true return true; } catch (Exception e) { // 失败扣除余额,返回 false return false; } } } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab52.accountservice.dao; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; import org.springframework.stereotype.Repository; @Mapper @Repository public interface AccountDao { /** * 获取账户余额 * * @param userId 用户 ID * @return 账户余额 */ @Select("SELECT balance FROM account WHERE id = #{userId}") Integer getBalance(@Param("userId") Long userId); /** * 扣减余额 * * @param price 需要扣减的数目 * @return 影响记录行数 */ @Update("UPDATE account SET balance = balance - #{price} WHERE id = 1 AND balance >= ${price}") int reduceBalance(@Param("price") Integer price); } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab52.accountservice.dto; /** * 账户减少余额 DTO */ public class AccountReduceBalanceDTO { /** * 用户编号 */ private Long userId; /** * 扣减金额 */ private Integer price; public Long getUserId() { return userId; } public AccountReduceBalanceDTO setUserId(Long userId) { this.userId = userId; return this; } public Integer getPrice() { return price; } public AccountReduceBalanceDTO setPrice(Integer price) { this.price = price; return this; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab52.accountservice.service; /** * 账户 Service */ public interface AccountService { /** * 扣除余额 * * @param userId 用户编号 * @param price 扣减金额 * @throws Exception 失败时抛出异常 */ void reduceBalance(Long userId, Integer price) throws Exception; } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab52.accountservice.service; import cn.iocoder.springboot.lab52.accountservice.dao.AccountDao; import io.seata.core.context.RootContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class AccountServiceImpl implements AccountService { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private AccountDao accountDao; @Override @Transactional // 开启新事物 public void reduceBalance(Long userId, Integer price) throws Exception { logger.info("[reduceBalance] 当前 XID: {}", RootContext.getXID()); // 检查余额 checkBalance(userId, price); logger.info("[reduceBalance] 开始扣减用户 {} 余额", userId); // 扣除余额 int updateCount = accountDao.reduceBalance(price); // 扣除成功 if (updateCount == 0) { logger.warn("[reduceBalance] 扣除用户 {} 余额失败", userId); throw new Exception("余额不足"); } logger.info("[reduceBalance] 扣除用户 {} 余额成功", userId); } private void checkBalance(Long userId, Integer price) throws Exception { logger.info("[checkBalance] 检查用户 {} 余额", userId); Integer balance = accountDao.getBalance(userId); if (balance < price) { logger.warn("[checkBalance] 用户 {} 余额不足,当前余额:{}", userId, balance); throw new Exception("余额不足"); } } } ================================================ FILE: lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-account-service/src/main/resources/application.yaml ================================================ server: port: 8083 spring: application: name: account-service datasource: url: jdbc:mysql://127.0.0.1:3306/seata_account?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: # Seata 配置项,对应 SeataProperties 类 seata: application-id: ${spring.application.name} # Seata 应用编号,默认为 ${spring.application.name} tx-service-group: ${spring.application.name}-group # Seata 事务组编号,用于 TC 集群名 # 服务配置项,对应 ServiceProperties 类 service: # 虚拟组和分组的映射 vgroup-mapping: account-service-group: default # 分组和 Seata 服务的映射 grouplist: default: 127.0.0.1:8091 ================================================ FILE: lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-order-service/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-52-seata-at-httpclient-demo-order-service org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 5.1.48 org.mybatis.spring.boot mybatis-spring-boot-starter 2.1.2 io.seata seata-spring-boot-starter 1.1.0 io.seata seata-http 1.1.0 org.apache.httpcomponents httpclient 4.5.8 ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab52.orderservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class OrderServiceApplication { public static void main(String[] args) { SpringApplication.run(OrderServiceApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab52.orderservice.controller; import cn.iocoder.springboot.lab52.orderservice.service.OrderService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/order") public class OrderController { private Logger logger = LoggerFactory.getLogger(OrderController.class); @Autowired private OrderService orderService; @PostMapping("/create") public Integer createOrder(@RequestParam("userId") Long userId, @RequestParam("productId") Long productId, @RequestParam("price") Integer price) throws Exception { logger.info("[createOrder] 收到下单请求,用户:{}, 商品:{}, 价格:{}", userId, productId, price); return orderService.createOrder(userId, productId, price); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab52.orderservice.dao; import cn.iocoder.springboot.lab53.orderservice.entity.OrderDO; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Options; import org.springframework.stereotype.Repository; @Mapper @Repository public interface OrderDao { /** * 插入订单记录 * * @param order 订单 * @return 影响记录数量 */ @Insert("INSERT INTO orders (user_id, product_id, pay_amount) VALUES (#{userId}, #{productId}, #{payAmount})") @Options(useGeneratedKeys = true, keyColumn = "id", keyProperty = "id") int saveOrder(OrderDO order); } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab53.orderservice.entity; /** * 订单实体 */ public class OrderDO { /** 订单编号 **/ private Integer id; /** 用户编号 **/ private Long userId; /** 产品编号 **/ private Long productId; /** 支付金额 **/ private Integer payAmount; public Integer getId() { return id; } public OrderDO setId(Integer id) { this.id = id; return this; } public Long getUserId() { return userId; } public OrderDO setUserId(Long userId) { this.userId = userId; return this; } public Long getProductId() { return productId; } public OrderDO setProductId(Long productId) { this.productId = productId; return this; } public Integer getPayAmount() { return payAmount; } public OrderDO setPayAmount(Integer payAmount) { this.payAmount = payAmount; return this; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab52.orderservice.service; /** * 订单 Service */ public interface OrderService { /** * 创建订单 * * @param userId 用户编号 * @param productId 产品编号 * @param price 价格 * @return 订单编号 * @throws Exception 创建订单失败,抛出异常 */ Integer createOrder(Long userId, Long productId, Integer price) throws Exception; } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab52.orderservice.service; import cn.iocoder.springboot.lab52.orderservice.dao.OrderDao; import cn.iocoder.springboot.lab53.orderservice.entity.OrderDO; import com.alibaba.fastjson.JSONObject; import io.seata.core.context.RootContext; import io.seata.integration.http.DefaultHttpExecutor; import io.seata.spring.annotation.GlobalTransactional; import org.apache.http.HttpResponse; import org.apache.http.util.EntityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.io.IOException; @Service public class OrderServiceImpl implements OrderService { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private OrderDao orderDao; @Override @GlobalTransactional public Integer createOrder(Long userId, Long productId, Integer price) throws Exception { Integer amount = 1; // 购买数量,暂时设置为 1。 logger.info("[createOrder] 当前 XID: {}", RootContext.getXID()); // 扣减库存 this.reduceStock(productId, amount); // 扣减余额 this.reduceBalance(userId, price); // 保存订单 OrderDO order = new OrderDO().setUserId(userId).setProductId(productId).setPayAmount(amount * price); orderDao.saveOrder(order); logger.info("[createOrder] 保存订单: {}", order.getId()); // 返回订单编号 return order.getId(); } private void reduceStock(Long productId, Integer amount) throws IOException { // 参数拼接 JSONObject params = new JSONObject().fluentPut("productId", String.valueOf(productId)) .fluentPut("amount", String.valueOf(amount)); // 执行调用 HttpResponse response = DefaultHttpExecutor.getInstance().executePost("http://127.0.0.1:8082", "/product/reduce-stock", params, HttpResponse.class); // 解析结果 Boolean success = Boolean.valueOf(EntityUtils.toString(response.getEntity())); if (!success) { throw new RuntimeException("扣除库存失败"); } } private void reduceBalance(Long userId, Integer price) throws IOException { // 参数拼接 JSONObject params = new JSONObject().fluentPut("userId", String.valueOf(userId)) .fluentPut("price", String.valueOf(price)); // 执行调用 HttpResponse response = DefaultHttpExecutor.getInstance().executePost("http://127.0.0.1:8083", "/account/reduce-balance", params, HttpResponse.class); // 解析结果 Boolean success = Boolean.valueOf(EntityUtils.toString(response.getEntity())); if (!success) { throw new RuntimeException("扣除余额失败"); } } } ================================================ FILE: lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-order-service/src/main/resources/application.yaml ================================================ server: port: 8081 # 端口 spring: application: name: order-service datasource: url: jdbc:mysql://127.0.0.1:3306/seata_order?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: # Seata 配置项,对应 SeataProperties 类 seata: application-id: ${spring.application.name} # Seata 应用编号,默认为 ${spring.application.name} tx-service-group: ${spring.application.name}-group # Seata 事务组编号,用于 TC 集群名 # 服务配置项,对应 ServiceProperties 类 service: # 虚拟组和分组的映射 vgroup-mapping: order-service-group: default # 分组和 Seata 服务的映射 grouplist: default: 127.0.0.1:8091 ================================================ FILE: lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-product-service/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-52-seata-at-httpclient-demo-product-service org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 5.1.48 org.mybatis.spring.boot mybatis-spring-boot-starter 2.1.2 io.seata seata-spring-boot-starter 1.1.0 io.seata seata-http 1.1.0 ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab52.productservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ProductServiceApplication { public static void main(String[] args) { SpringApplication.run(ProductServiceApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab52.productservice.controller; import cn.iocoder.springboot.lab52.productservice.dto.ProductReduceStockDTO; import cn.iocoder.springboot.lab52.productservice.service.ProductService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/product") public class ProductController { private Logger logger = LoggerFactory.getLogger(ProductController.class); @Autowired private ProductService productService; @PostMapping("/reduce-stock") public Boolean reduceStock(@RequestBody ProductReduceStockDTO productReduceStockDTO) { logger.info("[reduceStock] 收到减少库存请求, 商品:{}, 价格:{}", productReduceStockDTO.getProductId(), productReduceStockDTO.getAmount()); try { productService.reduceStock(productReduceStockDTO.getProductId(), productReduceStockDTO.getAmount()); // 正常扣除库存,返回 true return true; } catch (Exception e) { // 失败扣除库存,返回 false return false; } } } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab52.productservice.dao; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; import org.springframework.stereotype.Repository; @Mapper @Repository public interface ProductDao { /** * 获取库存 * * @param productId 商品编号 * @return 库存 */ @Select("SELECT stock FROM product WHERE id = #{productId}") Integer getStock(@Param("productId") Long productId); /** * 扣减库存 * * @param productId 商品编号 * @param amount 扣减数量 * @return 影响记录行数 */ @Update("UPDATE product SET stock = stock - #{amount} WHERE id = #{productId} AND stock >= #{amount}") int reduceStock(@Param("productId") Long productId, @Param("amount") Integer amount); } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab52.productservice.dto; /** * 商品减少库存 DTO */ public class ProductReduceStockDTO { /** * 商品编号 */ private Long productId; /** * 数量 */ private Integer amount; public Long getProductId() { return productId; } public ProductReduceStockDTO setProductId(Long productId) { this.productId = productId; return this; } public Integer getAmount() { return amount; } public ProductReduceStockDTO setAmount(Integer amount) { this.amount = amount; return this; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab52.productservice.service; /** * 商品 Service */ public interface ProductService { /** * 扣减库存 * * @param productId 商品 ID * @param amount 扣减数量 * @throws Exception 扣减失败时抛出异常 */ void reduceStock(Long productId, Integer amount) throws Exception; } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab52.productservice.service; import cn.iocoder.springboot.lab52.productservice.dao.ProductDao; import io.seata.core.context.RootContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class ProductServiceImpl implements ProductService { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private ProductDao productDao; @Override @Transactional // 开启事物 public void reduceStock(Long productId, Integer amount) throws Exception { logger.info("[reduceStock] 当前 XID: {}", RootContext.getXID()); // 检查库存 checkStock(productId, amount); logger.info("[reduceStock] 开始扣减 {} 库存", productId); // 扣减库存 int updateCount = productDao.reduceStock(productId, amount); // 扣除成功 if (updateCount == 0) { logger.warn("[reduceStock] 扣除 {} 库存失败", productId); throw new Exception("库存不足"); } // 扣除失败 logger.info("[reduceStock] 扣除 {} 库存成功", productId); } private void checkStock(Long productId, Integer requiredAmount) throws Exception { logger.info("[checkStock] 检查 {} 库存", productId); Integer stock = productDao.getStock(productId); if (stock < requiredAmount) { logger.warn("[checkStock] {} 库存不足,当前库存: {}", productId, stock); throw new Exception("库存不足"); } } } ================================================ FILE: lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-product-service/src/main/resources/application.yaml ================================================ server: port: 8082 # 端口 spring: application: name: product-service datasource: url: jdbc:mysql://127.0.0.1:3306/seata_product?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: # Seata 配置项,对应 SeataProperties 类 seata: application-id: ${spring.application.name} # Seata 应用编号,默认为 ${spring.application.name} tx-service-group: ${spring.application.name}-group # Seata 事务组编号,用于 TC 集群名 # 服务配置项,对应 ServiceProperties 类 service: # 虚拟组和分组的映射 vgroup-mapping: product-service-group: default # 分组和 Seata 服务的映射 grouplist: default: 127.0.0.1:8091 ================================================ FILE: lab-52/lab-52-seata-at-httpclient-demo/pom.xml ================================================ lab-52 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-52-seata-at-httpclient-demo lab-52-seata-at-httpclient-demo-order-service lab-52-seata-at-httpclient-demo-product-service lab-52-seata-at-httpclient-demo-account-service ================================================ FILE: lab-52/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-52 pom lab-52-multiple-datasource lab-52-seata-at-httpclient-demo ================================================ FILE: lab-52/《芋道 Spring Boot 分布式事务 Seata 入门》.md ================================================ ================================================ FILE: lab-53/lab-53-seata-at-dubbo-demo/data.sql ================================================ # Order DROP DATABASE IF EXISTS seata_order; CREATE DATABASE seata_order; CREATE TABLE seata_order.orders ( id INT(11) NOT NULL AUTO_INCREMENT, user_id INT(11) DEFAULT NULL, product_id INT(11) DEFAULT NULL, pay_amount DECIMAL(10, 0) DEFAULT NULL, add_time DATETIME DEFAULT CURRENT_TIMESTAMP, last_update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8; CREATE TABLE seata_order.undo_log ( id BIGINT(20) NOT NULL AUTO_INCREMENT, branch_id BIGINT(20) NOT NULL, xid VARCHAR(100) NOT NULL, context VARCHAR(128) NOT NULL, rollback_info LONGBLOB NOT NULL, log_status INT(11) NOT NULL, log_created DATETIME NOT NULL, log_modified DATETIME NOT NULL, PRIMARY KEY (id), UNIQUE KEY ux_undo_log (xid, branch_id) ) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8; # Product DROP DATABASE IF EXISTS seata_product; CREATE DATABASE seata_product; CREATE TABLE seata_product.product ( id INT(11) NOT NULL AUTO_INCREMENT, stock INT(11) DEFAULT NULL, last_update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8; INSERT INTO seata_product.product (id, stock) VALUES (1, 10); # 插入一条产品的库存 CREATE TABLE seata_product.undo_log ( id BIGINT(20) NOT NULL AUTO_INCREMENT, branch_id BIGINT(20) NOT NULL, xid VARCHAR(100) NOT NULL, context VARCHAR(128) NOT NULL, rollback_info LONGBLOB NOT NULL, log_status INT(11) NOT NULL, log_created DATETIME NOT NULL, log_modified DATETIME NOT NULL, PRIMARY KEY (id), UNIQUE KEY ux_undo_log (xid, branch_id) ) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8; # Account DROP DATABASE IF EXISTS seata_account; CREATE DATABASE seata_account; CREATE TABLE seata_account.account ( id INT(11) NOT NULL AUTO_INCREMENT, balance DOUBLE DEFAULT NULL, last_update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8; CREATE TABLE seata_account.undo_log ( id BIGINT(20) NOT NULL AUTO_INCREMENT, branch_id BIGINT(20) NOT NULL, xid VARCHAR(100) NOT NULL, context VARCHAR(128) NOT NULL, rollback_info LONGBLOB NOT NULL, log_status INT(11) NOT NULL, log_created DATETIME NOT NULL, log_modified DATETIME NOT NULL, PRIMARY KEY (id), UNIQUE KEY ux_undo_log (xid, branch_id) ) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8; INSERT INTO seata_account.account (id, balance) VALUES (1, 10); ================================================ FILE: lab-53/lab-53-seata-at-dubbo-demo/lab-53-seata-at-dubbo-demo-account-service/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-53-seata-at-dubbo-demo-account-service cn.iocoder.springboot.labs lab-53-seata-at-dubbo-demo-account-service-api 1.0-SNAPSHOT org.springframework.boot spring-boot-starter org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 5.1.48 org.mybatis.spring.boot mybatis-spring-boot-starter 2.1.2 io.seata seata-spring-boot-starter 1.1.0 org.apache.dubbo dubbo 2.7.4.1 org.apache.dubbo dubbo-spring-boot-starter 2.7.4.1 com.alibaba dubbo-registry-nacos 2.7.6 ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab53.accountservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class AccountServiceApplication { public static void main(String[] args) { SpringApplication.run(AccountServiceApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab53.accountservice.dao; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; import org.springframework.stereotype.Repository; @Mapper @Repository public interface AccountDao { /** * 获取账户余额 * * @param userId 用户 ID * @return 账户余额 */ @Select("SELECT balance FROM account WHERE id = #{userId}") Integer getBalance(@Param("userId") Long userId); /** * 扣减余额 * * @param price 需要扣减的数目 * @return 影响记录行数 */ @Update("UPDATE account SET balance = balance - #{price} WHERE id = 1 AND balance >= ${price}") int reduceBalance(@Param("price") Integer price); } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab53.accountservice.service; import cn.iocoder.springboot.lab53.accountservice.api.AccountService; import cn.iocoder.springboot.lab53.accountservice.dao.AccountDao; import io.seata.core.context.RootContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; @org.apache.dubbo.config.annotation.Service public class AccountServiceImpl implements AccountService { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private AccountDao accountDao; @Override @Transactional // 开启新事物 public void reduceBalance(Long userId, Integer price) throws Exception { logger.info("[reduceBalance] 当前 XID: {}", RootContext.getXID()); // 检查余额 checkBalance(userId, price); logger.info("[reduceBalance] 开始扣减用户 {} 余额", userId); // 扣除余额 int updateCount = accountDao.reduceBalance(price); // 扣除成功 if (updateCount == 0) { logger.warn("[reduceBalance] 扣除用户 {} 余额失败", userId); throw new Exception("余额不足"); } logger.info("[reduceBalance] 扣除用户 {} 余额成功", userId); } private void checkBalance(Long userId, Integer price) throws Exception { logger.info("[checkBalance] 检查用户 {} 余额", userId); Integer balance = accountDao.getBalance(userId); if (balance < price) { logger.warn("[checkBalance] 用户 {} 余额不足,当前余额:{}", userId, balance); throw new Exception("余额不足"); } } } ================================================ FILE: lab-53/lab-53-seata-at-dubbo-demo/lab-53-seata-at-dubbo-demo-account-service/src/main/resources/application-file.yaml ================================================ spring: application: name: account-service datasource: url: jdbc:mysql://127.0.0.1:3306/seata_account?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: # dubbo 配置项,对应 DubboConfigurationProperties 配置类 dubbo: # Dubbo 应用配置 application: name: ${spring.application.name} # 应用名 # Dubbo 注册中心配 registry: address: nacos://127.0.0.1:8848 # 注册中心地址。个鞥多注册中心,可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。 # Dubbo 服务提供者协议配置 protocol: port: -1 # 协议端口。使用 -1 表示随机端口。 name: dubbo # 使用 `dubbo://` 协议。更多协议,可见 http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html 文档 # 配置扫描 Dubbo 自定义的 @Service 注解,暴露成 Dubbo 服务提供者 scan: base-packages: cn.iocoder.springboot.lab53.accountservice.service # Seata 配置项,对应 SeataProperties 类 seata: application-id: ${spring.application.name} # Seata 应用编号,默认为 ${spring.application.name} tx-service-group: ${spring.application.name}-group # Seata 事务组编号,用于 TC 集群名 # 服务配置项,对应 ServiceProperties 类 service: # 虚拟组和分组的映射 vgroup-mapping: account-service-group: default # 分组和 Seata 服务的映射 grouplist: default: 127.0.0.1:8091 ================================================ FILE: lab-53/lab-53-seata-at-dubbo-demo/lab-53-seata-at-dubbo-demo-account-service/src/main/resources/application.yaml ================================================ spring: application: name: account-service datasource: url: jdbc:mysql://127.0.0.1:3306/seata_account?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: # dubbo 配置项,对应 DubboConfigurationProperties 配置类 dubbo: # Dubbo 应用配置 application: name: ${spring.application.name} # 应用名 # Dubbo 注册中心配 registry: address: nacos://127.0.0.1:8848 # 注册中心地址。个鞥多注册中心,可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。 # Dubbo 服务提供者协议配置 protocol: port: -1 # 协议端口。使用 -1 表示随机端口。 name: dubbo # 使用 `dubbo://` 协议。更多协议,可见 http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html 文档 # 配置扫描 Dubbo 自定义的 @Service 注解,暴露成 Dubbo 服务提供者 scan: base-packages: cn.iocoder.springboot.lab53.accountservice.service # Seata 配置项,对应 SeataProperties 类 seata: application-id: ${spring.application.name} # Seata 应用编号,默认为 ${spring.application.name} tx-service-group: ${spring.application.name}-group # Seata 事务组编号,用于 TC 集群名 # Seata 服务配置项,对应 ServiceProperties 类 service: # 虚拟组和分组的映射 vgroup-mapping: account-service-group: default # Seata 注册中心配置项,对应 RegistryProperties 类 registry: type: nacos # 注册中心类型,默认为 file nacos: cluster: default # 使用的 Seata 分组 namespace: # Nacos 命名空间 serverAddr: localhost # Nacos 服务地址 ================================================ FILE: lab-53/lab-53-seata-at-dubbo-demo/lab-53-seata-at-dubbo-demo-account-service-api/pom.xml ================================================ lab-53-seata-at-dubbo-demo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-53-seata-at-dubbo-demo-account-service-api ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab53.accountservice.api; /** * 账户 Service */ public interface AccountService { /** * 扣除余额 * * @param userId 用户编号 * @param price 扣减金额 * @throws Exception 失败时抛出异常 */ void reduceBalance(Long userId, Integer price) throws Exception; } ================================================ FILE: lab-53/lab-53-seata-at-dubbo-demo/lab-53-seata-at-dubbo-demo-order-service/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-53-seata-at-dubbo-demo-order-service cn.iocoder.springboot.labs lab-53-seata-at-dubbo-demo-order-service-api 1.0-SNAPSHOT cn.iocoder.springboot.labs lab-53-seata-at-dubbo-demo-account-service-api 1.0-SNAPSHOT cn.iocoder.springboot.labs lab-53-seata-at-dubbo-demo-product-service-api 1.0-SNAPSHOT org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 5.1.48 org.mybatis.spring.boot mybatis-spring-boot-starter 2.1.2 io.seata seata-spring-boot-starter 1.1.0 org.apache.dubbo dubbo 2.7.4.1 org.apache.dubbo dubbo-spring-boot-starter 2.7.4.1 com.alibaba dubbo-registry-nacos 2.7.6 ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab53.orderservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class OrderServiceApplication { public static void main(String[] args) { SpringApplication.run(OrderServiceApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab53.orderservice.controller; import cn.iocoder.springboot.lab53.orderservice.api.OrderService; import org.apache.dubbo.config.annotation.Reference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/order") public class OrderController { private Logger logger = LoggerFactory.getLogger(OrderController.class); @Reference private OrderService orderService; @PostMapping("/create") public Integer createOrder(@RequestParam("userId") Long userId, @RequestParam("productId") Long productId, @RequestParam("price") Integer price) throws Exception { logger.info("[createOrder] 收到下单请求,用户:{}, 商品:{}, 价格:{}", userId, productId, price); return orderService.createOrder(userId, productId, price); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab53.orderservice.dao; import cn.iocoder.springboot.lab53.orderservice.entity.OrderDO; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Options; import org.springframework.stereotype.Repository; @Mapper @Repository public interface OrderDao { /** * 插入订单记录 * * @param order 订单 * @return 影响记录数量 */ @Insert("INSERT INTO orders (user_id, product_id, pay_amount) VALUES (#{userId}, #{productId}, #{payAmount})") @Options(useGeneratedKeys = true, keyColumn = "id", keyProperty = "id") int saveOrder(OrderDO order); } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab53.orderservice.entity; /** * 订单实体 */ public class OrderDO { /** 订单编号 **/ private Integer id; /** 用户编号 **/ private Long userId; /** 产品编号 **/ private Long productId; /** 支付金额 **/ private Integer payAmount; public Integer getId() { return id; } public OrderDO setId(Integer id) { this.id = id; return this; } public Long getUserId() { return userId; } public OrderDO setUserId(Long userId) { this.userId = userId; return this; } public Long getProductId() { return productId; } public OrderDO setProductId(Long productId) { this.productId = productId; return this; } public Integer getPayAmount() { return payAmount; } public OrderDO setPayAmount(Integer payAmount) { this.payAmount = payAmount; return this; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab53.orderservice.service; import cn.iocoder.springboot.lab53.accountservice.api.AccountService; import cn.iocoder.springboot.lab53.orderservice.api.OrderService; import cn.iocoder.springboot.lab53.orderservice.dao.OrderDao; import cn.iocoder.springboot.lab53.orderservice.entity.OrderDO; import cn.iocoder.springboot.lab53.productservice.api.ProductService; import io.seata.core.context.RootContext; import io.seata.spring.annotation.GlobalTransactional; import org.apache.dubbo.config.annotation.Reference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @org.apache.dubbo.config.annotation.Service public class OrderServiceImpl implements OrderService { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private OrderDao orderDao; @Reference private AccountService accountService; @Reference private ProductService productService; @Override @GlobalTransactional public Integer createOrder(Long userId, Long productId, Integer price) throws Exception { Integer amount = 1; // 购买数量,暂时设置为 1。 logger.info("[createOrder] 当前 XID: {}", RootContext.getXID()); // 扣减库存 productService.reduceStock(productId, amount); // 扣减余额 accountService.reduceBalance(userId, price); // 保存订单 OrderDO order = new OrderDO().setUserId(userId).setProductId(productId).setPayAmount(amount * price); orderDao.saveOrder(order); logger.info("[createOrder] 保存订单: {}", order.getId()); // 返回订单编号 return order.getId(); } } ================================================ FILE: lab-53/lab-53-seata-at-dubbo-demo/lab-53-seata-at-dubbo-demo-order-service/src/main/resources/application-file.yaml ================================================ server: port: 8081 # 端口 spring: application: name: order-service datasource: url: jdbc:mysql://127.0.0.1:3306/seata_order?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: # dubbo 配置项,对应 DubboConfigurationProperties 配置类 dubbo: # Dubbo 应用配置 application: name: ${spring.application.name} # 应用名 # Dubbo 注册中心配 registry: address: nacos://127.0.0.1:8848 # 注册中心地址。个鞥多注册中心,可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。 # Dubbo 服务提供者协议配置 protocol: port: -1 # 协议端口。使用 -1 表示随机端口。 name: dubbo # 使用 `dubbo://` 协议。更多协议,可见 http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html 文档 # 配置扫描 Dubbo 自定义的 @Service 注解,暴露成 Dubbo 服务提供者 scan: base-packages: cn.iocoder.springboot.lab53.orderservice.service # Seata 配置项,对应 SeataProperties 类 seata: application-id: ${spring.application.name} # Seata 应用编号,默认为 ${spring.application.name} tx-service-group: ${spring.application.name}-group # Seata 事务组编号,用于 TC 集群名 # 服务配置项,对应 ServiceProperties 类 service: # 虚拟组和分组的映射 vgroup-mapping: order-service-group: default # 分组和 Seata 服务的映射 grouplist: default: 127.0.0.1:8091 ================================================ FILE: lab-53/lab-53-seata-at-dubbo-demo/lab-53-seata-at-dubbo-demo-order-service/src/main/resources/application.yaml ================================================ server: port: 8081 # 端口 spring: application: name: order-service datasource: url: jdbc:mysql://127.0.0.1:3306/seata_order?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: # dubbo 配置项,对应 DubboConfigurationProperties 配置类 dubbo: # Dubbo 应用配置 application: name: ${spring.application.name} # 应用名 # Dubbo 注册中心配 registry: address: nacos://127.0.0.1:8848 # 注册中心地址。个鞥多注册中心,可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。 # Dubbo 服务提供者协议配置 protocol: port: -1 # 协议端口。使用 -1 表示随机端口。 name: dubbo # 使用 `dubbo://` 协议。更多协议,可见 http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html 文档 # 配置扫描 Dubbo 自定义的 @Service 注解,暴露成 Dubbo 服务提供者 scan: base-packages: cn.iocoder.springboot.lab53.orderservice.service # Seata 配置项,对应 SeataProperties 类 seata: application-id: ${spring.application.name} # Seata 应用编号,默认为 ${spring.application.name} tx-service-group: ${spring.application.name}-group # Seata 事务组编号,用于 TC 集群名 # Seata 服务配置项,对应 ServiceProperties 类 service: # 虚拟组和分组的映射 vgroup-mapping: order-service-group: default # Seata 注册中心配置项,对应 RegistryProperties 类 registry: type: nacos # 注册中心类型,默认为 file nacos: cluster: default # 使用的 Seata 分组 namespace: # Nacos 命名空间 serverAddr: localhost # Nacos 服务地址 ================================================ FILE: lab-53/lab-53-seata-at-dubbo-demo/lab-53-seata-at-dubbo-demo-order-service-api/pom.xml ================================================ lab-53-seata-at-dubbo-demo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-53-seata-at-dubbo-demo-order-service-api ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab53.orderservice.api; /** * 订单 Service */ public interface OrderService { /** * 创建订单 * * @param userId 用户编号 * @param productId 产品编号 * @param price 价格 * @return 订单编号 * @throws Exception 创建订单失败,抛出异常 */ Integer createOrder(Long userId, Long productId, Integer price) throws Exception; } ================================================ FILE: lab-53/lab-53-seata-at-dubbo-demo/lab-53-seata-at-dubbo-demo-product-service/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-53-seata-at-dubbo-demo-product-service cn.iocoder.springboot.labs lab-53-seata-at-dubbo-demo-product-service-api 1.0-SNAPSHOT org.springframework.boot spring-boot-starter org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 5.1.48 org.mybatis.spring.boot mybatis-spring-boot-starter 2.1.2 io.seata seata-spring-boot-starter 1.1.0 org.apache.dubbo dubbo 2.7.4.1 org.apache.dubbo dubbo-spring-boot-starter 2.7.4.1 com.alibaba dubbo-registry-nacos 2.7.6 ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab53.productservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ProductServiceApplication { public static void main(String[] args) { SpringApplication.run(ProductServiceApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab53.productservice.dao; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; import org.springframework.stereotype.Repository; @Mapper @Repository public interface ProductDao { /** * 获取库存 * * @param productId 商品编号 * @return 库存 */ @Select("SELECT stock FROM product WHERE id = #{productId}") Integer getStock(@Param("productId") Long productId); /** * 扣减库存 * * @param productId 商品编号 * @param amount 扣减数量 * @return 影响记录行数 */ @Update("UPDATE product SET stock = stock - #{amount} WHERE id = #{productId} AND stock >= #{amount}") int reduceStock(@Param("productId") Long productId, @Param("amount") Integer amount); } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab53.productservice.service; import cn.iocoder.springboot.lab53.productservice.dao.ProductDao; import cn.iocoder.springboot.lab53.productservice.api.ProductService; import io.seata.core.context.RootContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; @org.apache.dubbo.config.annotation.Service public class ProductServiceImpl implements ProductService { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private ProductDao productDao; @Override @Transactional // 开启新事物 public void reduceStock(Long productId, Integer amount) throws Exception { logger.info("[reduceStock] 当前 XID: {}", RootContext.getXID()); // 检查库存 checkStock(productId, amount); logger.info("[reduceStock] 开始扣减 {} 库存", productId); // 扣减库存 int updateCount = productDao.reduceStock(productId, amount); // 扣除成功 if (updateCount == 0) { logger.warn("[reduceStock] 扣除 {} 库存失败", productId); throw new Exception("库存不足"); } // 扣除失败 logger.info("[reduceStock] 扣除 {} 库存成功", productId); } private void checkStock(Long productId, Integer requiredAmount) throws Exception { logger.info("[checkStock] 检查 {} 库存", productId); Integer stock = productDao.getStock(productId); if (stock < requiredAmount) { logger.warn("[checkStock] {} 库存不足,当前库存: {}", productId, stock); throw new Exception("库存不足"); } } } ================================================ FILE: lab-53/lab-53-seata-at-dubbo-demo/lab-53-seata-at-dubbo-demo-product-service/src/main/resources/application-file.yaml ================================================ spring: application: name: product-service datasource: url: jdbc:mysql://127.0.0.1:3306/seata_product?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: # dubbo 配置项,对应 DubboConfigurationProperties 配置类 dubbo: # Dubbo 应用配置 application: name: ${spring.application.name} # 应用名 # Dubbo 注册中心配 registry: address: nacos://127.0.0.1:8848 # 注册中心地址。个鞥多注册中心,可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。 # Dubbo 服务提供者协议配置 protocol: port: -1 # 协议端口。使用 -1 表示随机端口。 name: dubbo # 使用 `dubbo://` 协议。更多协议,可见 http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html 文档 # 配置扫描 Dubbo 自定义的 @Service 注解,暴露成 Dubbo 服务提供者 scan: base-packages: cn.iocoder.springboot.lab53.productservice.service # Seata 配置项,对应 SeataProperties 类 seata: application-id: ${spring.application.name} # Seata 应用编号,默认为 ${spring.application.name} tx-service-group: ${spring.application.name}-group # Seata 事务组编号,用于 TC 集群名 # 服务配置项,对应 ServiceProperties 类 service: # 虚拟组和分组的映射 vgroup-mapping: product-service-group: default # 分组和 Seata 服务的映射 grouplist: default: 127.0.0.1:8091 ================================================ FILE: lab-53/lab-53-seata-at-dubbo-demo/lab-53-seata-at-dubbo-demo-product-service/src/main/resources/application.yaml ================================================ spring: application: name: product-service datasource: url: jdbc:mysql://127.0.0.1:3306/seata_product?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: # dubbo 配置项,对应 DubboConfigurationProperties 配置类 dubbo: # Dubbo 应用配置 application: name: ${spring.application.name} # 应用名 # Dubbo 注册中心配 registry: address: nacos://127.0.0.1:8848 # 注册中心地址。个鞥多注册中心,可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。 # Dubbo 服务提供者协议配置 protocol: port: -1 # 协议端口。使用 -1 表示随机端口。 name: dubbo # 使用 `dubbo://` 协议。更多协议,可见 http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html 文档 # 配置扫描 Dubbo 自定义的 @Service 注解,暴露成 Dubbo 服务提供者 scan: base-packages: cn.iocoder.springboot.lab53.productservice.service # Seata 配置项,对应 SeataProperties 类 seata: application-id: ${spring.application.name} # Seata 应用编号,默认为 ${spring.application.name} tx-service-group: ${spring.application.name}-group # Seata 事务组编号,用于 TC 集群名 # Seata 服务配置项,对应 ServiceProperties 类 service: # 虚拟组和分组的映射 vgroup-mapping: product-service-group: default # Seata 注册中心配置项,对应 RegistryProperties 类 registry: type: nacos # 注册中心类型,默认为 file nacos: cluster: default # 使用的 Seata 分组 namespace: # Nacos 命名空间 serverAddr: localhost # Nacos 服务地址 ================================================ FILE: lab-53/lab-53-seata-at-dubbo-demo/lab-53-seata-at-dubbo-demo-product-service-api/pom.xml ================================================ lab-53-seata-at-dubbo-demo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-53-seata-at-dubbo-demo-product-service-api ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab53.productservice.api; /** * 商品 Service */ public interface ProductService { /** * 扣减库存 * * @param productId 商品 ID * @param amount 扣减数量 * @throws Exception 扣减失败时抛出异常 */ void reduceStock(Long productId, Integer amount) throws Exception; } ================================================ FILE: lab-53/lab-53-seata-at-dubbo-demo/pom.xml ================================================ lab-53 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-53-seata-at-dubbo-demo pom lab-53-seata-at-dubbo-demo-order-service-api lab-53-seata-at-dubbo-demo-order-service lab-53-seata-at-dubbo-demo-account-service-api lab-53-seata-at-dubbo-demo-account-service lab-53-seata-at-dubbo-demo-product-service-api lab-53-seata-at-dubbo-demo-product-service ================================================ FILE: lab-53/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-53 pom lab-53-seata-at-dubbo-demo ================================================ FILE: lab-53/《Dubbo 分布式事务 Seata 入门》.md ================================================ ================================================ FILE: lab-54/lab-54-demo/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-54-demo org.springframework.boot spring-boot-starter-web ================================================ FILE: lab-54/lab-54-demo/src/main/java/cn/iocoder/springboot/lab54/eventdemo/DemoApplication.java ================================================ package cn.iocoder.springboot.lab54.eventdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableAsync; @SpringBootApplication @EnableAsync // 开启 Spring 异步的功能 public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } ================================================ FILE: lab-54/lab-54-demo/src/main/java/cn/iocoder/springboot/lab54/eventdemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab54.eventdemo.controller; import cn.iocoder.springboot.lab54.eventdemo.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @Autowired private UserService userService; @GetMapping("/register") public String register(String username) { userService.register(username); return "success"; } } ================================================ FILE: lab-54/lab-54-demo/src/main/java/cn/iocoder/springboot/lab54/eventdemo/event/UserRegisterEvent.java ================================================ package cn.iocoder.springboot.lab54.eventdemo.event; import org.springframework.context.ApplicationEvent; /** * 用户注册事件 */ public class UserRegisterEvent extends ApplicationEvent { /** * 用户名 */ private String username; public UserRegisterEvent(Object source) { super(source); } public UserRegisterEvent(Object source, String username) { super(source); this.username = username; } public String getUsername() { return username; } } ================================================ FILE: lab-54/lab-54-demo/src/main/java/cn/iocoder/springboot/lab54/eventdemo/service/CouponService.java ================================================ package cn.iocoder.springboot.lab54.eventdemo.service; import cn.iocoder.springboot.lab54.eventdemo.event.UserRegisterEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Service; @Service public class CouponService { private Logger logger = LoggerFactory.getLogger(getClass()); @EventListener public void addCoupon(UserRegisterEvent event) { logger.info("[addCoupon][给用户({}) 发放优惠劵]", event.getUsername()); } } ================================================ FILE: lab-54/lab-54-demo/src/main/java/cn/iocoder/springboot/lab54/eventdemo/service/EmailService.java ================================================ package cn.iocoder.springboot.lab54.eventdemo.service; import cn.iocoder.springboot.lab54.eventdemo.event.UserRegisterEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationListener; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; @Service public class EmailService implements ApplicationListener { private Logger logger = LoggerFactory.getLogger(getClass()); @Override @Async public void onApplicationEvent(UserRegisterEvent event) { logger.info("[onApplicationEvent][给用户({}) 发送邮件]", event.getUsername()); } } ================================================ FILE: lab-54/lab-54-demo/src/main/java/cn/iocoder/springboot/lab54/eventdemo/service/UserService.java ================================================ package cn.iocoder.springboot.lab54.eventdemo.service; import cn.iocoder.springboot.lab54.eventdemo.event.UserRegisterEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.stereotype.Service; @Service public class UserService implements ApplicationEventPublisherAware { private Logger logger = LoggerFactory.getLogger(getClass()); private ApplicationEventPublisher applicationEventPublisher; @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.applicationEventPublisher = applicationEventPublisher; } public void register(String username) { // ... 执行注册逻辑 logger.info("[register][执行用户({}) 的注册逻辑]", username); // ... 发布 applicationEventPublisher.publishEvent(new UserRegisterEvent(this, username)); } } ================================================ FILE: lab-54/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-54 pom lab-54-demo ================================================ FILE: lab-54/《芋道 Spring Boot 事件机制 Event 入门》.md ================================================ ================================================ FILE: lab-55/lab-55-mapstruct-demo/pom.xml ================================================ lab-55 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-55-mapstruct-demo 1.8 1.3.1.Final org.mapstruct mapstruct ${mapstruct.version} org.apache.maven.plugins maven-compiler-plugin 3.8.1 ${java.version} ${java.version} org.mapstruct mapstruct-processor ${mapstruct.version} ================================================ FILE: lab-55/lab-55-mapstruct-demo/src/main/java/cn/iocoder/springboot/lab55/mapstructdemo/UserBOTest.java ================================================ package cn.iocoder.springboot.lab55.mapstructdemo; import cn.iocoder.springboot.lab55.mapstructdemo.bo.UserBO; import cn.iocoder.springboot.lab55.mapstructdemo.convert.UserConvert; import cn.iocoder.springboot.lab55.mapstructdemo.dataobject.UserDO; public class UserBOTest { public static void main(String[] args) { // 创建 UserDO 对象 UserDO userDO = new UserDO() .setId(1).setUsername("yudaoyuanma").setPassword("buzhidao"); // 进行转换 UserBO userBO = UserConvert.INSTANCE.convert(userDO); System.out.println(userBO.getId()); System.out.println(userBO.getUsername()); System.out.println(userBO.getPassword()); } } ================================================ FILE: lab-55/lab-55-mapstruct-demo/src/main/java/cn/iocoder/springboot/lab55/mapstructdemo/UserDetailBOTest.java ================================================ package cn.iocoder.springboot.lab55.mapstructdemo; import cn.iocoder.springboot.lab55.mapstructdemo.bo.UserDetailBO; import cn.iocoder.springboot.lab55.mapstructdemo.convert.UserConvert; import cn.iocoder.springboot.lab55.mapstructdemo.dataobject.UserDO; public class UserDetailBOTest { public static void main(String[] args) { // 创建 UserDO 对象 UserDO userDO = new UserDO() .setId(1).setUsername("yudaoyuanma").setPassword("buzhidao"); // 进行转换 UserDetailBO userDetailBO = UserConvert.INSTANCE.convertDetail(userDO); System.out.println(userDetailBO.getUserId()); } } ================================================ FILE: lab-55/lab-55-mapstruct-demo/src/main/java/cn/iocoder/springboot/lab55/mapstructdemo/bo/UserBO.java ================================================ package cn.iocoder.springboot.lab55.mapstructdemo.bo; public class UserBO { /** 用户编号 **/ private Integer id; /** 用户名 **/ private String username; /** 密码 **/ private String password; public Integer getId() { return id; } public UserBO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserBO setUsername(String username) { this.username = username; return this; } public String getPassword() { return password; } public UserBO setPassword(String password) { this.password = password; return this; } } ================================================ FILE: lab-55/lab-55-mapstruct-demo/src/main/java/cn/iocoder/springboot/lab55/mapstructdemo/bo/UserDetailBO.java ================================================ package cn.iocoder.springboot.lab55.mapstructdemo.bo; public class UserDetailBO { private Integer userId; public Integer getUserId() { return userId; } public UserDetailBO setUserId(Integer userId) { this.userId = userId; return this; } } ================================================ FILE: lab-55/lab-55-mapstruct-demo/src/main/java/cn/iocoder/springboot/lab55/mapstructdemo/convert/UserConvert.java ================================================ package cn.iocoder.springboot.lab55.mapstructdemo.convert; import cn.iocoder.springboot.lab55.mapstructdemo.bo.UserBO; import cn.iocoder.springboot.lab55.mapstructdemo.bo.UserDetailBO; import cn.iocoder.springboot.lab55.mapstructdemo.dataobject.UserDO; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.Mappings; import org.mapstruct.factory.Mappers; @Mapper public interface UserConvert { UserConvert INSTANCE = Mappers.getMapper(UserConvert.class); UserBO convert(UserDO userDO); @Mappings({ @Mapping(source = "id", target = "userId") }) UserDetailBO convertDetail(UserDO userDO); } ================================================ FILE: lab-55/lab-55-mapstruct-demo/src/main/java/cn/iocoder/springboot/lab55/mapstructdemo/dataobject/UserDO.java ================================================ package cn.iocoder.springboot.lab55.mapstructdemo.dataobject; /** * 用户 DO */ public class UserDO { /** 用户编号 **/ private Integer id; /** 用户名 **/ private String username; /** 密码 **/ private String password; public Integer getId() { return id; } public UserDO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserDO setUsername(String username) { this.username = username; return this; } public String getPassword() { return password; } public UserDO setPassword(String password) { this.password = password; return this; } } ================================================ FILE: lab-55/lab-55-mapstruct-demo-lombok/pom.xml ================================================ lab-55 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-55-mapstruct-demo-lombok 1.8 1.3.1.Final 1.18.12 org.mapstruct mapstruct ${mapstruct.version} org.projectlombok lombok ${lombok.version} provided org.apache.maven.plugins maven-compiler-plugin 3.8.1 ${java.version} ${java.version} org.mapstruct mapstruct-processor ${mapstruct.version} org.projectlombok lombok ${lombok.version} ================================================ FILE: lab-55/lab-55-mapstruct-demo-lombok/src/main/java/cn/iocoder/springboot/lab55/mapstructdemo/UserBOTest.java ================================================ package cn.iocoder.springboot.lab55.mapstructdemo; import cn.iocoder.springboot.lab55.mapstructdemo.bo.UserBO; import cn.iocoder.springboot.lab55.mapstructdemo.convert.UserConvert; import cn.iocoder.springboot.lab55.mapstructdemo.dataobject.UserDO; public class UserBOTest { public static void main(String[] args) { // 创建 UserDO 对象 UserDO userDO = new UserDO() .setId(1).setUsername("yudaoyuanma").setPassword("buzhidao"); // 进行转换 UserBO userBO = UserConvert.INSTANCE.convert(userDO); System.out.println(userBO.getId()); System.out.println(userBO.getUsername()); System.out.println(userBO.getPassword()); } } ================================================ FILE: lab-55/lab-55-mapstruct-demo-lombok/src/main/java/cn/iocoder/springboot/lab55/mapstructdemo/bo/UserBO.java ================================================ package cn.iocoder.springboot.lab55.mapstructdemo.bo; import lombok.Data; import lombok.experimental.Accessors; @Data @Accessors(chain = true) public class UserBO { /** 用户编号 **/ private Integer id; /** 用户名 **/ private String username; /** 密码 **/ private String password; } ================================================ FILE: lab-55/lab-55-mapstruct-demo-lombok/src/main/java/cn/iocoder/springboot/lab55/mapstructdemo/convert/UserConvert.java ================================================ package cn.iocoder.springboot.lab55.mapstructdemo.convert; import cn.iocoder.springboot.lab55.mapstructdemo.bo.UserBO; import cn.iocoder.springboot.lab55.mapstructdemo.dataobject.UserDO; import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; @Mapper public interface UserConvert { UserConvert INSTANCE = Mappers.getMapper(UserConvert.class); UserBO convert(UserDO userDO); } ================================================ FILE: lab-55/lab-55-mapstruct-demo-lombok/src/main/java/cn/iocoder/springboot/lab55/mapstructdemo/dataobject/UserDO.java ================================================ package cn.iocoder.springboot.lab55.mapstructdemo.dataobject; import lombok.Data; import lombok.experimental.Accessors; @Data @Accessors(chain = true) public class UserDO { /** 用户编号 **/ private Integer id; /** 用户名 **/ private String username; /** 密码 **/ private String password; public Integer getId() { return id; } public UserDO setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public UserDO setUsername(String username) { this.username = username; return this; } public String getPassword() { return password; } public UserDO setPassword(String password) { this.password = password; return this; } } ================================================ FILE: lab-55/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-55 pom lab-55-mapstruct-demo lab-55-mapstruct-demo-lombok ================================================ FILE: lab-55/《芋道 Spring Boot 对象转换 MapStruct 入门》.md ================================================ ================================================ FILE: lab-56/lab-56-demo01/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE 4.0.0 lab-56-demo01 org.springframework.boot spring-boot-starter-web ================================================ FILE: lab-56/lab-56-demo01/src/main/java/cn/iocoder/springboot/lab56/Demo01Application.java ================================================ package cn.iocoder.springboot.lab56; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Demo01Application { public static void main(String[] args) { System.setProperty("server.port", "18080"); // 端口 18080 SpringApplication.run(Demo01Application.class, args); } } ================================================ FILE: lab-56/lab-56-demo01/src/main/java/cn/iocoder/springboot/lab56/Demo02Application.java ================================================ package cn.iocoder.springboot.lab56; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Demo02Application { public static void main(String[] args) { System.setProperty("server.port", "28080"); // 端口 28080 SpringApplication.run(Demo02Application.class, args); } } ================================================ FILE: lab-56/lab-56-demo01/src/main/java/cn/iocoder/springboot/lab56/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab56.controller; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @Value("${server.port}") private Integer serverPort; @GetMapping("/echo") public String echo() { return "echo:" + serverPort; } } ================================================ FILE: lab-56/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-56 pom lab-56-demo01 ================================================ FILE: lab-56/《芋道 APISIX 极简入门(国产微服务网关)》.md ================================================ ================================================ FILE: lab-56/《芋道 Kong 极简入门(微服务网关)》.md ================================================ ================================================ FILE: lab-57/lab-57-hystrix-demo01/pom.xml ================================================ lab-57 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-57-hystrix-demo01 1.8 1.8 2.2.4.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.boot spring-boot-starter-web com.netflix.hystrix hystrix-core 1.5.18 com.netflix.hystrix hystrix-javanica 1.5.18 ================================================ FILE: lab-57/lab-57-hystrix-demo01/src/main/java/cn/iocoder/springboot/lab57/hystrixdemo/DemoApplication.java ================================================ package cn.iocoder.springboot.lab57.hystrixdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @SpringBootApplication public class DemoApplication { @Bean public RestTemplate restTemplate() { return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } ================================================ FILE: lab-57/lab-57-hystrix-demo01/src/main/java/cn/iocoder/springboot/lab57/hystrixdemo/config/HystrixConfig.java ================================================ package cn.iocoder.springboot.lab57.hystrixdemo.config; import com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; @Configuration @EnableAspectJAutoProxy // 开启 AOP 代理的支持 public class HystrixConfig { @Bean public HystrixCommandAspect hystrixCommandAspect() { return new HystrixCommandAspect(); } } ================================================ FILE: lab-57/lab-57-hystrix-demo01/src/main/java/cn/iocoder/springboot/lab57/hystrixdemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab57.hystrixdemo.controller; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import org.apache.commons.lang.exception.ExceptionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @RestController @RequestMapping("/demo") public class DemoController { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private RestTemplate restTemplate; @GetMapping("/get_user") @HystrixCommand(fallbackMethod = "getUserFallback") public String getUser(@RequestParam("id") Integer id) { logger.info("[getUser][准备调用 user-service 获取用户({})详情]", id); return restTemplate.getForEntity("http://127.0.0.1:18080/user/get?id=" + id, String.class).getBody(); } public String getUserFallback(Integer id, Throwable throwable) { logger.info("[getUserFallback][id({}) exception({})]", id, ExceptionUtils.getRootCauseMessage(throwable)); return "mock:User:" + id; } } ================================================ FILE: lab-57/lab-57-hystrix-dubbo-demo/lab-57-hystrix-dubbo-demo-application/pom.xml ================================================ lab-57-hystrix-dubbo-demo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-57-hystrix-dubbo-demo-application 1.8 1.8 2.2.4.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import cn.iocoder.springboot.labs lab-57-hystrix-dubbo-demo-user-service-api 1.0-SNAPSHOT org.springframework.boot spring-boot-starter-web org.apache.dubbo dubbo 2.7.4.1 org.apache.dubbo dubbo-spring-boot-starter 2.7.4.1 com.alibaba.nacos nacos-client 1.2.1 org.apache.dubbo dubbo-registry-nacos 2.7.4.1 com.netflix.hystrix hystrix-core 1.5.18 com.netflix.hystrix hystrix-javanica 1.5.18 ================================================ FILE: lab-57/lab-57-hystrix-dubbo-demo/lab-57-hystrix-dubbo-demo-application/src/main/java/cn/iocoder/springcloud/labx23/demo/DemoApplication.java ================================================ package cn.iocoder.springcloud.labx23.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } ================================================ FILE: lab-57/lab-57-hystrix-dubbo-demo/lab-57-hystrix-dubbo-demo-application/src/main/java/cn/iocoder/springcloud/labx23/demo/config/HystrixConfig.java ================================================ package cn.iocoder.springcloud.labx23.demo.config; import com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; @Configuration @EnableAspectJAutoProxy // 开启 AOP 代理的支持 public class HystrixConfig { @Bean public HystrixCommandAspect hystrixCommandAspect() { return new HystrixCommandAspect(); } } ================================================ FILE: lab-57/lab-57-hystrix-dubbo-demo/lab-57-hystrix-dubbo-demo-application/src/main/java/cn/iocoder/springcloud/labx23/demo/controller/DemoController.java ================================================ package cn.iocoder.springcloud.labx23.demo.controller; import cn.iocoder.springboot.lab57.userservice.api.UserService; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.dubbo.config.annotation.Reference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { private Logger logger = LoggerFactory.getLogger(getClass()); @Reference(protocol = "dubbo", version = "1.0.0") private UserService userService; @GetMapping("/get_user") @HystrixCommand(fallbackMethod = "getUserFallback") public String getUser(@RequestParam("id") Integer id) { logger.info("[getUser][准备调用 user-service 获取用户({})详情]", id); return userService.getUser(id); } public String getUserFallback(Integer id, Throwable throwable) { logger.info("[getUserFallback][id({}) exception({})]", id, ExceptionUtils.getRootCauseMessage(throwable)); return "mock:User:" + id; } } ================================================ FILE: lab-57/lab-57-hystrix-dubbo-demo/lab-57-hystrix-dubbo-demo-application/src/main/resources/application.yaml ================================================ # dubbo 配置项,对应 DubboConfigurationProperties 配置类 dubbo: # Dubbo 应用配置 application: name: demo-consumer # 应用名 # Dubbo 注册中心配置 registry: address: nacos://127.0.0.1:8848 # 注册中心地址。个鞥多注册中心,可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。 # Dubbo 消费者配置 consumer: timeout: 1000 # 【重要】远程服务调用超时时间,单位:毫秒。默认为 1000 毫秒,胖友可以根据自己业务修改 UserRpcService: version: 1.0.0 ================================================ FILE: lab-57/lab-57-hystrix-dubbo-demo/lab-57-hystrix-dubbo-demo-user-service/pom.xml ================================================ lab-57-hystrix-dubbo-demo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-57-hystrix-dubbo-demo-user-service 1.8 1.8 2.2.4.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import cn.iocoder.springboot.labs lab-57-hystrix-dubbo-demo-user-service-api 1.0-SNAPSHOT org.springframework.boot spring-boot-starter org.apache.dubbo dubbo 2.7.4.1 org.apache.dubbo dubbo-spring-boot-starter 2.7.4.1 com.alibaba.nacos nacos-client 1.2.1 org.apache.dubbo dubbo-registry-nacos 2.7.4.1 ================================================ FILE: lab-57/lab-57-hystrix-dubbo-demo/lab-57-hystrix-dubbo-demo-user-service/src/main/java/cn/iocoder/springboot/lab57/userservice/UserServiceApplication.java ================================================ package cn.iocoder.springboot.lab57.userservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class UserServiceApplication { public static void main(String[] args) { SpringApplication.run(UserServiceApplication.class); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab57.userservice.service; import cn.iocoder.springboot.lab57.userservice.api.UserService; @org.apache.dubbo.config.annotation.Service(version = "1.0.0") public class UserServiceImpl implements UserService { @Override public String getUser(Integer id) { return "User:" + id; } } ================================================ FILE: lab-57/lab-57-hystrix-dubbo-demo/lab-57-hystrix-dubbo-demo-user-service/src/main/resources/application.yaml ================================================ # dubbo 配置项,对应 DubboConfigurationProperties 配置类 dubbo: # Dubbo 应用配置 application: name: user-service # 应用名 # Dubbo 注册中心配 registry: address: nacos://127.0.0.1:8848 # 注册中心地址。个鞥多注册中心,可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。 # Dubbo 服务提供者协议配置 protocol: port: -1 # 协议端口。使用 -1 表示随机端口。 name: dubbo # 使用 `dubbo://` 协议。更多协议,可见 http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html 文档 # Dubbo 服务提供者配置 provider: timeout: 1000 # 【重要】远程服务调用超时时间,单位:毫秒。默认为 1000 毫秒,胖友可以根据自己业务修改 UserRpcService: version: 1.0.0 # 配置扫描 Dubbo 自定义的 @Service 注解,暴露成 Dubbo 服务提供者 scan: base-packages: cn.iocoder.springboot.lab57.userservice.service ================================================ FILE: lab-57/lab-57-hystrix-dubbo-demo/lab-57-hystrix-dubbo-demo-user-service-api/pom.xml ================================================ lab-57-hystrix-dubbo-demo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-57-hystrix-dubbo-demo-user-service-api ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab57.userservice.api; public interface UserService { String getUser(Integer id); } ================================================ FILE: lab-57/lab-57-hystrix-dubbo-demo/pom.xml ================================================ lab-57 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-57-hystrix-dubbo-demo pom lab-57-hystrix-dubbo-demo-user-service lab-57-hystrix-dubbo-demo-user-service-api lab-57-hystrix-dubbo-demo-application ================================================ FILE: lab-57/lab-57-user-service/pom.xml ================================================ lab-57 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-57-user-service 1.8 1.8 2.2.4.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.boot spring-boot-starter-web ================================================ FILE: lab-57/lab-57-user-service/src/main/java/cn/iocoder/springboot/lab57/userservice/UserServiceApplication.java ================================================ package cn.iocoder.springboot.lab57.userservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.List; import java.util.stream.Collectors; @SpringBootApplication public class UserServiceApplication { @RestController @RequestMapping("/user") public class UserController { @GetMapping("/get") public String get(@RequestParam("id") Integer id) { return "User:" + id; } @GetMapping("/batch_get") public List batchGet(@RequestParam("ids") List ids) { return ids.stream().map(id -> "User:" + id).collect(Collectors.toList()); } } public static void main(String[] args) { // 设置端口 System.setProperty("server.port", "18080"); // 应用启动 SpringApplication.run(UserServiceApplication.class, args); } } ================================================ FILE: lab-57/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-57 pom lab-57-user-service lab-57-hystrix-demo01 lab-57-hystrix-dubbo-demo ================================================ FILE: lab-57/《芋道 Spring Boot 服务容错 Hystrix 入门》.md ================================================ ================================================ FILE: lab-58/lab-58-feign-demo/pom.xml ================================================ lab-58 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-58-feign-demo 1.8 1.8 2.2.4.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.boot spring-boot-starter-web io.github.openfeign feign-core 11.0 io.github.openfeign feign-gson 11.0 io.github.openfeign feign-spring4 11.0 ================================================ FILE: lab-58/lab-58-feign-demo/src/main/java/cn/iocoder/springboot/lab58/feigndemo/FeignDemoApplication.java ================================================ package cn.iocoder.springboot.lab58.feigndemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class FeignDemoApplication { public static void main(String[] args) { SpringApplication.run(FeignDemoApplication.class, args); } } ================================================ FILE: lab-58/lab-58-feign-demo/src/main/java/cn/iocoder/springboot/lab58/feigndemo/config/FeignConfig.java ================================================ package cn.iocoder.springboot.lab58.feigndemo.config; import cn.iocoder.springboot.lab58.feigndemo.feign.UserServiceFeignClient; import cn.iocoder.springboot.lab58.feigndemo.feign.UserServiceFeignClient02; import feign.Feign; import feign.gson.GsonDecoder; import feign.gson.GsonEncoder; import feign.spring.SpringContract; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class FeignConfig { @Bean public UserServiceFeignClient userServiceFeignClient() { return Feign.builder() .encoder(new GsonEncoder()) .decoder(new GsonDecoder()) .target(UserServiceFeignClient.class, "http://127.0.0.1:18080"); // 目标地址 } @Bean public UserServiceFeignClient02 userServiceFeignClient02() { return Feign.builder() .encoder(new GsonEncoder()) .decoder(new GsonDecoder()) .contract(new SpringContract()) .target(UserServiceFeignClient02.class, "http://127.0.0.1:18080"); // 目标地址 } } ================================================ FILE: lab-58/lab-58-feign-demo/src/main/java/cn/iocoder/springboot/lab58/feigndemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab58.feigndemo.controller; import cn.iocoder.springboot.lab58.feigndemo.feign.UserServiceFeignClient; import cn.iocoder.springboot.lab58.feigndemo.feign.request.UserAddRequest; import cn.iocoder.springboot.lab58.feigndemo.feign.response.UserResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.List; import java.util.Map; @RestController @RequestMapping("/demo") public class DemoController { @Autowired private UserServiceFeignClient userServiceFeignClient; @GetMapping("/test01") public UserResponse test01() { return userServiceFeignClient.get(1); // System.out.println("编号:" + user.getId()); // System.out.println("昵称:" + user.getName()); // System.out.println("性别:" + user.getGender()); } @GetMapping("/test02A") public List test02A() { return userServiceFeignClient.list("你猜", 1); } @GetMapping("/test02B") public List test02B() { Map queryMap = new HashMap<>(); queryMap.put("name", "昵称"); return userServiceFeignClient.list(queryMap); } @GetMapping("/test03") public Integer test03() { return userServiceFeignClient.add(new UserAddRequest() .setName("昵称").setGender(1)); } } ================================================ FILE: lab-58/lab-58-feign-demo/src/main/java/cn/iocoder/springboot/lab58/feigndemo/controller/DemoController02.java ================================================ package cn.iocoder.springboot.lab58.feigndemo.controller; import cn.iocoder.springboot.lab58.feigndemo.feign.UserServiceFeignClient02; import cn.iocoder.springboot.lab58.feigndemo.feign.request.UserAddRequest; import cn.iocoder.springboot.lab58.feigndemo.feign.response.UserResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; @RestController @RequestMapping("/demo02") public class DemoController02 { @Autowired private UserServiceFeignClient02 userServiceFeignClient; @GetMapping("/test01") public UserResponse test01() { return userServiceFeignClient.get(1); // System.out.println("编号:" + user.getId()); // System.out.println("昵称:" + user.getName()); // System.out.println("性别:" + user.getGender()); } @GetMapping("/test02A") public List test02A() { return userServiceFeignClient.list("你猜", null); } // @GetMapping("/test02B") // public List test02B() { // Map queryMap = new HashMap<>(); // queryMap.put("name", "昵称"); // return userServiceFeignClient.list(queryMap); // } @GetMapping("/test03") public Integer test03() { return userServiceFeignClient.add(new UserAddRequest() .setName("昵称").setGender(1)); } } ================================================ FILE: lab-58/lab-58-feign-demo/src/main/java/cn/iocoder/springboot/lab58/feigndemo/feign/UserServiceFeignClient.java ================================================ package cn.iocoder.springboot.lab58.feigndemo.feign; import cn.iocoder.springboot.lab58.feigndemo.feign.request.UserAddRequest; import cn.iocoder.springboot.lab58.feigndemo.feign.response.UserResponse; import feign.*; import java.util.List; import java.util.Map; /** * 基于 Contract.Default 默认契约 */ public interface UserServiceFeignClient { // 获得用户详情 @RequestLine("GET /user/get?id={id}") UserResponse get(@Param("id") Integer id); @RequestLine("GET /user/list?name={name}&gender={gender}") List list(@Param("name") String name, @Param("gender") Integer gender); @RequestLine("GET /user/list") List list(@QueryMap Map queryMap); @RequestLine("POST /user/add") @Headers("Content-Type: application/json") Integer add(UserAddRequest request); } ================================================ FILE: lab-58/lab-58-feign-demo/src/main/java/cn/iocoder/springboot/lab58/feigndemo/feign/UserServiceFeignClient02.java ================================================ package cn.iocoder.springboot.lab58.feigndemo.feign; import cn.iocoder.springboot.lab58.feigndemo.feign.request.UserAddRequest; import cn.iocoder.springboot.lab58.feigndemo.feign.response.UserResponse; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; import java.util.List; /** * 基于 SpringContract 拓展契约 */ public interface UserServiceFeignClient02 { // 获得用户详情 @GetMapping("/user/get") UserResponse get(@RequestParam("id") Integer id); @GetMapping("/user/list") List list(@RequestParam("name") String name, @RequestParam("gender") Integer gender); // @RequestLine("GET /user/list") // List list(@QueryMap Map queryMap); @PostMapping(value = "/user/add", consumes = "application/json") Integer add(@RequestBody UserAddRequest request); } ================================================ FILE: lab-58/lab-58-feign-demo/src/main/java/cn/iocoder/springboot/lab58/feigndemo/feign/request/UserAddRequest.java ================================================ package cn.iocoder.springboot.lab58.feigndemo.feign.request; /** * User 添加 Request */ public class UserAddRequest { private String name; private Integer gender; public String getName() { return name; } public UserAddRequest setName(String name) { this.name = name; return this; } public Integer getGender() { return gender; } public UserAddRequest setGender(Integer gender) { this.gender = gender; return this; } } ================================================ FILE: lab-58/lab-58-feign-demo/src/main/java/cn/iocoder/springboot/lab58/feigndemo/feign/response/UserResponse.java ================================================ package cn.iocoder.springboot.lab58.feigndemo.feign.response; public class UserResponse { private Integer id; private String name; private Integer gender; public Integer getId() { return id; } public UserResponse setId(Integer id) { this.id = id; return this; } public String getName() { return name; } public UserResponse setName(String name) { this.name = name; return this; } public Integer getGender() { return gender; } public UserResponse setGender(Integer gender) { this.gender = gender; return this; } } ================================================ FILE: lab-58/lab-58-user-service/pom.xml ================================================ lab-58 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-58-user-service 1.8 1.8 2.2.4.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.boot spring-boot-starter-web ================================================ FILE: lab-58/lab-58-user-service/src/main/java/cn/iocoder/springboot/lab58/userservice/UserServiceApplication.java ================================================ package cn.iocoder.springboot.lab58.userservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class UserServiceApplication { public static void main(String[] args) { // 设置端口 System.setProperty("server.port", "18080"); // 应用启动 SpringApplication.run(UserServiceApplication.class, args); } } ================================================ FILE: lab-58/lab-58-user-service/src/main/java/cn/iocoder/springboot/lab58/userservice/controller/UserController.java ================================================ package cn.iocoder.springboot.lab58.userservice.controller; import cn.iocoder.springboot.lab58.userservice.request.UserAddRequest; import cn.iocoder.springboot.lab58.userservice.response.UserResponse; import org.springframework.web.bind.annotation.*; import java.util.ArrayList; import java.util.List; @RestController @RequestMapping("/user") public class UserController { @GetMapping("/get") // 获得指定用户 public UserResponse get(@RequestParam("id") Integer id) { return new UserResponse().setId(id) .setName("昵称:" + id).setGender(id % 2 == 0 ? 1 : 2); } @GetMapping("/list") // 获得匹配的用户列表 public List list(@RequestParam(value = "name", required = false) String name, @RequestParam(value = "gender", required = false) Integer gender) { List users = new ArrayList<>(); for (int id = 1; id <= 3; id++) { users.add(new UserResponse().setId(id) .setName(name + "_" + id).setGender(gender)); } return users; } @PostMapping("/add") // 添加用户 public Integer add(@RequestBody UserAddRequest request) { System.out.println("昵称:" + request.getName()); System.out.println("性别:" + request.getGender()); return 1; } } ================================================ FILE: lab-58/lab-58-user-service/src/main/java/cn/iocoder/springboot/lab58/userservice/request/UserAddRequest.java ================================================ package cn.iocoder.springboot.lab58.userservice.request; /** * User 添加 Request */ public class UserAddRequest { private String name; private Integer gender; public String getName() { return name; } public UserAddRequest setName(String name) { this.name = name; return this; } public Integer getGender() { return gender; } public UserAddRequest setGender(Integer gender) { this.gender = gender; return this; } } ================================================ FILE: lab-58/lab-58-user-service/src/main/java/cn/iocoder/springboot/lab58/userservice/response/UserResponse.java ================================================ package cn.iocoder.springboot.lab58.userservice.response; /** * User 信息 Response */ public class UserResponse { private Integer id; private String name; private Integer gender; public Integer getId() { return id; } public UserResponse setId(Integer id) { this.id = id; return this; } public String getName() { return name; } public UserResponse setName(String name) { this.name = name; return this; } public Integer getGender() { return gender; } public UserResponse setGender(Integer gender) { this.gender = gender; return this; } } ================================================ FILE: lab-58/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-58 pom lab-58-user-service lab-58-feign-demo ================================================ FILE: lab-58/《芋道 Spring Boot 声明式调用 Feign 入门》.md ================================================ ================================================ FILE: lab-59/lab-59-resilience4j-actuator/pom.xml ================================================ lab-59 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-59-resilience4j-actuator 1.8 1.8 2.2.4.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.boot spring-boot-starter-web io.github.resilience4j resilience4j-spring-boot2 1.4.0 org.aspectj aspectjrt 1.9.5 org.aspectj aspectjweaver 1.9.5 org.springframework.boot spring-boot-starter-actuator ================================================ FILE: lab-59/lab-59-resilience4j-actuator/src/main/java/cn/iocoder/springboot/lab59/resillience4jdemo/DemoApplication.java ================================================ package cn.iocoder.springboot.lab59.resillience4jdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @SpringBootApplication public class DemoApplication { @Bean public RestTemplate restTemplate() { return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } ================================================ FILE: lab-59/lab-59-resilience4j-actuator/src/main/java/cn/iocoder/springboot/lab59/resillience4jdemo/controller/BulkheadDemoController.java ================================================ package cn.iocoder.springboot.lab59.resillience4jdemo.controller; import io.github.resilience4j.bulkhead.annotation.Bulkhead; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/bulkhead-demo") public class BulkheadDemoController { private Logger logger = LoggerFactory.getLogger(getClass()); @GetMapping("/get_user") @Bulkhead(name = "backendC", fallbackMethod = "getUserFallback", type = Bulkhead.Type.SEMAPHORE) public String getUser(@RequestParam("id") Integer id) throws InterruptedException { logger.info("[getUser][id({})]", id); Thread.sleep(10 * 1000L); // sleep 10 秒 return "User:" + id; } public String getUserFallback(Integer id, Throwable throwable) { logger.info("[getUserFallback][id({}) exception({})]", id, throwable.getClass().getSimpleName()); return "mock:User:" + id; } } ================================================ FILE: lab-59/lab-59-resilience4j-actuator/src/main/java/cn/iocoder/springboot/lab59/resillience4jdemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab59.resillience4jdemo.controller; import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @RestController @RequestMapping("/demo") public class DemoController { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private RestTemplate restTemplate; @GetMapping("/get_user") @CircuitBreaker(name = "backendA", fallbackMethod = "getUserFallback") public String getUser(@RequestParam("id") Integer id) { logger.info("[getUser][准备调用 user-service 获取用户({})详情]", id); return restTemplate.getForEntity("http://127.0.0.1:18080/user/get?id=" + id, String.class).getBody(); } public String getUserFallback(Integer id, Throwable throwable) { logger.info("[getUserFallback][id({}) exception({})]", id, throwable.getClass().getSimpleName()); return "mock:User:" + id; } } ================================================ FILE: lab-59/lab-59-resilience4j-actuator/src/main/java/cn/iocoder/springboot/lab59/resillience4jdemo/controller/RateLimiterDemoController.java ================================================ package cn.iocoder.springboot.lab59.resillience4jdemo.controller; import io.github.resilience4j.ratelimiter.annotation.RateLimiter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/rate-limiter-demo") public class RateLimiterDemoController { private Logger logger = LoggerFactory.getLogger(getClass()); @GetMapping("/get_user") @RateLimiter(name = "backendB", fallbackMethod = "getUserFallback") public String getUser(@RequestParam("id") Integer id) { return "User:" + id; } public String getUserFallback(Integer id, Throwable throwable) { logger.info("[getUserFallback][id({}) exception({})]", id, throwable.getClass().getSimpleName()); return "mock:User:" + id; } } ================================================ FILE: lab-59/lab-59-resilience4j-actuator/src/main/java/cn/iocoder/springboot/lab59/resillience4jdemo/controller/RetryDemoController.java ================================================ package cn.iocoder.springboot.lab59.resillience4jdemo.controller; import io.github.resilience4j.retry.annotation.Retry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @RestController @RequestMapping("/retry-demo") public class RetryDemoController { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private RestTemplate restTemplate; @GetMapping("/get_user") @Retry(name = "backendE", fallbackMethod = "getUserFallback") public String getUser(@RequestParam("id") Integer id) { logger.info("[getUser][准备调用 user-service 获取用户({})详情]", id); return restTemplate.getForEntity("http://127.0.0.1:18080/user/get?id=" + id, String.class).getBody(); } public String getUserFallback(Integer id, Throwable throwable) { logger.info("[getUserFallback][id({}) exception({})]", id, throwable.getClass().getSimpleName()); return "mock:User:" + id; } } ================================================ FILE: lab-59/lab-59-resilience4j-actuator/src/main/java/cn/iocoder/springboot/lab59/resillience4jdemo/controller/ThreadPoolBulkheadDemoController.java ================================================ package cn.iocoder.springboot.lab59.resillience4jdemo.controller; import io.github.resilience4j.bulkhead.annotation.Bulkhead; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @RestController @RequestMapping("/thread-pool-bulkhead-demo") public class ThreadPoolBulkheadDemoController { @Autowired private ThreadPoolBulkheadService threadPoolBulkheadService; @GetMapping("/get_user") public String getUser(@RequestParam("id") Integer id) throws ExecutionException, InterruptedException { threadPoolBulkheadService.getUser0(id); return threadPoolBulkheadService.getUser0(id).get(); } @Service public static class ThreadPoolBulkheadService { private Logger logger = LoggerFactory.getLogger(ThreadPoolBulkheadService.class); @Bulkhead(name = "backendD", fallbackMethod = "getUserFallback", type = Bulkhead.Type.THREADPOOL) public CompletableFuture getUser0(Integer id) throws InterruptedException { logger.info("[getUser][id({})]", id); Thread.sleep(10 * 1000L); // sleep 10 秒 return CompletableFuture.completedFuture("User:" + id); } public CompletableFuture getUserFallback(Integer id, Throwable throwable) { logger.info("[getUserFallback][id({}) exception({})]", id, throwable.getClass().getSimpleName()); return CompletableFuture.completedFuture("mock:User:" + id); } } } ================================================ FILE: lab-59/lab-59-resilience4j-actuator/src/main/java/cn/iocoder/springboot/lab59/resillience4jdemo/controller/TimeLimiterDemoController.java ================================================ package cn.iocoder.springboot.lab59.resillience4jdemo.controller; import io.github.resilience4j.bulkhead.annotation.Bulkhead; import io.github.resilience4j.timelimiter.annotation.TimeLimiter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @RestController @RequestMapping("/time-limiter-demo") public class TimeLimiterDemoController { @Autowired private TimeLimiterService timeLimiterService; @GetMapping("/get_user") public String getUser(@RequestParam("id") Integer id) throws ExecutionException, InterruptedException { return timeLimiterService.getUser0(id).get(); } @Service public static class TimeLimiterService { private Logger logger = LoggerFactory.getLogger(TimeLimiterService.class); @Bulkhead(name = "backendD", type = Bulkhead.Type.THREADPOOL) @TimeLimiter(name = "backendF", fallbackMethod = "getUserFallback") public CompletableFuture getUser0(Integer id) throws InterruptedException { logger.info("[getUser][id({})]", id); Thread.sleep(10 * 1000L); // sleep 10 秒 return CompletableFuture.completedFuture("User:" + id); } public CompletableFuture getUserFallback(Integer id, Throwable throwable) { logger.info("[getUserFallback][id({}) exception({})]", id, throwable.getClass().getSimpleName()); return CompletableFuture.completedFuture("mock:User:" + id); } } } ================================================ FILE: lab-59/lab-59-resilience4j-actuator/src/main/resources/application.yml ================================================ resilience4j: # Resilience4j 的断路器配置项,对应 CircuitBreakerProperties 属性类 circuitbreaker: instances: backendA: failure-rate-threshold: 50 # 熔断器关闭状态和半开状态使用的同一个失败率阈值,单位:百分比。默认为 50 ring-buffer-size-in-closed-state: 5 # 熔断器关闭状态的缓冲区大小,不会限制线程的并发量,在熔断器发生状态转换前所有请求都会调用后端服务。默认为 100 ring-buffer-size-in-half-open-state: 5 # 熔断器半开状态的缓冲区大小,会限制线程的并发量。例如,缓冲区为 10 则每次只会允许 10 个请求调用后端服务。默认为 10 wait-duration-in-open-state : 5000 # 熔断器从打开状态转变为半开状态等待的时间,单位:微秒 automatic-transition-from-open-to-half-open-enabled: true # 如果置为 true,当等待时间结束会自动由打开变为半开;若置为 false,则需要一个请求进入来触发熔断器状态转换。默认为 true register-health-indicator: true # 是否注册到健康监测 # Resilience4j 的限流器配置项,对应 RateLimiterProperties 属性类 ratelimiter: instances: backendB: limit-for-period: 1 # 每个周期内,允许的请求数。默认为 50 limit-refresh-period: 10s # 每个周期的时长,单位:微秒。默认为 500 timeout-duration: 5s # 被限流时,阻塞等待的时长,单位:微秒。默认为 5s register-health-indicator: true # 是否注册到健康监测 # Resilience4j 的信号量 Bulkhead 配置项,对应 BulkheadConfigurationProperties 属性类 bulkhead: instances: backendC: max-concurrent-calls: 1 # 并发调用数。默认为 25 max-wait-duration: 5s # 并发调用到达上限时,阻塞等待的时长,单位:微秒。默认为 0 # Resilience4j 的线程池 Bulkhead 配置项,对应 ThreadPoolBulkheadProperties 属性类 thread-pool-bulkhead: instances: backendD: max-thread-pool-size: 1 # 线程池的最大大小。默认为 Runtime.getRuntime().availableProcessors() core-thread-pool-size: 1 # 线程池的核心大小。默认为 Runtime.getRuntime().availableProcessors() - 1 queue-capacity: 200 # 线程池的队列大小。默认为 100 keep-alive-duration: 100s # 超过核心大小的线程,空闲存活时间。默认为 20 毫秒 # Resilience4j 的重试 Retry 配置项,对应 RetryProperties 属性类 retry: instances: backendE: max-retry-Attempts: 3 # 最大重试次数。默认为 3 wait-duration: 5s # 下次重试的间隔,单位:微秒。默认为 500 毫秒 retry-exceptions: # 需要重试的异常列表。默认为空 ingore-exceptions: # 需要忽略的异常列表。默认为空 # Resilience4j 的超时限制器 TimeLimiter 配置项,对应 TimeLimiterProperties 属性类 timelimiter: instances: backendF: timeout-duration: 1s # 等待超时时间,单位:微秒。默认为 1 秒 cancel-running-future: true # 当等待超时时,是否关闭取消线程。默认为 true management: endpoints: # Actuator HTTP 配置项,对应 WebEndpointProperties 配置类 web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 endpoint: # Health 端点配置项,对应 HealthProperties 配置类 health: show-details: ALWAYS # 何时显示完整的健康信息。默认为 NEVER 都不展示。可选 WHEN_AUTHORIZED 当经过授权的用户;可选 ALWAYS 总是展示。 # 健康检查配置项 health: circuitbreakers.enabled: true ratelimiters.enabled: true ================================================ FILE: lab-59/lab-59-resilience4j-demo01/pom.xml ================================================ lab-59 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-59-resilience4j-demo01 1.8 1.8 2.2.4.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.boot spring-boot-starter-web io.github.resilience4j resilience4j-spring-boot2 1.4.0 org.aspectj aspectjrt 1.9.5 org.aspectj aspectjweaver 1.9.5 ================================================ FILE: lab-59/lab-59-resilience4j-demo01/src/main/java/cn/iocoder/springboot/lab59/resillience4jdemo/DemoApplication.java ================================================ package cn.iocoder.springboot.lab59.resillience4jdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @SpringBootApplication public class DemoApplication { @Bean public RestTemplate restTemplate() { return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } ================================================ FILE: lab-59/lab-59-resilience4j-demo01/src/main/java/cn/iocoder/springboot/lab59/resillience4jdemo/controller/BulkheadDemoController.java ================================================ package cn.iocoder.springboot.lab59.resillience4jdemo.controller; import io.github.resilience4j.bulkhead.annotation.Bulkhead; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/bulkhead-demo") public class BulkheadDemoController { private Logger logger = LoggerFactory.getLogger(getClass()); @GetMapping("/get_user") @Bulkhead(name = "backendC", fallbackMethod = "getUserFallback", type = Bulkhead.Type.SEMAPHORE) public String getUser(@RequestParam("id") Integer id) throws InterruptedException { logger.info("[getUser][id({})]", id); Thread.sleep(10 * 1000L); // sleep 10 秒 return "User:" + id; } public String getUserFallback(Integer id, Throwable throwable) { logger.info("[getUserFallback][id({}) exception({})]", id, throwable.getClass().getSimpleName()); return "mock:User:" + id; } } ================================================ FILE: lab-59/lab-59-resilience4j-demo01/src/main/java/cn/iocoder/springboot/lab59/resillience4jdemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab59.resillience4jdemo.controller; import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @RestController @RequestMapping("/demo") public class DemoController { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private RestTemplate restTemplate; @GetMapping("/get_user") @CircuitBreaker(name = "backendA", fallbackMethod = "getUserFallback") public String getUser(@RequestParam("id") Integer id) { logger.info("[getUser][准备调用 user-service 获取用户({})详情]", id); return restTemplate.getForEntity("http://127.0.0.1:18080/user/get?id=" + id, String.class).getBody(); } public String getUserFallback(Integer id, Throwable throwable) { logger.info("[getUserFallback][id({}) exception({})]", id, throwable.getClass().getSimpleName()); return "mock:User:" + id; } } ================================================ FILE: lab-59/lab-59-resilience4j-demo01/src/main/java/cn/iocoder/springboot/lab59/resillience4jdemo/controller/RateLimiterDemoController.java ================================================ package cn.iocoder.springboot.lab59.resillience4jdemo.controller; import io.github.resilience4j.ratelimiter.annotation.RateLimiter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/rate-limiter-demo") public class RateLimiterDemoController { private Logger logger = LoggerFactory.getLogger(getClass()); @GetMapping("/get_user") @RateLimiter(name = "backendB", fallbackMethod = "getUserFallback") public String getUser(@RequestParam("id") Integer id) { return "User:" + id; } public String getUserFallback(Integer id, Throwable throwable) { logger.info("[getUserFallback][id({}) exception({})]", id, throwable.getClass().getSimpleName()); return "mock:User:" + id; } } ================================================ FILE: lab-59/lab-59-resilience4j-demo01/src/main/java/cn/iocoder/springboot/lab59/resillience4jdemo/controller/RetryDemoController.java ================================================ package cn.iocoder.springboot.lab59.resillience4jdemo.controller; import io.github.resilience4j.retry.annotation.Retry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @RestController @RequestMapping("/retry-demo") public class RetryDemoController { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private RestTemplate restTemplate; @GetMapping("/get_user") @Retry(name = "backendE", fallbackMethod = "getUserFallback") public String getUser(@RequestParam("id") Integer id) { logger.info("[getUser][准备调用 user-service 获取用户({})详情]", id); return restTemplate.getForEntity("http://127.0.0.1:18080/user/get?id=" + id, String.class).getBody(); } public String getUserFallback(Integer id, Throwable throwable) { logger.info("[getUserFallback][id({}) exception({})]", id, throwable.getClass().getSimpleName()); return "mock:User:" + id; } } ================================================ FILE: lab-59/lab-59-resilience4j-demo01/src/main/java/cn/iocoder/springboot/lab59/resillience4jdemo/controller/ThreadPoolBulkheadDemoController.java ================================================ package cn.iocoder.springboot.lab59.resillience4jdemo.controller; import io.github.resilience4j.bulkhead.annotation.Bulkhead; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @RestController @RequestMapping("/thread-pool-bulkhead-demo") public class ThreadPoolBulkheadDemoController { @Autowired private ThreadPoolBulkheadService threadPoolBulkheadService; @GetMapping("/get_user") public String getUser(@RequestParam("id") Integer id) throws ExecutionException, InterruptedException { threadPoolBulkheadService.getUser0(id); return threadPoolBulkheadService.getUser0(id).get(); } @Service public static class ThreadPoolBulkheadService { private Logger logger = LoggerFactory.getLogger(ThreadPoolBulkheadService.class); @Bulkhead(name = "backendD", fallbackMethod = "getUserFallback", type = Bulkhead.Type.THREADPOOL) public CompletableFuture getUser0(Integer id) throws InterruptedException { logger.info("[getUser][id({})]", id); Thread.sleep(10 * 1000L); // sleep 10 秒 return CompletableFuture.completedFuture("User:" + id); } public CompletableFuture getUserFallback(Integer id, Throwable throwable) { logger.info("[getUserFallback][id({}) exception({})]", id, throwable.getClass().getSimpleName()); return CompletableFuture.completedFuture("mock:User:" + id); } } } ================================================ FILE: lab-59/lab-59-resilience4j-demo01/src/main/java/cn/iocoder/springboot/lab59/resillience4jdemo/controller/TimeLimiterDemoController.java ================================================ package cn.iocoder.springboot.lab59.resillience4jdemo.controller; import io.github.resilience4j.bulkhead.annotation.Bulkhead; import io.github.resilience4j.timelimiter.annotation.TimeLimiter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @RestController @RequestMapping("/time-limiter-demo") public class TimeLimiterDemoController { @Autowired private TimeLimiterService timeLimiterService; @GetMapping("/get_user") public String getUser(@RequestParam("id") Integer id) throws ExecutionException, InterruptedException { return timeLimiterService.getUser0(id).get(); } @Service public static class TimeLimiterService { private Logger logger = LoggerFactory.getLogger(TimeLimiterService.class); @Bulkhead(name = "backendD", type = Bulkhead.Type.THREADPOOL) @TimeLimiter(name = "backendF", fallbackMethod = "getUserFallback") public CompletableFuture getUser0(Integer id) throws InterruptedException { logger.info("[getUser][id({})]", id); Thread.sleep(10 * 1000L); // sleep 10 秒 return CompletableFuture.completedFuture("User:" + id); } public CompletableFuture getUserFallback(Integer id, Throwable throwable) { logger.info("[getUserFallback][id({}) exception({})]", id, throwable.getClass().getSimpleName()); return CompletableFuture.completedFuture("mock:User:" + id); } } } ================================================ FILE: lab-59/lab-59-resilience4j-demo01/src/main/resources/application.yml ================================================ resilience4j: # Resilience4j 的断路器配置项,对应 CircuitBreakerProperties 属性类 circuitbreaker: instances: backendA: failure-rate-threshold: 50 # 熔断器关闭状态和半开状态使用的同一个失败率阈值,单位:百分比。默认为 50 ring-buffer-size-in-closed-state: 5 # 熔断器关闭状态的缓冲区大小,不会限制线程的并发量,在熔断器发生状态转换前所有请求都会调用后端服务。默认为 100 ring-buffer-size-in-half-open-state: 5 # 熔断器半开状态的缓冲区大小,会限制线程的并发量。例如,缓冲区为 10 则每次只会允许 10 个请求调用后端服务。默认为 10 wait-duration-in-open-state : 5000 # 熔断器从打开状态转变为半开状态等待的时间,单位:微秒 automatic-transition-from-open-to-half-open-enabled: true # 如果置为 true,当等待时间结束会自动由打开变为半开;若置为 false,则需要一个请求进入来触发熔断器状态转换。默认为 true register-health-indicator: true # 是否注册到健康监测 # Resilience4j 的限流器配置项,对应 RateLimiterProperties 属性类 ratelimiter: instances: backendB: limit-for-period: 1 # 每个周期内,允许的请求数。默认为 50 limit-refresh-period: 10s # 每个周期的时长,单位:微秒。默认为 500 timeout-duration: 5s # 被限流时,阻塞等待的时长,单位:微秒。默认为 5s register-health-indicator: true # 是否注册到健康监测 # Resilience4j 的信号量 Bulkhead 配置项,对应 BulkheadConfigurationProperties 属性类 bulkhead: instances: backendC: max-concurrent-calls: 1 # 并发调用数。默认为 25 max-wait-duration: 5s # 并发调用到达上限时,阻塞等待的时长,单位:微秒。默认为 0 # Resilience4j 的线程池 Bulkhead 配置项,对应 ThreadPoolBulkheadProperties 属性类 thread-pool-bulkhead: instances: backendD: max-thread-pool-size: 1 # 线程池的最大大小。默认为 Runtime.getRuntime().availableProcessors() core-thread-pool-size: 1 # 线程池的核心大小。默认为 Runtime.getRuntime().availableProcessors() - 1 queue-capacity: 200 # 线程池的队列大小。默认为 100 keep-alive-duration: 100s # 超过核心大小的线程,空闲存活时间。默认为 20 毫秒 # Resilience4j 的重试 Retry 配置项,对应 RetryProperties 属性类 retry: instances: backendE: max-retry-Attempts: 3 # 最大重试次数。默认为 3 wait-duration: 5s # 下次重试的间隔,单位:微秒。默认为 500 毫秒 retry-exceptions: # 需要重试的异常列表。默认为空 ingore-exceptions: # 需要忽略的异常列表。默认为空 # Resilience4j 的超时限制器 TimeLimiter 配置项,对应 TimeLimiterProperties 属性类 timelimiter: instances: backendF: timeout-duration: 1s # 等待超时时间,单位:微秒。默认为 1 秒 cancel-running-future: true # 当等待超时时,是否关闭取消线程。默认为 true ================================================ FILE: lab-59/lab-59-user-service/pom.xml ================================================ lab-59 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-59-user-service 1.8 1.8 2.2.4.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.boot spring-boot-starter-web ================================================ FILE: lab-59/lab-59-user-service/src/main/java/cn/iocoder/springboot/lab59/userservice/UserServiceApplication.java ================================================ package cn.iocoder.springboot.lab59.userservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.List; import java.util.stream.Collectors; @SpringBootApplication public class UserServiceApplication { @RestController @RequestMapping("/user") public class UserController { @GetMapping("/get") public String get(@RequestParam("id") Integer id) { return "User:" + id; } @GetMapping("/batch_get") public List batchGet(@RequestParam("ids") List ids) { return ids.stream().map(id -> "User:" + id).collect(Collectors.toList()); } } public static void main(String[] args) { // 设置端口 System.setProperty("server.port", "18080"); // 应用启动 SpringApplication.run(UserServiceApplication.class, args); } } ================================================ FILE: lab-59/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-59 pom lab-59-user-service lab-59-resilience4j-demo01 lab-59-resilience4j-actuator ================================================ FILE: lab-59/《芋道 Spring Boot 服务容错 Resilience4j 入门》.md ================================================ ================================================ FILE: lab-60/lab-60-soul-dubbo-demo/lab-60-soul-dubbo-demo-user-service/pom.xml ================================================ lab-60-soul-dubbo-demo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-60-soul-dubbo-demo-user-service 1.8 1.8 2.2.4.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import cn.iocoder.springboot.labs lab-60-soul-dubbo-demo-user-service-api 1.0-SNAPSHOT org.springframework.boot spring-boot-starter org.apache.dubbo dubbo 2.7.4.1 org.apache.dubbo dubbo-spring-boot-starter 2.7.4.1 com.alibaba.nacos nacos-client 1.2.1 org.apache.dubbo dubbo-registry-nacos 2.7.4.1 org.dromara soul-client-apache-dubbo 2.1.2-RELEASE ================================================ FILE: lab-60/lab-60-soul-dubbo-demo/lab-60-soul-dubbo-demo-user-service/src/main/java/cn/iocoder/springboot/lab60/userservice/UserServiceApplication.java ================================================ package cn.iocoder.springboot.lab60.userservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class UserServiceApplication { public static void main(String[] args) { SpringApplication.run(UserServiceApplication.class); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab60.userservice.service; import cn.iocoder.springboot.lab60.userservice.api.UserService; import cn.iocoder.springboot.lab60.userservice.api.dto.UserCreateDTO; import org.dromara.soul.client.common.annotation.SoulClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @org.apache.dubbo.config.annotation.Service(version = "1.0.0") public class UserServiceImpl implements UserService { private Logger logger = LoggerFactory.getLogger(getClass()); @Override @SoulClient(path = "/user/get", desc = "获得用户详细") public String getUser(Integer id) { return "User:" + id; } @Override @SoulClient(path = "/user/create", desc = "创建用户") public Integer createUser(UserCreateDTO createDTO) { logger.info("[createUser][username({}) password({})]", createDTO.getNickname(), createDTO.getGender()); return 1; } } ================================================ FILE: lab-60/lab-60-soul-dubbo-demo/lab-60-soul-dubbo-demo-user-service/src/main/resources/application.yaml ================================================ # dubbo 配置项,对应 DubboConfigurationProperties 配置类 dubbo: # Dubbo 应用配置 application: name: user-service # 应用名 # Dubbo 注册中心配 registry: address: nacos://127.0.0.1:8848 # 注册中心地址。个鞥多注册中心,可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。 # Dubbo 服务提供者协议配置 protocol: port: -1 # 协议端口。使用 -1 表示随机端口。 name: dubbo # 使用 `dubbo://` 协议。更多协议,可见 http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html 文档 # Dubbo 服务提供者配置 provider: timeout: 1000 # 【重要】远程服务调用超时时间,单位:毫秒。默认为 1000 毫秒,胖友可以根据自己业务修改 # 配置扫描 Dubbo 自定义的 @Service 注解,暴露成 Dubbo 服务提供者 scan: base-packages: cn.iocoder.springboot.lab60.userservice.service soul: # Soul 针对 Dubbo 的配置项,对应 DubboConfig 配置类 dubbo: admin-url: http://127.0.0.1:9095 # Soul Admin 地址 context-path: /user-api # 设置在 Soul 网关的路由前缀,例如说 /order、/product 等等。 # 后续,网关会根据该 context-path 来进行路由 app-name: user-service # 应用名。未配置情况下,默认使用 Dubbo 的应用名 ================================================ FILE: lab-60/lab-60-soul-dubbo-demo/lab-60-soul-dubbo-demo-user-service-api/pom.xml ================================================ lab-60-soul-dubbo-demo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-60-soul-dubbo-demo-user-service-api ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab60.userservice.api; import cn.iocoder.springboot.lab60.userservice.api.dto.UserCreateDTO; public interface UserService { String getUser(Integer id); Integer createUser(UserCreateDTO createDTO); } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab60.userservice.api.dto; /** * 用户创建 DTO */ public class UserCreateDTO { /** * 昵称 */ private String nickname; /** * 性别 */ private Integer gender; public String getNickname() { return nickname; } public UserCreateDTO setNickname(String nickname) { this.nickname = nickname; return this; } public Integer getGender() { return gender; } public UserCreateDTO setGender(Integer gender) { this.gender = gender; return this; } } ================================================ FILE: lab-60/lab-60-soul-dubbo-demo/pom.xml ================================================ lab-60 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-60-soul-dubbo-demo pom lab-60-soul-dubbo-demo-user-service-api lab-60-soul-dubbo-demo-user-service ================================================ FILE: lab-60/lab-60-soul-spring-boot-demo/pom.xml ================================================ lab-60 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-60-soul-spring-boot-demo 1.8 1.8 2.2.4.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.boot spring-boot-starter-web org.dromara soul-client-springmvc 2.1.2-RELEASE ================================================ FILE: lab-60/lab-60-soul-spring-boot-demo/src/main/java/cn/iocoder/springboot/lab60/DemoApplication.java ================================================ package cn.iocoder.springboot.lab60; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } ================================================ FILE: lab-60/lab-60-soul-spring-boot-demo/src/main/java/cn/iocoder/springboot/lab60/controller/UserController.java ================================================ package cn.iocoder.springboot.lab60.controller; import cn.iocoder.springboot.lab60.dto.UserCreateDTO; import org.dromara.soul.client.common.annotation.SoulClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/user") public class UserController { private Logger logger = LoggerFactory.getLogger(getClass()); @GetMapping("/get") @SoulClient(path = "/user/get", desc = "获得用户详细") public String getUser(@RequestParam("id") Integer id) { return "DEMO:" + id; } @PostMapping("/create") @SoulClient(path = "/user/create", desc = "创建用户") public Integer createUser(@RequestBody UserCreateDTO createDTO) { logger.info("[createUser][username({}) password({})]", createDTO.getNickname(), createDTO.getGender()); return 1; } } ================================================ FILE: lab-60/lab-60-soul-spring-boot-demo/src/main/java/cn/iocoder/springboot/lab60/dto/UserCreateDTO.java ================================================ package cn.iocoder.springboot.lab60.dto; /** * 用户创建 DTO */ public class UserCreateDTO { /** * 昵称 */ private String nickname; /** * 性别 */ private Integer gender; public String getNickname() { return nickname; } public UserCreateDTO setNickname(String nickname) { this.nickname = nickname; return this; } public Integer getGender() { return gender; } public UserCreateDTO setGender(Integer gender) { this.gender = gender; return this; } } ================================================ FILE: lab-60/lab-60-soul-spring-boot-demo/src/main/resources/application.yaml ================================================ soul: # Soul 针对 SpringMVC 的配置项,对应 SoulHttpConfig 配置类 http: admin-url: http://127.0.0.1:9095 # Soul Admin 地址 context-path: /sb-demo-api # 设置在 Soul 网关的路由前缀,例如说 /order、/product 等等。 # 后续,网关会根据该 context-path 来进行路由 app-name: sb-demo-service # 应用名。未配置情况下,默认使用 `spring.application.name` 配置项 zookeeper-url: 127.0.0.1:2181 # 使用 Zookeeper 作为注册中心的地址 ================================================ FILE: lab-60/lab-60-soul-spring-cloud-demo/pom.xml ================================================ lab-60 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-60-soul-spring-cloud-demo 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery org.dromara soul-client-springcloud 2.1.2-RELEASE ================================================ FILE: lab-60/lab-60-soul-spring-cloud-demo/src/main/java/cn/iocoder/springcloud/lab60/DemoApplication.java ================================================ package cn.iocoder.springcloud.lab60; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } ================================================ FILE: lab-60/lab-60-soul-spring-cloud-demo/src/main/java/cn/iocoder/springcloud/lab60/controller/UserController.java ================================================ package cn.iocoder.springcloud.lab60.controller; import cn.iocoder.springcloud.lab60.dto.UserCreateDTO; import org.dromara.soul.client.common.annotation.SoulClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/user") public class UserController { private Logger logger = LoggerFactory.getLogger(getClass()); @GetMapping("/get") @SoulClient(path = "/user/get", desc = "获得用户详细") public String getUser(@RequestParam("id") Integer id) { return "DEMO:" + id; } @PostMapping("/create") @SoulClient(path = "/user/create", desc = "创建用户") public Integer createUser(@RequestBody UserCreateDTO createDTO) { logger.info("[createUser][username({}) password({})]", createDTO.getNickname(), createDTO.getGender()); return 1; } } ================================================ FILE: lab-60/lab-60-soul-spring-cloud-demo/src/main/java/cn/iocoder/springcloud/lab60/dto/UserCreateDTO.java ================================================ package cn.iocoder.springcloud.lab60.dto; /** * 用户创建 DTO */ public class UserCreateDTO { /** * 昵称 */ private String nickname; /** * 性别 */ private Integer gender; public String getNickname() { return nickname; } public UserCreateDTO setNickname(String nickname) { this.nickname = nickname; return this; } public Integer getGender() { return gender; } public UserCreateDTO setGender(Integer gender) { this.gender = gender; return this; } } ================================================ FILE: lab-60/lab-60-soul-spring-cloud-demo/src/main/resources/application.yaml ================================================ spring: application: name: sc-user-service # Spring 应用名 cloud: nacos: # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 soul: # Soul 针对 SpringMVC 的配置项,对应 SoulSpringCloudConfig 配置类 springcloud: admin-url: http://127.0.0.1:9095 # Soul Admin 地址 context-path: /sc-user-service-api # 设置在 Soul 网关的路由前缀,例如说 /order、/product 等等。 # 后续,网关会根据该 context-path 来进行路由 app-name: sc-user-service # 应用名。未配置情况下,默认使用 `spring.application.name` 配置项 ================================================ FILE: lab-60/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-60 pom lab-60-soul-dubbo-demo lab-60-soul-spring-boot-demo lab-60-soul-spring-cloud-demo ================================================ FILE: lab-60/《芋道 Soul 极简入门(国产微服务网关)》.md ================================================ ================================================ FILE: lab-61/lab-61-cat-opentracing/pom.xml ================================================ lab-61 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-61-cat-opentracing 1.8 1.8 com.dianping.cat cat-client 3.0.0 io.opentracing opentracing-api 0.33.0 central Maven2 Central Repository default http://repo1.maven.org/maven2 unidal.releases http://unidal.org/nexus/content/repositories/releases/ ================================================ FILE: lab-61/lab-61-cat-opentracing/src/main/java/cn/iocoder/springboot/lab61/cat/opentracing/CatSpan.java ================================================ package cn.iocoder.springboot.lab61.cat.opentracing; import com.dianping.cat.message.Transaction; import io.opentracing.Span; import io.opentracing.SpanContext; import io.opentracing.tag.Tag; import java.util.Map; public class CatSpan implements Span { private Transaction transaction; public CatSpan(Transaction transaction) { this.transaction = transaction; } @Override public SpanContext context() { return null; } @Override public Span setTag(String key, String value) { return null; } @Override public Span setTag(String key, boolean value) { return null; } @Override public Span setTag(String key, Number value) { return null; } @Override public Span setTag(Tag tag, T value) { return null; } @Override public Span log(Map fields) { return null; } @Override public Span log(long timestampMicroseconds, Map fields) { return null; } @Override public Span log(String event) { return null; } @Override public Span log(long timestampMicroseconds, String event) { return null; } @Override public Span setBaggageItem(String key, String value) { return null; } @Override public String getBaggageItem(String key) { return null; } @Override public Span setOperationName(String operationName) { return null; } @Override public void finish() { transaction.setStatus(Transaction.SUCCESS); transaction.complete(); } @Override public void finish(long finishMicros) { } } ================================================ FILE: lab-61/lab-61-cat-opentracing/src/main/java/cn/iocoder/springboot/lab61/cat/opentracing/CatSpanBuilder.java ================================================ package cn.iocoder.springboot.lab61.cat.opentracing; import com.dianping.cat.Cat; import com.dianping.cat.message.Transaction; import io.opentracing.Span; import io.opentracing.SpanContext; import io.opentracing.Tracer; import io.opentracing.tag.Tag; import java.util.LinkedList; import java.util.List; public class CatSpanBuilder implements Tracer.SpanBuilder { private List tags = new LinkedList<>(); private String operationName; public CatSpanBuilder(String operationName) { this.operationName = operationName; } public Tracer.SpanBuilder asChildOf(SpanContext parent) { return null; } public Tracer.SpanBuilder asChildOf(Span parent) { return null; } public Tracer.SpanBuilder addReference(String referenceType, SpanContext referencedContext) { return null; } public Tracer.SpanBuilder ignoreActiveSpan() { return null; } public Tracer.SpanBuilder withTag(String key, String value) { tags.add(new cn.iocoder.springboot.lab61.cat.opentracing.Tag(key, value)); return this; } public Tracer.SpanBuilder withTag(String key, boolean value) { return null; } public Tracer.SpanBuilder withTag(String key, Number value) { return null; } public Tracer.SpanBuilder withTag(Tag tag, T value) { return null; } public Tracer.SpanBuilder withStartTimestamp(long microseconds) { return null; } public Span start() { Transaction transaction = Cat.newTransaction("Opentracing", operationName); // TODO tag 的处理 return new CatSpan(transaction); } } ================================================ FILE: lab-61/lab-61-cat-opentracing/src/main/java/cn/iocoder/springboot/lab61/cat/opentracing/CatTracer.java ================================================ package cn.iocoder.springboot.lab61.cat.opentracing; import io.opentracing.*; import io.opentracing.propagation.Format; public class CatTracer implements Tracer { public ScopeManager scopeManager() { return null; } public Span activeSpan() { return null; } public Scope activateSpan(Span span) { return null; } public SpanBuilder buildSpan(String operationName) { return new CatSpanBuilder(operationName); } public void inject(SpanContext spanContext, Format format, C carrier) { } public SpanContext extract(Format format, C carrier) { return null; } public void close() { } } ================================================ FILE: lab-61/lab-61-cat-opentracing/src/main/java/cn/iocoder/springboot/lab61/cat/opentracing/Tag.java ================================================ package cn.iocoder.springboot.lab61.cat.opentracing; public class Tag { private String key; private String value; public Tag(String key, String value) { this.key = key; this.value = value; } public String getKey() { return key; } public String getValue() { return value; } } ================================================ FILE: lab-61/lab-61-cat-opentracing-demo/pom.xml ================================================ lab-61 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-61-cat-opentracing-demo 1.8 1.8 2.2.4.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.boot spring-boot-starter-web com.dianping.cat cat-client 3.0.0 cn.iocoder.springboot.labs lab-61-cat-opentracing 1.0-SNAPSHOT central Maven2 Central Repository default http://repo1.maven.org/maven2 unidal.releases http://unidal.org/nexus/content/repositories/releases/ ================================================ FILE: lab-61/lab-61-cat-opentracing-demo/src/main/java/catdemo/Application.java ================================================ package catdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-61/lab-61-cat-opentracing-demo/src/main/java/catdemo/config/ZipkinConfig.java ================================================ package catdemo.config; import cn.iocoder.springboot.lab61.cat.opentracing.CatTracer; import io.opentracing.Tracer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class ZipkinConfig { @Bean public Tracer openTracer() { return new CatTracer(); } } ================================================ FILE: lab-61/lab-61-cat-opentracing-demo/src/main/java/catdemo/controller/DemoController.java ================================================ package catdemo.controller; import com.dianping.cat.Cat; import io.opentracing.Span; import io.opentracing.Tracer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @Autowired private Tracer tracer; /** * 监控模型 Transaction 的示例 */ @GetMapping("/test") public String test() { // 创建一个 Span Span parentSpan = tracer.buildSpan("parent").start(); Span childSpan = tracer.buildSpan("child").start(); childSpan.finish(); Cat.logEvent("测试事件", "123"); // 结束一个 Span parentSpan.finish(); // 返回 return "opentracing"; } } ================================================ FILE: lab-61/lab-61-cat-opentracing-demo/src/main/resources/META-INF/app.properties ================================================ app.name=demo-application ================================================ FILE: lab-61/lab-61-demo/pom.xml ================================================ lab-61 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-61-demo 1.8 1.8 2.2.4.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.boot spring-boot-starter-web com.dianping.cat cat-client 3.0.0 central Maven2 Central Repository default http://repo1.maven.org/maven2 unidal.releases http://unidal.org/nexus/content/repositories/releases/ ================================================ FILE: lab-61/lab-61-demo/src/main/java/cn/iocoder/springboot/lab61/catdemo/Application.java ================================================ package cn.iocoder.springboot.lab61.catdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-61/lab-61-demo/src/main/java/cn/iocoder/springboot/lab61/catdemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab61.catdemo.controller; import com.dianping.cat.Cat; import com.dianping.cat.message.Event; import com.dianping.cat.message.Transaction; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { /** * 监控模型 Transaction 的示例 */ @GetMapping("/transaction") public String transaction() { // 创建 Transaction 对象 Transaction t = Cat.newTransaction("URL", "/demo/transaction"); try { // ... yourBusiness(); 业务逻辑 // 设置 Transaction 的状态为成功 t.setStatus(Transaction.SUCCESS); } catch (Exception e) { // 设置 Transaction 的状态为异常 t.setStatus(e); } finally { // 标记 Transaction 结束 t.complete(); } return "success"; } /** * 监控模型 Event 的示例 01 */ @GetMapping("/event-01") public String event01() { // Cat.logEvent("URL.Server", "127.0.0.1"); Cat.logEvent("URL.Server", "127.0.0.1", Event.SUCCESS, "data"); return "success"; } /** * 监控模型 Event 的示例 02 */ @GetMapping("/event-02") public String event02() { try { int result = 1 / 0; } catch (Throwable e) { Cat.logError(e); // Cat.logError("custom-message", e); } return "success"; } /** * 监控模型 Event 的示例 03 */ @GetMapping("/event-03") public String event03() { try { int result = 1 / 0; } catch (Throwable e) { Cat.logErrorWithCategory("custom-category", e); // Cat.logErrorWithCategory("custom-category", "custom-message", e); } return "success"; } /** * 监控模型 Metric 示例 01 */ @GetMapping("/metric-01") public String metric01() { Cat.logMetricForCount("visit.count", 1); return "success"; } /** * 监控模型 Metric 示例 02 */ @GetMapping("/metric-02") public String metric02() { Cat.logMetricForDuration("visit.duration", 10L); return "success"; } } ================================================ FILE: lab-61/lab-61-demo/src/main/resources/META-INF/app.properties ================================================ app.name=demo-application ================================================ FILE: lab-61/lab-61-logback/pom.xml ================================================ lab-61 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-61-logback 1.8 1.8 2.2.4.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.boot spring-boot-starter-web com.dianping.cat cat-client 3.0.0 central Maven2 Central Repository default http://repo1.maven.org/maven2 unidal.releases http://unidal.org/nexus/content/repositories/releases/ ================================================ FILE: lab-61/lab-61-logback/src/main/java/cn/iocoder/springboot/lab61/catdemo/Application.java ================================================ package cn.iocoder.springboot.lab61.catdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-61/lab-61-logback/src/main/java/cn/iocoder/springboot/lab61/catdemo/controller/LoggerController.java ================================================ package cn.iocoder.springboot.lab61.catdemo.controller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/logger") public class LoggerController { private Logger logger = LoggerFactory.getLogger(getClass()); @GetMapping("/error") public String error() { try { int result = 1 / 0; } catch (Throwable e) { logger.error("计算异常", e); } return "success"; } } ================================================ FILE: lab-61/lab-61-logback/src/main/resources/META-INF/app.properties ================================================ app.name=demo-application ================================================ FILE: lab-61/lab-61-logback/src/main/resources/logback-spring.xml ================================================ ================================================ FILE: lab-61/lab-61-springmvc/pom.xml ================================================ lab-61 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-61-springmvc 1.8 1.8 2.2.4.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.boot spring-boot-starter-web com.dianping.cat cat-client 3.0.0 central Maven2 Central Repository default http://repo1.maven.org/maven2 unidal.releases http://unidal.org/nexus/content/repositories/releases/ ================================================ FILE: lab-61/lab-61-springmvc/src/main/java/cn/iocoder/springboot/lab61/catdemo/Application.java ================================================ package cn.iocoder.springboot.lab61.catdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-61/lab-61-springmvc/src/main/java/cn/iocoder/springboot/lab61/catdemo/config/CatFilterConfigure.java ================================================ package cn.iocoder.springboot.lab61.catdemo.config; import com.dianping.cat.servlet.CatFilter; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class CatFilterConfigure { @Bean public FilterRegistrationBean catFilter() { // 创建 CatFilter 对象 CatFilter filter = new CatFilter(); // 创建 FilterRegistrationBean 对象 FilterRegistrationBean registration = new FilterRegistrationBean<>(); registration.setFilter(filter); registration.addUrlPatterns("/*"); // 匹配所有 URL registration.setName("cat-filter"); registration.setOrder(1); return registration; } } ================================================ FILE: lab-61/lab-61-springmvc/src/main/java/cn/iocoder/springboot/lab61/catdemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab61.catdemo.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @GetMapping("/hello") public String hello() { return "world"; } } ================================================ FILE: lab-61/lab-61-springmvc/src/main/resources/META-INF/app.properties ================================================ app.name=demo-application ================================================ FILE: lab-61/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-61 pom lab-61-demo lab-61-logback lab-61-springmvc lab-61-cat-opentracing lab-61-cat-opentracing-demo ================================================ FILE: lab-61/《芋道 Spring Boot 监控平台 CAT 入门》.md ================================================ ================================================ FILE: lab-62/lab-62-sofarpc-annotations-demo/lab-62-sofarpc-annotations-demo-user-rpc-service-api/pom.xml ================================================ lab-62-sofarpc-xml-demo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-62-sofarpc-annotations-demo-user-rpc-service-api ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab62.rpc.api; import cn.iocoder.springboot.lab62.rpc.dto.UserAddDTO; import cn.iocoder.springboot.lab62.rpc.dto.UserDTO; /** * 用户服务 RPC Service 接口 */ public interface UserRpcService { /** * 根据指定用户编号,获得用户信息 * * @param id 用户编号 * @return 用户信息 */ UserDTO get(Integer id); /** * 添加新用户,返回新添加的用户编号 * * @param addDTO 添加的用户信息 * @return 用户编号 */ Integer add(UserAddDTO addDTO); } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab62.rpc.dto; import java.io.Serializable; /** * 用户添加 DTO */ public class UserAddDTO implements Serializable { /** * 昵称 */ private String name; /** * 性别 */ private Integer gender; public String getName() { return name; } public UserAddDTO setName(String name) { this.name = name; return this; } public Integer getGender() { return gender; } public UserAddDTO setGender(Integer gender) { this.gender = gender; return this; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab62.rpc.dto; import java.io.Serializable; /** * 用户信息 DTO */ public class UserDTO implements Serializable { /** * 用户编号 */ private Integer id; /** * 昵称 */ private String name; /** * 性别 */ private Integer gender; public Integer getId() { return id; } public UserDTO setId(Integer id) { this.id = id; return this; } public String getName() { return name; } public UserDTO setName(String name) { this.name = name; return this; } public Integer getGender() { return gender; } public UserDTO setGender(Integer gender) { this.gender = gender; return this; } } ================================================ FILE: lab-62/lab-62-sofarpc-annotations-demo/lab-62-sofarpc-annotations-demo-user-rpc-service-consumer/pom.xml ================================================ com.alipay.sofa sofaboot-dependencies 3.3.2 4.0.0 lab-62-sofarpc-annotations-demo-user-rpc-service-consumer cn.iocoder.springboot.labs lab-62-sofarpc-annotations-demo-user-rpc-service-api 1.0-SNAPSHOT org.springframework.boot spring-boot-starter-web com.alipay.sofa rpc-sofa-boot-starter org.apache.curator curator-framework ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab62.rpc; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication //@ImportResource("classpath:sofarpc.xml") public class ConsumerApplication { public static void main(String[] args) { // 启动 Spring Boot 应用 SpringApplication.run(ConsumerApplication.class, args);; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab62.rpc.controller; import cn.iocoder.springboot.lab62.rpc.api.UserRpcService; import cn.iocoder.springboot.lab62.rpc.dto.UserAddDTO; import cn.iocoder.springboot.lab62.rpc.dto.UserDTO; import com.alipay.sofa.runtime.api.annotation.SofaReference; import com.alipay.sofa.runtime.api.annotation.SofaReferenceBinding; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/user") public class UserController { @SofaReference(binding = @SofaReferenceBinding(bindingType = "bolt")) private UserRpcService userRpcService; @GetMapping("/get") public UserDTO get(@RequestParam("id") Integer id) { return userRpcService.get(id); } @GetMapping("/add") // 为了方便测试,实际使用 @PostMapping public Integer add(@RequestParam("name") String name, @RequestParam("gender") Integer gender) { UserAddDTO addDTO = new UserAddDTO().setName(name).setGender(gender); return userRpcService.add(addDTO); } } ================================================ FILE: lab-62/lab-62-sofarpc-annotations-demo/lab-62-sofarpc-annotations-demo-user-rpc-service-consumer/src/main/resources/application.yaml ================================================ spring: application: name: user-service-consumer # 应用名 com.alipay.sofa.rpc.registry.address: zookeeper://127.0.0.1:2181 # SOFARPC 注解中心 ================================================ FILE: lab-62/lab-62-sofarpc-annotations-demo/lab-62-sofarpc-annotations-demo-user-rpc-service-provider/pom.xml ================================================ com.alipay.sofa sofaboot-dependencies 3.4.0 4.0.0 lab-62-sofarpc-annotations-demo-user-rpc-service-provider cn.iocoder.springboot.labs lab-62-sofarpc-annotations-demo-user-rpc-service-api 1.0-SNAPSHOT org.springframework.boot spring-boot-starter com.alipay.sofa rpc-sofa-boot-starter org.apache.curator curator-framework ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab62.rpc; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication //@ImportResource("classpath:sofarpc.xml") public class ProviderApplication { public static void main(String[] args) { // 启动 Spring Boot 应用 SpringApplication.run(ProviderApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab62.rpc.service; import cn.iocoder.springboot.lab62.rpc.api.UserRpcService; import cn.iocoder.springboot.lab62.rpc.dto.UserAddDTO; import cn.iocoder.springboot.lab62.rpc.dto.UserDTO; import com.alipay.sofa.runtime.api.annotation.SofaService; import com.alipay.sofa.runtime.api.annotation.SofaServiceBinding; import org.springframework.stereotype.Service; @Service @SofaService(bindings = @SofaServiceBinding(bindingType = "bolt")) public class UserRpcServiceImpl implements UserRpcService { @Override public UserDTO get(Integer id) { return new UserDTO().setId(id) .setName("没有昵称:" + id) .setGender(id % 2 + 1); // 1 - 男;2 - 女 } @Override public Integer add(UserAddDTO addDTO) { return (int) (System.currentTimeMillis() / 1000); // 嘿嘿,随便返回一个 id } } ================================================ FILE: lab-62/lab-62-sofarpc-annotations-demo/lab-62-sofarpc-annotations-demo-user-rpc-service-provider/src/main/resources/application.yaml ================================================ spring: application: name: user-service-provider # 应用名 com.alipay.sofa.rpc.registry.address: zookeeper://127.0.0.1:2181 # SOFA RPC 注册中心 ================================================ FILE: lab-62/lab-62-sofarpc-annotations-demo/pom.xml ================================================ lab-62 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-62-sofarpc-annotations-demo pom lab-62-sofarpc-annotations-demo-user-rpc-service-api lab-62-sofarpc-annotations-demo-user-rpc-service-provider lab-62-sofarpc-annotations-demo-user-rpc-service-consumer ================================================ FILE: lab-62/lab-62-sofarpc-xml-demo/lab-62-sofarpc-xml-demo-user-rpc-service-api/pom.xml ================================================ lab-62-sofarpc-xml-demo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-62-sofarpc-xml-demo-user-rpc-service-api ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab62.rpc.api; import cn.iocoder.springboot.lab62.rpc.dto.UserAddDTO; import cn.iocoder.springboot.lab62.rpc.dto.UserDTO; /** * 用户服务 RPC Service 接口 */ public interface UserRpcService { /** * 根据指定用户编号,获得用户信息 * * @param id 用户编号 * @return 用户信息 */ UserDTO get(Integer id); /** * 添加新用户,返回新添加的用户编号 * * @param addDTO 添加的用户信息 * @return 用户编号 */ Integer add(UserAddDTO addDTO); } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab62.rpc.dto; import java.io.Serializable; /** * 用户添加 DTO */ public class UserAddDTO implements Serializable { /** * 昵称 */ private String name; /** * 性别 */ private Integer gender; public String getName() { return name; } public UserAddDTO setName(String name) { this.name = name; return this; } public Integer getGender() { return gender; } public UserAddDTO setGender(Integer gender) { this.gender = gender; return this; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab62.rpc.dto; import java.io.Serializable; /** * 用户信息 DTO */ public class UserDTO implements Serializable { /** * 用户编号 */ private Integer id; /** * 昵称 */ private String name; /** * 性别 */ private Integer gender; public Integer getId() { return id; } public UserDTO setId(Integer id) { this.id = id; return this; } public String getName() { return name; } public UserDTO setName(String name) { this.name = name; return this; } public Integer getGender() { return gender; } public UserDTO setGender(Integer gender) { this.gender = gender; return this; } } ================================================ FILE: lab-62/lab-62-sofarpc-xml-demo/lab-62-sofarpc-xml-demo-user-rpc-service-consumer/pom.xml ================================================ com.alipay.sofa sofaboot-dependencies 3.3.2 4.0.0 lab-62-sofarpc-xml-demo-user-rpc-service-consumer cn.iocoder.springboot.labs lab-62-sofarpc-xml-demo-user-rpc-service-api 1.0-SNAPSHOT org.springframework.boot spring-boot-starter-web com.alipay.sofa rpc-sofa-boot-starter org.apache.curator curator-framework ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab62.rpc; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.ImportResource; @SpringBootApplication @ImportResource("classpath:sofarpc.xml") public class ConsumerApplication { public static void main(String[] args) { // 启动 Spring Boot 应用 SpringApplication.run(ConsumerApplication.class, args);; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab62.rpc.controller; import cn.iocoder.springboot.lab62.rpc.api.UserRpcService; import cn.iocoder.springboot.lab62.rpc.dto.UserAddDTO; import cn.iocoder.springboot.lab62.rpc.dto.UserDTO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/user") public class UserController { @Autowired private UserRpcService userRpcService; @GetMapping("/get") public UserDTO get(@RequestParam("id") Integer id) { return userRpcService.get(id); } @GetMapping("/add") // 为了方便测试,实际使用 @PostMapping public Integer add(@RequestParam("name") String name, @RequestParam("gender") Integer gender) { UserAddDTO addDTO = new UserAddDTO().setName(name).setGender(gender); return userRpcService.add(addDTO); } } ================================================ FILE: lab-62/lab-62-sofarpc-xml-demo/lab-62-sofarpc-xml-demo-user-rpc-service-consumer/src/main/resources/application.yaml ================================================ spring: application: name: user-service-consumer # 应用名 com.alipay.sofa.rpc.registry.address: zookeeper://127.0.0.1:2181 # SOFARPC 注解中心 ================================================ FILE: lab-62/lab-62-sofarpc-xml-demo/lab-62-sofarpc-xml-demo-user-rpc-service-consumer/src/main/resources/sofarpc.xml ================================================ ================================================ FILE: lab-62/lab-62-sofarpc-xml-demo/lab-62-sofarpc-xml-demo-user-rpc-service-provider/pom.xml ================================================ com.alipay.sofa sofaboot-dependencies 3.4.0 4.0.0 lab-62-sofarpc-xml-demo-user-rpc-service-provider cn.iocoder.springboot.labs lab-62-sofarpc-xml-demo-user-rpc-service-api 1.0-SNAPSHOT org.springframework.boot spring-boot-starter com.alipay.sofa rpc-sofa-boot-starter org.apache.curator curator-framework ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab62.rpc; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.ImportResource; @SpringBootApplication @ImportResource("classpath:sofarpc.xml") public class ProviderApplication { public static void main(String[] args) { // 启动 Spring Boot 应用 SpringApplication.run(ProviderApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab62.rpc.service; import cn.iocoder.springboot.lab62.rpc.api.UserRpcService; import cn.iocoder.springboot.lab62.rpc.dto.UserAddDTO; import cn.iocoder.springboot.lab62.rpc.dto.UserDTO; import org.springframework.stereotype.Service; @Service public class UserRpcServiceImpl implements UserRpcService { @Override public UserDTO get(Integer id) { return new UserDTO().setId(id) .setName("没有昵称:" + id) .setGender(id % 2 + 1); // 1 - 男;2 - 女 } @Override public Integer add(UserAddDTO addDTO) { return (int) (System.currentTimeMillis() / 1000); // 嘿嘿,随便返回一个 id } } ================================================ FILE: lab-62/lab-62-sofarpc-xml-demo/lab-62-sofarpc-xml-demo-user-rpc-service-provider/src/main/resources/application.yaml ================================================ spring: application: name: user-service-provider # 应用名 com.alipay.sofa.rpc.registry.address: zookeeper://127.0.0.1:2181 # SOFA RPC 注册中心 ================================================ FILE: lab-62/lab-62-sofarpc-xml-demo/lab-62-sofarpc-xml-demo-user-rpc-service-provider/src/main/resources/sofarpc.xml ================================================ ================================================ FILE: lab-62/lab-62-sofarpc-xml-demo/pom.xml ================================================ lab-62 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-62-sofarpc-xml-demo pom lab-62-sofarpc-xml-demo-user-rpc-service-api lab-62-sofarpc-xml-demo-user-rpc-service-provider lab-62-sofarpc-xml-demo-user-rpc-service-consumer ================================================ FILE: lab-62/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-62 pom lab-62-sofarpc-xml-demo lab-62-sofarpc-annotations-demo ================================================ FILE: lab-62/《芋道 Spring Boot SOFARPC 入门》.md ================================================ ================================================ FILE: lab-63/lab-63-motan-annotations-demo/lab-63-motan-annotations-demo-user-rpc-service-api/pom.xml ================================================ lab-63-motan-annotations-demo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-63-motan-annotations-demo-user-rpc-service-api ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab63.rpc.api; import cn.iocoder.springboot.lab63.rpc.dto.UserAddDTO; import cn.iocoder.springboot.lab63.rpc.dto.UserDTO; /** * 用户服务 RPC Service 接口 */ public interface UserRpcService { /** * 根据指定用户编号,获得用户信息 * * @param id 用户编号 * @return 用户信息 */ UserDTO get(Integer id); /** * 添加新用户,返回新添加的用户编号 * * @param addDTO 添加的用户信息 * @return 用户编号 */ Integer add(UserAddDTO addDTO); } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab63.rpc.dto; import java.io.Serializable; /** * 用户添加 DTO */ public class UserAddDTO implements Serializable { /** * 昵称 */ private String name; /** * 性别 */ private Integer gender; public String getName() { return name; } public UserAddDTO setName(String name) { this.name = name; return this; } public Integer getGender() { return gender; } public UserAddDTO setGender(Integer gender) { this.gender = gender; return this; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab63.rpc.dto; import java.io.Serializable; /** * 用户信息 DTO */ public class UserDTO implements Serializable { /** * 用户编号 */ private Integer id; /** * 昵称 */ private String name; /** * 性别 */ private Integer gender; public Integer getId() { return id; } public UserDTO setId(Integer id) { this.id = id; return this; } public String getName() { return name; } public UserDTO setName(String name) { this.name = name; return this; } public Integer getGender() { return gender; } public UserDTO setGender(Integer gender) { this.gender = gender; return this; } } ================================================ FILE: lab-63/lab-63-motan-annotations-demo/lab-63-motan-annotations-demo-user-rpc-service-consumer/pom.xml ================================================ lab-63-motan-annotations-demo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-63-motan-annotations-demo-user-rpc-service-consumer 1.8 1.8 2.2.4.RELEASE 1.1.8 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import cn.iocoder.springboot.labs lab-63-motan-annotations-demo-user-rpc-service-api 1.0-SNAPSHOT org.springframework.boot spring-boot-starter-web com.weibo motan-core ${moton.version} com.weibo motan-transport-netty4 ${moton.version} com.weibo motan-registry-zookeeper ${moton.version} com.weibo motan-springsupport ${moton.version} ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab63.rpc; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.ImportResource; @SpringBootApplication @ImportResource("classpath:motan.xml") public class ConsumerApplication { public static void main(String[] args) { // 启动 Spring Boot 应用 SpringApplication.run(ConsumerApplication.class, args);; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab63.rpc.controller; import cn.iocoder.springboot.lab63.rpc.api.UserRpcService; import cn.iocoder.springboot.lab63.rpc.dto.UserAddDTO; import cn.iocoder.springboot.lab63.rpc.dto.UserDTO; import com.weibo.api.motan.config.springsupport.annotation.MotanReferer; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/user") public class UserController { // @Autowired @MotanReferer private UserRpcService userRpcService; @GetMapping("/get") public UserDTO get(@RequestParam("id") Integer id) { return userRpcService.get(id); } @GetMapping("/add") // 为了方便测试,实际使用 @PostMapping public Integer add(@RequestParam("name") String name, @RequestParam("gender") Integer gender) { UserAddDTO addDTO = new UserAddDTO().setName(name).setGender(gender); return userRpcService.add(addDTO); } } ================================================ FILE: lab-63/lab-63-motan-annotations-demo/lab-63-motan-annotations-demo-user-rpc-service-consumer/src/main/resources/motan.xml ================================================ ================================================ FILE: lab-63/lab-63-motan-annotations-demo/lab-63-motan-annotations-demo-user-rpc-service-provider/pom.xml ================================================ lab-63-motan-annotations-demo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-63-motan-annotations-demo-user-rpc-service-provider 1.8 1.8 2.2.4.RELEASE 1.1.8 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import cn.iocoder.springboot.labs lab-63-motan-annotations-demo-user-rpc-service-api 1.0-SNAPSHOT org.springframework.boot spring-boot-starter com.weibo motan-core ${moton.version} com.weibo motan-transport-netty4 ${moton.version} com.weibo motan-registry-zookeeper ${moton.version} com.weibo motan-springsupport ${moton.version} ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab63.rpc; import com.weibo.api.motan.common.MotanConstants; import com.weibo.api.motan.util.MotanSwitcherUtil; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.ImportResource; @SpringBootApplication @ImportResource("classpath:motan.xml") public class ProviderApplication { public static void main(String[] args) { // 启动 Spring Boot 应用 SpringApplication.run(ProviderApplication.class, args); // 设置 Motan 开启对外服务 MotanSwitcherUtil.setSwitcherValue(MotanConstants.REGISTRY_HEARTBEAT_SWITCHER, true); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab63.rpc.service; import cn.iocoder.springboot.lab63.rpc.api.UserRpcService; import cn.iocoder.springboot.lab63.rpc.dto.UserAddDTO; import cn.iocoder.springboot.lab63.rpc.dto.UserDTO; import com.weibo.api.motan.config.springsupport.annotation.MotanService; import org.springframework.stereotype.Service; @Service @MotanService(export = "motan2:8001") public class UserRpcServiceImpl implements UserRpcService { @Override public UserDTO get(Integer id) { return new UserDTO().setId(id) .setName("没有昵称:" + id) .setGender(id % 2 + 1); // 1 - 男;2 - 女 } @Override public Integer add(UserAddDTO addDTO) { return (int) (System.currentTimeMillis() / 1000); // 嘿嘿,随便返回一个 id } } ================================================ FILE: lab-63/lab-63-motan-annotations-demo/lab-63-motan-annotations-demo-user-rpc-service-provider/src/main/resources/motan.xml ================================================ ================================================ FILE: lab-63/lab-63-motan-annotations-demo/pom.xml ================================================ lab-63 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-63-motan-annotations-demo pom lab-63-motan-annotations-demo-user-rpc-service-api lab-63-motan-annotations-demo-user-rpc-service-provider lab-63-motan-annotations-demo-user-rpc-service-consumer ================================================ FILE: lab-63/lab-63-motan-xml-demo/lab-63-motan-xml-demo-user-rpc-service-api/pom.xml ================================================ lab-63-motan-xml-demo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-63-motan-xml-demo-user-rpc-service-api ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab63.rpc.api; import cn.iocoder.springboot.lab63.rpc.dto.UserAddDTO; import cn.iocoder.springboot.lab63.rpc.dto.UserDTO; /** * 用户服务 RPC Service 接口 */ public interface UserRpcService { /** * 根据指定用户编号,获得用户信息 * * @param id 用户编号 * @return 用户信息 */ UserDTO get(Integer id); /** * 添加新用户,返回新添加的用户编号 * * @param addDTO 添加的用户信息 * @return 用户编号 */ Integer add(UserAddDTO addDTO); } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab63.rpc.dto; import java.io.Serializable; /** * 用户添加 DTO */ public class UserAddDTO implements Serializable { /** * 昵称 */ private String name; /** * 性别 */ private Integer gender; public String getName() { return name; } public UserAddDTO setName(String name) { this.name = name; return this; } public Integer getGender() { return gender; } public UserAddDTO setGender(Integer gender) { this.gender = gender; return this; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab63.rpc.dto; import java.io.Serializable; /** * 用户信息 DTO */ public class UserDTO implements Serializable { /** * 用户编号 */ private Integer id; /** * 昵称 */ private String name; /** * 性别 */ private Integer gender; public Integer getId() { return id; } public UserDTO setId(Integer id) { this.id = id; return this; } public String getName() { return name; } public UserDTO setName(String name) { this.name = name; return this; } public Integer getGender() { return gender; } public UserDTO setGender(Integer gender) { this.gender = gender; return this; } } ================================================ FILE: lab-63/lab-63-motan-xml-demo/lab-63-motan-xml-demo-user-rpc-service-consumer/pom.xml ================================================ lab-63-motan-xml-demo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-63-motan-xml-demo-user-rpc-service-consumer 1.8 1.8 2.2.4.RELEASE 1.1.8 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import cn.iocoder.springboot.labs lab-63-motan-xml-demo-user-rpc-service-api 1.0-SNAPSHOT org.springframework.boot spring-boot-starter-web com.weibo motan-core ${moton.version} com.weibo motan-transport-netty4 ${moton.version} com.weibo motan-registry-zookeeper ${moton.version} com.weibo motan-springsupport ${moton.version} ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab63.rpc; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.ImportResource; @SpringBootApplication @ImportResource("classpath:motan.xml") public class ConsumerApplication { public static void main(String[] args) { // 启动 Spring Boot 应用 SpringApplication.run(ConsumerApplication.class, args);; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab63.rpc.controller; import cn.iocoder.springboot.lab63.rpc.api.UserRpcService; import cn.iocoder.springboot.lab63.rpc.dto.UserAddDTO; import cn.iocoder.springboot.lab63.rpc.dto.UserDTO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/user") public class UserController { @Autowired private UserRpcService userRpcService; @GetMapping("/get") public UserDTO get(@RequestParam("id") Integer id) { return userRpcService.get(id); } @GetMapping("/add") // 为了方便测试,实际使用 @PostMapping public Integer add(@RequestParam("name") String name, @RequestParam("gender") Integer gender) { UserAddDTO addDTO = new UserAddDTO().setName(name).setGender(gender); return userRpcService.add(addDTO); } } ================================================ FILE: lab-63/lab-63-motan-xml-demo/lab-63-motan-xml-demo-user-rpc-service-consumer/src/main/resources/motan.xml ================================================ ================================================ FILE: lab-63/lab-63-motan-xml-demo/lab-63-motan-xml-demo-user-rpc-service-provider/pom.xml ================================================ lab-63-motan-xml-demo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-63-motan-xml-demo-user-rpc-service-provider 1.8 1.8 2.2.4.RELEASE 1.1.8 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import cn.iocoder.springboot.labs lab-63-motan-xml-demo-user-rpc-service-api 1.0-SNAPSHOT org.springframework.boot spring-boot-starter com.weibo motan-core ${moton.version} com.weibo motan-transport-netty4 ${moton.version} com.weibo motan-registry-zookeeper ${moton.version} com.weibo motan-springsupport ${moton.version} ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab63.rpc; import com.weibo.api.motan.common.MotanConstants; import com.weibo.api.motan.util.MotanSwitcherUtil; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.ImportResource; @SpringBootApplication @ImportResource("classpath:motan.xml") public class ProviderApplication { public static void main(String[] args) { // 启动 Spring Boot 应用 SpringApplication.run(ProviderApplication.class, args); // 设置 Motan 开启对外服务 MotanSwitcherUtil.setSwitcherValue(MotanConstants.REGISTRY_HEARTBEAT_SWITCHER, true); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab63.rpc.service; import cn.iocoder.springboot.lab63.rpc.api.UserRpcService; import cn.iocoder.springboot.lab63.rpc.dto.UserAddDTO; import cn.iocoder.springboot.lab63.rpc.dto.UserDTO; import org.springframework.stereotype.Service; @Service public class UserRpcServiceImpl implements UserRpcService { @Override public UserDTO get(Integer id) { return new UserDTO().setId(id) .setName("没有昵称:" + id) .setGender(id % 2 + 1); // 1 - 男;2 - 女 } @Override public Integer add(UserAddDTO addDTO) { return (int) (System.currentTimeMillis() / 1000); // 嘿嘿,随便返回一个 id } } ================================================ FILE: lab-63/lab-63-motan-xml-demo/lab-63-motan-xml-demo-user-rpc-service-provider/src/main/resources/motan.xml ================================================ ================================================ FILE: lab-63/lab-63-motan-xml-demo/pom.xml ================================================ lab-63 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-63-motan-xml-demo pom lab-63-motan-xml-demo-user-rpc-service-api lab-63-motan-xml-demo-user-rpc-service-provider lab-63-motan-xml-demo-user-rpc-service-consumer ================================================ FILE: lab-63/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-63 lab-63-motan-xml-demo lab-63-motan-annotations-demo ================================================ FILE: lab-63/《芋道 Spring Boot Motan 入门》.md ================================================ ================================================ FILE: lab-64/lab-64-grpc-demo/lab-64-grpc-demo-application/pom.xml ================================================ lab-64-grpc-demo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-64-grpc-demo-application 1.23.0 2.2.4.RELEASE 1.8 1.8 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import cn.iocoder.springboot.labs lab-64-grpc-demo-user-service-api 1.0-SNAPSHOT org.springframework.boot spring-boot-starter-web io.grpc grpc-netty ${io.grpc.version} ================================================ FILE: lab-64/lab-64-grpc-demo/lab-64-grpc-demo-application/src/main/java/cn/iocoder/springboot/lab64/demo/DemoApplication.java ================================================ package cn.iocoder.springboot.lab64.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { // 启动 Spring Boot 应用 SpringApplication.run(DemoApplication.class, args); } } ================================================ FILE: lab-64/lab-64-grpc-demo/lab-64-grpc-demo-application/src/main/java/cn/iocoder/springboot/lab64/demo/config/GrpcConfig.java ================================================ package cn.iocoder.springboot.lab64.demo.config; import cn.iocoder.springboot.lab64.userservice.api.UserServiceGrpc; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class GrpcConfig { private static final Integer GRPC_PORT = 8888; @Bean public ManagedChannel userGrpcManagedChannel() { return ManagedChannelBuilder.forAddress("127.0.0.1", GRPC_PORT).usePlaintext().build(); } @Bean public UserServiceGrpc.UserServiceBlockingStub userServiceGrpc() { // 创建 ManagedChannel 对象 ManagedChannel userGrpcManagedChannel = this.userGrpcManagedChannel(); // 创建 UserServiceGrpc 对象 return UserServiceGrpc.newBlockingStub(userGrpcManagedChannel); } } ================================================ FILE: lab-64/lab-64-grpc-demo/lab-64-grpc-demo-application/src/main/java/cn/iocoder/springboot/lab64/demo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab64.demo.controller; import cn.iocoder.springboot.lab64.userservice.api.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @Autowired private UserServiceGrpc.UserServiceBlockingStub userServiceGrpc; @GetMapping("/get") public String get(@RequestParam("id") Integer id) { // 创建请求 UserGetRequest request = UserGetRequest.newBuilder().setId(id).build(); // 执行 gRPC 请求 UserGetResponse response = userServiceGrpc.get(request); // 响应 return response.getName(); } @GetMapping("/create") // 为了方便测试,实际使用 @PostMapping public Integer create(@RequestParam("name") String name, @RequestParam("gender") Integer gender) { // 创建请求 UserCreateRequest request = UserCreateRequest.newBuilder() .setName(name).setGender(gender).build(); // 执行 gRPC 请求 UserCreateResponse response = userServiceGrpc.create(request); // 响应 return response.getId(); } } ================================================ FILE: lab-64/lab-64-grpc-demo/lab-64-grpc-demo-user-service/pom.xml ================================================ lab-64-grpc-demo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-64-grpc-demo-user-service 1.30.0 2.2.4.RELEASE 1.8 1.8 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import cn.iocoder.springboot.labs lab-64-grpc-demo-user-service-api 1.0-SNAPSHOT org.springframework.boot spring-boot-starter io.grpc grpc-netty ${io.grpc.version} ================================================ FILE: lab-64/lab-64-grpc-demo/lab-64-grpc-demo-user-service/src/main/java/cn/iocoder/springboot/lab64/userservice/UserServiceApplication.java ================================================ package cn.iocoder.springboot.lab64.userservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import java.util.concurrent.CountDownLatch; @SpringBootApplication public class UserServiceApplication { public static void main(String[] args) throws InterruptedException { // 启动 Spring Boot 应用 SpringApplication.run(UserServiceApplication.class, args); // 阻塞,避免应用退出 new CountDownLatch(1).await(); } } ================================================ FILE: lab-64/lab-64-grpc-demo/lab-64-grpc-demo-user-service/src/main/java/cn/iocoder/springboot/lab64/userservice/config/GrpcConfig.java ================================================ package cn.iocoder.springboot.lab64.userservice.config; import cn.iocoder.springboot.lab64.userservice.rpc.UserServiceGrpcImpl; import io.grpc.Server; import io.grpc.ServerBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.io.IOException; @Configuration public class GrpcConfig { private final Logger logger = LoggerFactory.getLogger(getClass()); /** * gRPC Server 端口 */ private static final Integer GRPC_PORT = 8888; @Bean public Server grpcServer(final UserServiceGrpcImpl userServiceGrpc) throws IOException { // 创建 gRPC Server 对象 Server server = ServerBuilder.forPort(GRPC_PORT) .addService(userServiceGrpc) .build(); // 启动 gRPC Server server.start(); logger.info("[grpcServer][启动完成,端口为({})]", server.getPort()); return server; } } ================================================ FILE: lab-64/lab-64-grpc-demo/lab-64-grpc-demo-user-service/src/main/java/cn/iocoder/springboot/lab64/userservice/rpc/UserServiceGrpcImpl.java ================================================ package cn.iocoder.springboot.lab64.userservice.rpc; import cn.iocoder.springboot.lab64.userservice.api.*; import io.grpc.stub.StreamObserver; import org.springframework.stereotype.Service; @Service public class UserServiceGrpcImpl extends UserServiceGrpc.UserServiceImplBase { @Override public void get(UserGetRequest request, StreamObserver responseObserver) { // 创建响应对象 UserGetResponse.Builder builder = UserGetResponse.newBuilder(); builder.setId(request.getId()) .setName("没有昵称:" + request.getId()) .setGender(request.getId() % 2 + 1); // 返回响应 responseObserver.onNext(builder.build()); responseObserver.onCompleted(); } @Override public void create(UserCreateRequest request, StreamObserver responseObserver) { // 创建响应对象 UserCreateResponse.Builder builder = UserCreateResponse.newBuilder(); builder.setId((int) (System.currentTimeMillis() / 1000)); // 返回响应 responseObserver.onNext(builder.build()); responseObserver.onCompleted(); } } ================================================ FILE: lab-64/lab-64-grpc-demo/lab-64-grpc-demo-user-service-api/pom.xml ================================================ lab-64-grpc-demo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-64-grpc-demo-user-service-api 1.30.0 1.8 1.8 1.6.2 0.6.1 io.grpc grpc-protobuf ${io.grpc.version} io.grpc grpc-stub ${io.grpc.version} kr.motd.maven os-maven-plugin ${os-maven-plugin.version} org.xolstice.maven.plugins protobuf-maven-plugin ${protobuf-maven-plugin.version} grpc-java com.google.protobuf:protoc:3.9.1:exe:${os.detected.classifier} io.grpc:protoc-gen-grpc-java:${io.grpc.version}:exe:${os.detected.classifier} compile compile-custom ================================================ FILE: lab-64/lab-64-grpc-demo/lab-64-grpc-demo-user-service-api/src/main/proto/UserService.proto ================================================ syntax = "proto3"; option java_multiple_files = true; package cn.iocoder.springboot.lab64.userservice.api; message UserGetRequest { int32 id = 1; } message UserGetResponse { int32 id = 1; string name = 2; int32 gender = 3; } message UserCreateRequest { string name = 1; int32 gender = 2; } message UserCreateResponse { int32 id = 1; } service UserService { rpc get(UserGetRequest) returns (UserGetResponse); rpc create(UserCreateRequest) returns (UserCreateResponse); } ================================================ FILE: lab-64/lab-64-grpc-demo/pom.xml ================================================ lab-64 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-64-grpc-demo pom lab-64-grpc-demo-user-service lab-64-grpc-demo-user-service-api lab-64-grpc-demo-application ================================================ FILE: lab-64/lab-64-grpc-starter/lab-64-grpc-starter-application/pom.xml ================================================ lab-64-grpc-starter cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-64-grpc-starter-application 2.2.4.RELEASE 1.8 1.8 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import cn.iocoder.springboot.labs lab-64-grpc-starter-user-service-api 1.0-SNAPSHOT org.springframework.boot spring-boot-starter-web net.devh grpc-client-spring-boot-starter 2.8.0.RELEASE ================================================ FILE: lab-64/lab-64-grpc-starter/lab-64-grpc-starter-application/src/main/java/cn/iocoder/springboot/lab64/demo/DemoApplication.java ================================================ package cn.iocoder.springboot.lab64.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { // 启动 Spring Boot 应用 SpringApplication.run(DemoApplication.class, args); } } ================================================ FILE: lab-64/lab-64-grpc-starter/lab-64-grpc-starter-application/src/main/java/cn/iocoder/springboot/lab64/demo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab64.demo.controller; import cn.iocoder.springboot.lab64.userservice.api.*; import net.devh.boot.grpc.client.inject.GrpcClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @GrpcClient("userService") private UserServiceGrpc.UserServiceBlockingStub userServiceGrpc; @GetMapping("/get") public String get(@RequestParam("id") Integer id) { // 创建请求 UserGetRequest request = UserGetRequest.newBuilder().setId(id).build(); // 执行 gRPC 请求 UserGetResponse response = userServiceGrpc.get(request); // 响应 return response.getName(); } @GetMapping("/create") // 为了方便测试,实际使用 @PostMapping public Integer create(@RequestParam("name") String name, @RequestParam("gender") Integer gender) { // 创建请求 UserCreateRequest request = UserCreateRequest.newBuilder() .setName(name).setGender(gender).build(); // 执行 gRPC 请求 UserCreateResponse response = userServiceGrpc.create(request); // 响应 return response.getId(); } } ================================================ FILE: lab-64/lab-64-grpc-starter/lab-64-grpc-starter-application/src/main/resources/application.yml ================================================ grpc: # gRPC 客户端配置,对应 GrpcChannelsProperties 配置类的映射 client: userService: address: 'static://127.0.0.1:8888' # 用户服务的地址 negotiation-type: plaintext ================================================ FILE: lab-64/lab-64-grpc-starter/lab-64-grpc-starter-user-service/pom.xml ================================================ lab-64-grpc-starter cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-64-grpc-starter-user-service 2.2.4.RELEASE 1.8 1.8 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import cn.iocoder.springboot.labs lab-64-grpc-starter-user-service-api 1.0-SNAPSHOT org.springframework.boot spring-boot-starter net.devh grpc-server-spring-boot-starter 2.8.0.RELEASE ================================================ FILE: lab-64/lab-64-grpc-starter/lab-64-grpc-starter-user-service/src/main/java/cn/iocoder/springboot/lab64/userservice/UserServiceApplication.java ================================================ package cn.iocoder.springboot.lab64.userservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class UserServiceApplication { public static void main(String[] args) throws InterruptedException { // 启动 Spring Boot 应用 SpringApplication.run(UserServiceApplication.class, args); } } ================================================ FILE: lab-64/lab-64-grpc-starter/lab-64-grpc-starter-user-service/src/main/java/cn/iocoder/springboot/lab64/userservice/rpc/UserServiceGrpcImpl.java ================================================ package cn.iocoder.springboot.lab64.userservice.rpc; import cn.iocoder.springboot.lab64.userservice.api.*; import io.grpc.stub.StreamObserver; import net.devh.boot.grpc.server.service.GrpcService; @GrpcService public class UserServiceGrpcImpl extends UserServiceGrpc.UserServiceImplBase { @Override public void get(UserGetRequest request, StreamObserver responseObserver) { // 创建响应对象 UserGetResponse.Builder builder = UserGetResponse.newBuilder(); builder.setId(request.getId()) .setName("没有昵称:" + request.getId()) .setGender(request.getId() % 2 + 1); // 返回响应 responseObserver.onNext(builder.build()); responseObserver.onCompleted(); } @Override public void create(UserCreateRequest request, StreamObserver responseObserver) { // 创建响应对象 UserCreateResponse.Builder builder = UserCreateResponse.newBuilder(); builder.setId((int) (System.currentTimeMillis() / 1000)); // 返回响应 responseObserver.onNext(builder.build()); responseObserver.onCompleted(); } } ================================================ FILE: lab-64/lab-64-grpc-starter/lab-64-grpc-starter-user-service/src/main/resources/application.yml ================================================ grpc: # gRPC 服务器配置,对应 GrpcServerProperties 配置类 server: port: 8888 ================================================ FILE: lab-64/lab-64-grpc-starter/lab-64-grpc-starter-user-service-api/pom.xml ================================================ lab-64-grpc-starter cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-64-grpc-starter-user-service-api 1.30.0 1.8 1.8 1.6.2 0.6.1 io.grpc grpc-protobuf ${io.grpc.version} io.grpc grpc-stub ${io.grpc.version} kr.motd.maven os-maven-plugin ${os-maven-plugin.version} org.xolstice.maven.plugins protobuf-maven-plugin ${protobuf-maven-plugin.version} grpc-java com.google.protobuf:protoc:3.9.1:exe:${os.detected.classifier} io.grpc:protoc-gen-grpc-java:${io.grpc.version}:exe:${os.detected.classifier} compile compile-custom ================================================ FILE: lab-64/lab-64-grpc-starter/lab-64-grpc-starter-user-service-api/src/main/proto/UserService.proto ================================================ syntax = "proto3"; option java_multiple_files = true; package cn.iocoder.springboot.lab64.userservice.api; message UserGetRequest { int32 id = 1; } message UserGetResponse { int32 id = 1; string name = 2; int32 gender = 3; } message UserCreateRequest { string name = 1; int32 gender = 2; } message UserCreateResponse { int32 id = 1; } service UserService { rpc get(UserGetRequest) returns (UserGetResponse); rpc create(UserCreateRequest) returns (UserCreateResponse); } ================================================ FILE: lab-64/lab-64-grpc-starter/pom.xml ================================================ lab-64 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-64-grpc-starter pom lab-64-grpc-starter-user-service lab-64-grpc-starter-user-service-api lab-64-grpc-starter-application ================================================ FILE: lab-64/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-64 pom lab-64-grpc-demo lab-64-grpc-starter ================================================ FILE: lab-64/《芋道 Spring Boot gRPC 入门》.md ================================================ ================================================ FILE: lab-65/lab-65-cxf-ws-demo/lab-65-cxf-ws-demo-application/pom.xml ================================================ lab-65-cxf-ws-demo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-65-cxf-ws-demo-application 2.2.4.RELEASE 1.8 1.8 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.apache.cxf cxf-spring-boot-starter-jaxws 3.3.6 org.apache.cxf cxf-codegen-plugin 3.2.5 generate-sources generate-sources src/main/resources/wsdl/user.wsdl classpath:wsdl/user.wsdl ${project.build.directory}/generated/cxf wsdl2java ================================================ FILE: lab-65/lab-65-cxf-ws-demo/lab-65-cxf-ws-demo-application/src/main/java/cn/iocoder/springboot/lab65/demo/DemoApplication.java ================================================ package cn.iocoder.springboot.lab65.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { // 启动 Spring Boot 应用 SpringApplication.run(DemoApplication.class, args); } } ================================================ FILE: lab-65/lab-65-cxf-ws-demo/lab-65-cxf-ws-demo-application/src/main/java/cn/iocoder/springboot/lab65/demo/config/CXFConfig.java ================================================ package cn.iocoder.springboot.lab65.demo.config; import https.github_com.yunaiv.springboot_labs.tree.master.lab_65.lab_65_cxf_ws_demo.UserService; import org.apache.cxf.jaxws.JaxWsProxyFactoryBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class CXFConfig { @Bean public UserService userService() { JaxWsProxyFactoryBean jaxWsProxyFactoryBean = new JaxWsProxyFactoryBean(); // 设置 UserService 接口 jaxWsProxyFactoryBean.setServiceClass(UserService.class); // 设置 Web Services 地址 jaxWsProxyFactoryBean.setAddress("http://127.0.0.1:9090/ws/user"); // 创建 return (UserService) jaxWsProxyFactoryBean.create(); } } ================================================ FILE: lab-65/lab-65-cxf-ws-demo/lab-65-cxf-ws-demo-application/src/main/java/cn/iocoder/springboot/lab65/demo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab65.demo.controller; import https.github_com.yunaiv.springboot_labs.tree.master.lab_65.lab_65_cxf_ws_demo.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @Autowired private UserService userService; @GetMapping("/get") public String get(@RequestParam("id") Integer id) { UserGetRequest request = new UserGetRequest(); request.setId(id); // 执行 Web Services 请求 UserGetResponse response = userService.get(request); // 响应 return response.getName(); } @GetMapping("/create") // 为了方便测试,实际使用 @PostMapping public Integer create(@RequestParam("name") String name, @RequestParam("gender") Integer gender) { // 请求 UserCreateRequest request = new UserCreateRequest(); request.setName(name); request.setGender(gender); // 执行 Web Services 请求 UserCreateResponse response = userService.create(request); // 响应 return response.getId(); } } ================================================ FILE: lab-65/lab-65-cxf-ws-demo/lab-65-cxf-ws-demo-application/src/main/resources/wsdl/user.wsdl ================================================ ================================================ FILE: lab-65/lab-65-cxf-ws-demo/lab-65-cxf-ws-demo-user-service/pom.xml ================================================ lab-65-spring-ws-demo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-65-cxf-ws-demo-user-service 2.2.4.RELEASE 1.8 1.8 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.apache.cxf cxf-spring-boot-starter-jaxws 3.3.6 ================================================ FILE: lab-65/lab-65-cxf-ws-demo/lab-65-cxf-ws-demo-user-service/src/main/java/cn/iocoder/springboot/lab65/userservice/UserServiceApplication.java ================================================ package cn.iocoder.springboot.lab65.userservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class UserServiceApplication { public static void main(String[] args) { // 启动 Spring Boot 应用 SpringApplication.run(UserServiceApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab65.userservice.config; import cn.iocoder.springboot.lab65.userservice.service.UserService; import org.apache.cxf.Bus; import org.apache.cxf.bus.spring.SpringBus; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.xml.ws.Endpoint; @Configuration public class CXFConfig { public static final String NAMESPACE_URI = "https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-65/lab-65-cxf-ws-demo"; @Bean(name = Bus.DEFAULT_BUS_ID) public SpringBus springBus() { return new SpringBus(); } @Bean public Endpoint userServiceEndpoint(UserService userService) { Endpoint endpoint = Endpoint.create(userService); endpoint.publish("/user");//发布地址 return endpoint; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab65.userservice.request; /** * 创建用户信息 Request */ public class UserCreateRequest { /** * 昵称 */ private String name; /** * 性别别 */ private Integer gender; public String getName() { return name; } public UserCreateRequest setName(String name) { this.name = name; return this; } public Integer getGender() { return gender; } public UserCreateRequest setGender(Integer gender) { this.gender = gender; return this; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab65.userservice.request; /** * 获得用户信息 Request */ public class UserGetRequest { /** * 用户编号 */ private Integer id; public Integer getId() { return id; } public UserGetRequest setId(Integer id) { this.id = id; return this; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab65.userservice.response; /** * 创建用户信息 Response */ public class UserCreateResponse { /** * 用户编号 */ private Integer id; public Integer getId() { return id; } public UserCreateResponse setId(Integer id) { this.id = id; return this; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab65.userservice.response; /** * 获得用户信息 Response */ public class UserGetResponse { /** * 用户编号 */ private Integer id; /** * 昵称 */ private String name; /** * 性别别 */ private Integer gender; public Integer getId() { return id; } public UserGetResponse setId(Integer id) { this.id = id; return this; } public String getName() { return name; } public UserGetResponse setName(String name) { this.name = name; return this; } public Integer getGender() { return gender; } public UserGetResponse setGender(Integer gender) { this.gender = gender; return this; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab65.userservice.service; import cn.iocoder.springboot.lab65.userservice.config.CXFConfig; import cn.iocoder.springboot.lab65.userservice.request.UserCreateRequest; import cn.iocoder.springboot.lab65.userservice.request.UserGetRequest; import cn.iocoder.springboot.lab65.userservice.response.UserCreateResponse; import cn.iocoder.springboot.lab65.userservice.response.UserGetResponse; import javax.jws.WebService; @WebService(targetNamespace = CXFConfig.NAMESPACE_URI) public interface UserService { UserGetResponse get(UserGetRequest request); UserCreateResponse create(UserCreateRequest request); } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab65.userservice.service; import cn.iocoder.springboot.lab65.userservice.config.CXFConfig; import cn.iocoder.springboot.lab65.userservice.request.UserCreateRequest; import cn.iocoder.springboot.lab65.userservice.request.UserGetRequest; import cn.iocoder.springboot.lab65.userservice.response.UserCreateResponse; import cn.iocoder.springboot.lab65.userservice.response.UserGetResponse; import org.springframework.stereotype.Service; import javax.jws.WebService; @Service @WebService( serviceName = "userService", // 服务名称 targetNamespace = CXFConfig.NAMESPACE_URI // Namespace 命名空间 ) public class UserServiceImpl implements UserService { @Override public UserGetResponse get(UserGetRequest request) { UserGetResponse response = new UserGetResponse(); response.setId(request.getId()); response.setName("没有昵称:" + request.getId()); response.setGender(request.getId() % 2 + 1); return response; } @Override public UserCreateResponse create(UserCreateRequest request) { UserCreateResponse response = new UserCreateResponse(); response.setId((int) (System.currentTimeMillis() / 1000)); return response; } } ================================================ FILE: lab-65/lab-65-cxf-ws-demo/lab-65-cxf-ws-demo-user-service/src/main/resources/application.yml ================================================ # CXF 配置项,对应 CxfProperties 配置类 cxf: path: /ws/ # CXF CXFServlet 的匹配路径 server: port: 9090 # 设置服务器端口为 9090 ================================================ FILE: lab-65/lab-65-cxf-ws-demo/pom.xml ================================================ lab-65 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-65-cxf-ws-demo pom lab-65-cxf-ws-demo-user-service lab-65-cxf-ws-demo-application ================================================ FILE: lab-65/lab-65-spring-ws-demo/lab-65-spring-ws-demo-application/pom.xml ================================================ lab-65-spring-ws-demo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-65-spring-ws-demo-application 2.2.4.RELEASE 1.8 1.8 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.boot spring-boot-starter-web-services wsdl4j wsdl4j org.jvnet.jaxb2.maven2 maven-jaxb2-plugin 0.14.0 generate WSDL http://127.0.0.1:8080/ws/users.wsdl cn.iocoder.springboot.lab65.demo.wsdl ================================================ FILE: lab-65/lab-65-spring-ws-demo/lab-65-spring-ws-demo-application/src/main/java/cn/iocoder/springboot/lab65/demo/DemoApplication.java ================================================ package cn.iocoder.springboot.lab65.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { // 启动 Spring Boot 应用 SpringApplication.run(DemoApplication.class, args); } } ================================================ FILE: lab-65/lab-65-spring-ws-demo/lab-65-spring-ws-demo-application/src/main/java/cn/iocoder/springboot/lab65/demo/client/UserClient.java ================================================ package cn.iocoder.springboot.lab65.demo.client; import cn.iocoder.springboot.lab65.demo.wsdl.UserCreateRequest; import cn.iocoder.springboot.lab65.demo.wsdl.UserCreateResponse; import cn.iocoder.springboot.lab65.demo.wsdl.UserGetRequest; import cn.iocoder.springboot.lab65.demo.wsdl.UserGetResponse; import org.springframework.ws.client.core.support.WebServiceGatewaySupport; public class UserClient extends WebServiceGatewaySupport { public UserGetResponse getUser(Integer id) { // 创建请求对象 UserGetRequest request = new UserGetRequest(); request.setId(id); // 执行请求 return (UserGetResponse) getWebServiceTemplate().marshalSendAndReceive(request); } public UserCreateResponse createUser(String name, Integer gender) { // 创建请求对象 UserCreateRequest request = new UserCreateRequest(); request.setName(name); request.setGender(gender); // 执行请求 return (UserCreateResponse) getWebServiceTemplate().marshalSendAndReceive(request); } } ================================================ FILE: lab-65/lab-65-spring-ws-demo/lab-65-spring-ws-demo-application/src/main/java/cn/iocoder/springboot/lab65/demo/config/WebServicesConfig.java ================================================ package cn.iocoder.springboot.lab65.demo.config; import cn.iocoder.springboot.lab65.demo.client.UserClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.oxm.jaxb.Jaxb2Marshaller; @Configuration public class WebServicesConfig { // 创建 Jaxb2Marshaller Bean,实现 XML 与 Bean 的互相转换 @Bean public Jaxb2Marshaller marshaller() { Jaxb2Marshaller marshaller = new Jaxb2Marshaller(); marshaller.setContextPath("cn.iocoder.springboot.lab65.demo.wsdl"); // 用户服务的 WSDL 文件 return marshaller; } // 创建 UserClient Bean @Bean public UserClient countryClient(Jaxb2Marshaller marshaller) { UserClient client = new UserClient(); client.setDefaultUri("http://127.0.0.1:8080/ws"); // 用户服务的 Web Services 地址 client.setMarshaller(marshaller); client.setUnmarshaller(marshaller); return client; } } ================================================ FILE: lab-65/lab-65-spring-ws-demo/lab-65-spring-ws-demo-application/src/main/java/cn/iocoder/springboot/lab65/demo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab65.demo.controller; import cn.iocoder.springboot.lab65.demo.client.UserClient; import cn.iocoder.springboot.lab65.demo.wsdl.UserCreateResponse; import cn.iocoder.springboot.lab65.demo.wsdl.UserGetResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @Autowired private UserClient userClient; @GetMapping("/get") public String get(@RequestParam("id") Integer id) { // 执行 Web Services 请求 UserGetResponse response = userClient.getUser(id); // 响应 return response.getName(); } @GetMapping("/create") // 为了方便测试,实际使用 @PostMapping public Integer create(@RequestParam("name") String name, @RequestParam("gender") Integer gender) { // 执行 Web Services 请求 UserCreateResponse response = userClient.createUser(name, gender); // 响应 return response.getId(); } } ================================================ FILE: lab-65/lab-65-spring-ws-demo/lab-65-spring-ws-demo-application/src/main/resources/application.yml ================================================ server: port: 9090 ================================================ FILE: lab-65/lab-65-spring-ws-demo/lab-65-spring-ws-demo-user-service/pom.xml ================================================ lab-65-spring-ws-demo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-65-spring-ws-demo-user-service 2.2.4.RELEASE 1.8 1.8 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.boot spring-boot-starter-web-services wsdl4j wsdl4j org.codehaus.mojo jaxb2-maven-plugin 2.5.0 xjc xjc ${project.basedir}/src/main/resources/users.xsd cn.iocoder.springboot.lab65.userservice.model ================================================ FILE: lab-65/lab-65-spring-ws-demo/lab-65-spring-ws-demo-user-service/src/main/java/cn/iocoder/springboot/lab65/userservice/UserServiceApplication.java ================================================ package cn.iocoder.springboot.lab65.userservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class UserServiceApplication { public static void main(String[] args) { // 启动 Spring Boot 应用 SpringApplication.run(UserServiceApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab65.userservice.config; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; import org.springframework.ws.config.annotation.EnableWs; import org.springframework.ws.config.annotation.WsConfigurerAdapter; import org.springframework.ws.server.EndpointInterceptor; import org.springframework.ws.transport.http.MessageDispatcherServlet; import org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition; import org.springframework.xml.xsd.SimpleXsdSchema; import org.springframework.xml.xsd.XsdSchema; import java.util.List; @Configuration @EnableWs // 开启 Web Services 服务 public class WebServicesConfig extends WsConfigurerAdapter { public static final String NAMESPACE_URI = "https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-65/lab-65-spring-ws-demo"; @Bean public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) { MessageDispatcherServlet servlet = new MessageDispatcherServlet(); servlet.setApplicationContext(applicationContext); servlet.setTransformWsdlLocations(true); return new ServletRegistrationBean<>(servlet, "/ws/*"); } @Bean public XsdSchema usersSchema() { return new SimpleXsdSchema(new ClassPathResource("users.xsd")); } @Bean(name = "users") public DefaultWsdl11Definition defaultWsdl11Definition(XsdSchema usersSchema) { DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition(); wsdl11Definition.setLocationUri("/ws"); wsdl11Definition.setTargetNamespace(NAMESPACE_URI); wsdl11Definition.setSchema(usersSchema); wsdl11Definition.setPortTypeName("UsersPort"); return wsdl11Definition; } @Override public void addInterceptors(List interceptors) { // 可自定义附加拦截器 } } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab65.userservice.endpoint; import cn.iocoder.springboot.lab65.userservice.config.WebServicesConfig; import cn.iocoder.springboot.lab65.userservice.model.UserCreateRequest; import cn.iocoder.springboot.lab65.userservice.model.UserCreateResponse; import cn.iocoder.springboot.lab65.userservice.model.UserGetRequest; import cn.iocoder.springboot.lab65.userservice.model.UserGetResponse; import org.springframework.ws.server.endpoint.annotation.Endpoint; import org.springframework.ws.server.endpoint.annotation.PayloadRoot; import org.springframework.ws.server.endpoint.annotation.RequestPayload; import org.springframework.ws.server.endpoint.annotation.ResponsePayload; @Endpoint public class UserEndpoint { @PayloadRoot(namespace = WebServicesConfig.NAMESPACE_URI, localPart = "UserGetRequest") @ResponsePayload public UserGetResponse get(@RequestPayload UserGetRequest request) { UserGetResponse response = new UserGetResponse(); response.setId(request.getId()); response.setName("没有昵称:" + request.getId()); response.setGender(request.getId() % 2 + 1); return response; } @PayloadRoot(namespace = WebServicesConfig.NAMESPACE_URI, localPart = "UserCreateRequest") @ResponsePayload public UserCreateResponse create(@RequestPayload UserCreateRequest request) { UserCreateResponse response = new UserCreateResponse(); response.setId((int) (System.currentTimeMillis() / 1000)); return response; } } ================================================ FILE: lab-65/lab-65-spring-ws-demo/lab-65-spring-ws-demo-user-service/src/main/resources/users.xsd ================================================ ================================================ FILE: lab-65/lab-65-spring-ws-demo/pom.xml ================================================ lab-65 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-65-spring-ws-demo pom lab-65-spring-ws-demo-user-service lab-65-spring-ws-demo-application ================================================ FILE: lab-65/lab-65-ws-feign-client/pom.xml ================================================ lab-65 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-65-ws-feign-client 2.2.4.RELEASE 1.8 1.8 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.boot spring-boot-starter-web io.github.openfeign feign-soap 11.0 org.jvnet.jaxb2.maven2 maven-jaxb2-plugin 0.14.0 generate WSDL http://127.0.0.1:8080/ws/users.wsdl cn.iocoder.springboot.lab65.demo.wsdl ================================================ FILE: lab-65/lab-65-ws-feign-client/src/main/java/cn/iocoder/springboot/lab65/demo/FeignDemoApplication.java ================================================ package cn.iocoder.springboot.lab65.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class FeignDemoApplication { public static void main(String[] args) { SpringApplication.run(FeignDemoApplication.class, args); } } ================================================ FILE: lab-65/lab-65-ws-feign-client/src/main/java/cn/iocoder/springboot/lab65/demo/config/FeignConfig.java ================================================ package cn.iocoder.springboot.lab65.demo.config; import cn.iocoder.springboot.lab65.demo.feign.UserServiceFeignClient; import feign.Feign; import feign.jaxb.JAXBContextFactory; import feign.soap.SOAPDecoder; import feign.soap.SOAPEncoder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class FeignConfig { private static final JAXBContextFactory JAXB_FACTORY = new JAXBContextFactory.Builder() .withMarshallerJAXBEncoding("UTF-8") .build(); @Bean public UserServiceFeignClient userServiceFeignClient() { return Feign.builder() .encoder(new SOAPEncoder(JAXB_FACTORY)) .decoder(new SOAPDecoder(JAXB_FACTORY)) .target(UserServiceFeignClient.class, "http://127.0.0.1:8080/ws"); // 目标地址 } } ================================================ FILE: lab-65/lab-65-ws-feign-client/src/main/java/cn/iocoder/springboot/lab65/demo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab65.demo.controller; import cn.iocoder.springboot.lab65.demo.feign.UserServiceFeignClient; import cn.iocoder.springboot.lab65.demo.wsdl.UserCreateRequest; import cn.iocoder.springboot.lab65.demo.wsdl.UserCreateResponse; import cn.iocoder.springboot.lab65.demo.wsdl.UserGetRequest; import cn.iocoder.springboot.lab65.demo.wsdl.UserGetResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @Autowired private UserServiceFeignClient userClient; @GetMapping("/get") public String get(@RequestParam("id") Integer id) { // 请求 UserGetRequest request = new UserGetRequest(); request.setId(id); // 执行 Web Services 请求 UserGetResponse response = userClient.getUser(request); // 响应 return response.getName(); } @GetMapping("/create") // 为了方便测试,实际使用 @PostMapping public Integer create(@RequestParam("name") String name, @RequestParam("gender") Integer gender) { // 请求 UserCreateRequest request = new UserCreateRequest(); request.setName(name); request.setGender(gender); // 执行 Web Services 请求 UserCreateResponse response = userClient.createUser(request); // 响应 return response.getId(); } } ================================================ FILE: lab-65/lab-65-ws-feign-client/src/main/java/cn/iocoder/springboot/lab65/demo/feign/UserServiceFeignClient.java ================================================ package cn.iocoder.springboot.lab65.demo.feign; import cn.iocoder.springboot.lab65.demo.wsdl.UserCreateRequest; import cn.iocoder.springboot.lab65.demo.wsdl.UserCreateResponse; import cn.iocoder.springboot.lab65.demo.wsdl.UserGetRequest; import cn.iocoder.springboot.lab65.demo.wsdl.UserGetResponse; import feign.Headers; import feign.RequestLine; /** * 用户服务 Feign Client */ public interface UserServiceFeignClient { // 获得用户详情 @RequestLine("POST /") @Headers("Content-Type: text/xml") UserGetResponse getUser(UserGetRequest request); // 创建用户 @RequestLine("POST /") @Headers("Content-Type: text/xml") UserCreateResponse createUser(UserCreateRequest request); } ================================================ FILE: lab-65/lab-65-ws-feign-client/src/main/resources/application.yml ================================================ server: port: 9090 ================================================ FILE: lab-65/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-65 pom lab-65-spring-ws-demo lab-65-ws-feign-client lab-65-cxf-ws-demo ================================================ FILE: lab-65/《芋道 Spring Boot Web Services 入门》.md ================================================ ================================================ FILE: lab-66/lab-66-spring-data-solr/pom.xml ================================================ lab-66 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-66-spring-data-solr 2.2.4.RELEASE 1.8 1.8 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.boot spring-boot-starter-data-solr org.springframework.boot spring-boot-starter-test test ================================================ FILE: lab-66/lab-66-spring-data-solr/src/main/java/cn/iocoder/springboot/lab15/springdatasolr/Application.java ================================================ package cn.iocoder.springboot.lab15.springdatasolr; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { } ================================================ FILE: lab-66/lab-66-spring-data-solr/src/main/java/cn/iocoder/springboot/lab15/springdatasolr/dataobject/SolrProductDO.java ================================================ package cn.iocoder.springboot.lab15.springdatasolr.dataobject; import org.springframework.data.annotation.Id; import org.springframework.data.solr.core.mapping.Indexed; import org.springframework.data.solr.core.mapping.SolrDocument; @SolrDocument(collection = "new_core") public class SolrProductDO { /** * ID 主键 */ @Id @Indexed(name = "id", type = "int") private Integer id; /** * SPU 名字 */ @Indexed(name = "name", type = "string") private String name; /** * 描述 */ @Indexed(name = "description", type = "string") private String description; /** * 分类编号 */ @Indexed(name = "cid", type = "cid") private Integer cid; /** * 分类名 */ @Indexed(name = "category_name", type = "string") private String categoryName; public Integer getId() { return id; } public SolrProductDO setId(Integer id) { this.id = id; return this; } public String getName() { return name; } public SolrProductDO setName(String name) { this.name = name; return this; } public String getDescription() { return description; } public SolrProductDO setDescription(String description) { this.description = description; return this; } public Integer getCid() { return cid; } public SolrProductDO setCid(Integer cid) { this.cid = cid; return this; } public String getCategoryName() { return categoryName; } public SolrProductDO setCategoryName(String categoryName) { this.categoryName = categoryName; return this; } @Override public String toString() { return "SolrProductDO{" + "id=" + id + ", name='" + name + '\'' + ", description='" + description + '\'' + ", cid=" + cid + ", categoryName='" + categoryName + '\'' + '}'; } } ================================================ FILE: lab-66/lab-66-spring-data-solr/src/main/java/cn/iocoder/springboot/lab15/springdatasolr/repository/ProductRepository.java ================================================ package cn.iocoder.springboot.lab15.springdatasolr.repository; import cn.iocoder.springboot.lab15.springdatasolr.dataobject.SolrProductDO; import org.springframework.data.solr.repository.SolrCrudRepository; public interface ProductRepository extends SolrCrudRepository { } ================================================ FILE: lab-66/lab-66-spring-data-solr/src/main/java/cn/iocoder/springboot/lab15/springdatasolr/repository/ProductRepository02.java ================================================ package cn.iocoder.springboot.lab15.springdatasolr.repository; import cn.iocoder.springboot.lab15.springdatasolr.dataobject.SolrProductDO; import org.springframework.data.solr.repository.SolrCrudRepository; public interface ProductRepository02 extends SolrCrudRepository { SolrProductDO findByName(String name); // Page findByNameLike(String name, Pageable pageable); } ================================================ FILE: lab-66/lab-66-spring-data-solr/src/main/java/cn/iocoder/springboot/lab15/springdatasolr/repository/ProductRepository03.java ================================================ package cn.iocoder.springboot.lab15.springdatasolr.repository; import cn.iocoder.springboot.lab15.springdatasolr.dataobject.SolrProductDO; import org.springframework.data.solr.repository.Query; import org.springframework.data.solr.repository.SolrCrudRepository; import java.util.List; public interface ProductRepository03 extends SolrCrudRepository { /** * 根据 name 匹配商品名或者商品分类,获得符合的商品列表 */ @Query("name:?0 OR category_name:?0") List findByCustomQuery(String name); } ================================================ FILE: lab-66/lab-66-spring-data-solr/src/main/resources/application.yml ================================================ spring: data: # Spring Data Solr 配置项,对应 SolrProperties 配置类 solr: host: 'http://127.0.0.1:8983/solr' # Solr 服务器地址 ================================================ FILE: lab-66/lab-66-spring-data-solr/src/test/java/cn/iocoder/springboot/lab15/springdatasolr/repository/ProductRepository02Test.java ================================================ package cn.iocoder.springboot.lab15.springdatasolr.repository; import cn.iocoder.springboot.lab15.springdatasolr.Application; import cn.iocoder.springboot.lab15.springdatasolr.dataobject.SolrProductDO; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class ProductRepository02Test { @Autowired private ProductRepository02 productRepository; @Test // 根据名字获得一条记录 public void testFindByName() { SolrProductDO product = productRepository.findByName("芋道源码"); System.out.println(product); } // @Test // 使用 name 模糊查询,分页返回结果 // public void testFindByNameLike() { // // 根据情况,是否要制造测试数据 // if (false) { // testInsert(); // } // // // 创建排序条件 // Sort sort = Sort.by(Sort.Direction.DESC, "id"); // ID 倒序 // // 创建分页条件。 // Pageable pageable = PageRequest.of(0, 10, sort); // // 执行分页操作 // Page page = productRepository.findByNameLike("芋道", pageable); // // 打印 // System.out.println(page.getTotalElements()); // System.out.println(page.getTotalPages()); // } // // /** // * 为了给分页制造一点数据 // */ // private void testInsert() { // for (int i = 1; i <= 100; i++) { // SolrProductDO product = new SolrProductDO(); // product.setId(i); // 一般 ES 的 ID 编号,使用 DB 数据对应的编号。这里,先写死 // product.setName("芋道源码:" + i); // product.setDescription("我只是一个描述"); // product.setCid(1); // product.setCategoryName("技术"); // productRepository.save(product); // } // } } ================================================ FILE: lab-66/lab-66-spring-data-solr/src/test/java/cn/iocoder/springboot/lab15/springdatasolr/repository/ProductRepository03Test.java ================================================ package cn.iocoder.springboot.lab15.springdatasolr.repository; import cn.iocoder.springboot.lab15.springdatasolr.Application; import cn.iocoder.springboot.lab15.springdatasolr.dataobject.SolrProductDO; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.List; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class ProductRepository03Test { @Autowired private ProductRepository03 productRepository; @Test public void testFindByCustomQuery() { List products = productRepository.findByCustomQuery("技术"); System.out.println(products.size()); } } ================================================ FILE: lab-66/lab-66-spring-data-solr/src/test/java/cn/iocoder/springboot/lab15/springdatasolr/repository/ProductRepositoryTest.java ================================================ package cn.iocoder.springboot.lab15.springdatasolr.repository; import cn.iocoder.springboot.lab15.springdatasolr.Application; import cn.iocoder.springboot.lab15.springdatasolr.dataobject.SolrProductDO; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.Arrays; import java.util.Optional; @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class ProductRepositoryTest { @Autowired private ProductRepository productRepository; @Test // 插入一条记录 public void testInsert() { SolrProductDO product = new SolrProductDO(); product.setId(1); // 一般 Solr 的 ID 编号,使用 DB 数据对应的编号。这里,先写死 product.setName("芋道源码"); product.setDescription("我只是一个描述"); product.setCid(2); product.setCategoryName("技术"); productRepository.save(product); } // 这里要注意,如果使用 save 方法来更新的话,必须是全量字段,否则其它字段会被覆盖。 // 所以,这里仅仅是作为一个示例。 @Test // 更新一条记录 public void testUpdate() { SolrProductDO product = new SolrProductDO(); product.setId(1); product.setCid(2); product.setCategoryName("技术-Java"); productRepository.save(product); } @Test // 根据 ID 编号,删除一条记录 public void testDelete() { productRepository.deleteById(1); } @Test // 根据 ID 编号,查询一条记录 public void testSelectById() { Optional userDO = productRepository.findById(1); System.out.println(userDO.isPresent()); } @Test // 根据 ID 编号数组,查询多条记录 public void testSelectByIds() { Iterable users = productRepository.findAllById(Arrays.asList(1, 4)); users.forEach(System.out::println); } } ================================================ FILE: lab-66/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-66 pom lab-66-spring-data-solr ================================================ FILE: lab-66/《芋道 Spring Boot Web Solr 入门》.md ================================================ ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-client/pom.xml ================================================ lab-67-netty-demo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-67-netty-demo-client 2.2.4.RELEASE 1.8 1.8 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.boot spring-boot-starter-web io.netty netty-all 4.1.50.Final cn.iocoder.springboot.labs lab-67-netty-demo-common 1.0-SNAPSHOT ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-client/src/main/java/cn/iocoder/springboot/lab67/nettyclientdemo/NettyClientApplication.java ================================================ package cn.iocoder.springboot.lab67.nettyclientdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class NettyClientApplication { public static void main(String[] args) { SpringApplication.run(NettyClientApplication.class, args); } } ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-client/src/main/java/cn/iocoder/springboot/lab67/nettyclientdemo/client/NettyClient.java ================================================ package cn.iocoder.springboot.lab67.nettyclientdemo.client; import cn.iocoder.springboot.lab67.nettyclientdemo.client.handler.NettyClientHandlerInitializer; import cn.iocoder.springboot.lab67.nettycommondemo.codec.Invocation; import io.netty.bootstrap.Bootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioSocketChannel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import java.util.concurrent.TimeUnit; @Component public class NettyClient { /** * 重连频率,单位:秒 */ private static final Integer RECONNECT_SECONDS = 20; private Logger logger = LoggerFactory.getLogger(getClass()); @Value("${netty.server.host}") private String serverHost; @Value("${netty.server.port}") private Integer serverPort; @Autowired private NettyClientHandlerInitializer nettyClientHandlerInitializer; /** * 线程组,用于客户端对服务端的链接、数据读写 */ private EventLoopGroup eventGroup = new NioEventLoopGroup(); /** * Netty Client Channel */ private volatile Channel channel; /** * 启动 Netty Client */ @PostConstruct public void start() throws InterruptedException { // 创建 Bootstrap 对象,用于 Netty Client 启动 Bootstrap bootstrap = new Bootstrap(); // 设置 Bootstrap 的各种属性。 bootstrap.group(eventGroup) // 设置一个 EventLoopGroup 对象 .channel(NioSocketChannel.class) // 指定 Channel 为客户端 NioSocketChannel .remoteAddress(serverHost, serverPort) // 指定链接服务器的地址 .option(ChannelOption.SO_KEEPALIVE, true) // TCP Keepalive 机制,实现 TCP 层级的心跳保活功能 .option(ChannelOption.TCP_NODELAY, true) // 允许较小的数据包的发送,降低延迟 .handler(nettyClientHandlerInitializer); // 链接服务器,并异步等待成功,即启动客户端 bootstrap.connect().addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { // 连接失败 if (!future.isSuccess()) { logger.error("[start][Netty Client 连接服务器({}:{}) 失败]", serverHost, serverPort); reconnect(); return; } // 连接成功 channel = future.channel(); logger.info("[start][Netty Client 连接服务器({}:{}) 成功]", serverHost, serverPort); } }); } public void reconnect() { eventGroup.schedule(new Runnable() { @Override public void run() { logger.info("[reconnect][开始重连]"); try { start(); } catch (InterruptedException e) { logger.error("[reconnect][重连失败]", e); } } }, RECONNECT_SECONDS, TimeUnit.SECONDS); logger.info("[reconnect][{} 秒后将发起重连]", RECONNECT_SECONDS); } /** * 关闭 Netty Server */ @PreDestroy public void shutdown() { // 关闭 Netty Client if (channel != null) { channel.close(); } // 优雅关闭一个 EventLoopGroup 对象 eventGroup.shutdownGracefully(); } /** * 发送消息 * * @param invocation 消息体 */ public void send(Invocation invocation) { if (channel == null) { logger.error("[send][连接不存在]"); return; } if (!channel.isActive()) { logger.error("[send][连接({})未激活]", channel.id()); return; } // 发送消息 channel.writeAndFlush(invocation); } } ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-client/src/main/java/cn/iocoder/springboot/lab67/nettyclientdemo/client/handler/NettyClientHandler.java ================================================ package cn.iocoder.springboot.lab67.nettyclientdemo.client.handler; import cn.iocoder.springboot.lab67.nettyclientdemo.client.NettyClient; import cn.iocoder.springboot.lab67.nettyclientdemo.message.heartbeat.HeartbeatRequest; import cn.iocoder.springboot.lab67.nettycommondemo.codec.Invocation; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.handler.timeout.IdleStateEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component @ChannelHandler.Sharable public class NettyClientHandler extends ChannelInboundHandlerAdapter { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private NettyClient nettyClient; @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { // 发起重连 nettyClient.reconnect(); // 继续触发事件 super.channelInactive(ctx); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { logger.error("[exceptionCaught][连接({}) 发生异常]", ctx.channel().id(), cause); // 断开连接 ctx.channel().close(); } @Override public void userEventTriggered(ChannelHandlerContext ctx, Object event) throws Exception { // 空闲时,向服务端发起一次心跳 if (event instanceof IdleStateEvent) { logger.info("[userEventTriggered][发起一次心跳]"); HeartbeatRequest heartbeatRequest = new HeartbeatRequest(); ctx.writeAndFlush(new Invocation(HeartbeatRequest.TYPE, heartbeatRequest)) .addListener(ChannelFutureListener.CLOSE_ON_FAILURE); } else { super.userEventTriggered(ctx, event); } } } ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-client/src/main/java/cn/iocoder/springboot/lab67/nettyclientdemo/client/handler/NettyClientHandlerInitializer.java ================================================ package cn.iocoder.springboot.lab67.nettyclientdemo.client.handler; import cn.iocoder.springboot.lab67.nettycommondemo.codec.InvocationDecoder; import cn.iocoder.springboot.lab67.nettycommondemo.codec.InvocationEncoder; import cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.MessageDispatcher; import io.netty.channel.Channel; import io.netty.channel.ChannelInitializer; import io.netty.handler.timeout.IdleStateHandler; import io.netty.handler.timeout.ReadTimeoutHandler; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class NettyClientHandlerInitializer extends ChannelInitializer { /** * 心跳超时时间 */ private static final Integer READ_TIMEOUT_SECONDS = 60; @Autowired private MessageDispatcher messageDispatcher; @Autowired private NettyClientHandler nettyClientHandler; @Override protected void initChannel(Channel ch) { ch.pipeline() // 空闲检测 .addLast(new IdleStateHandler(READ_TIMEOUT_SECONDS, 0, 0)) .addLast(new ReadTimeoutHandler(3 * READ_TIMEOUT_SECONDS)) // 编码器 .addLast(new InvocationEncoder()) // 解码器 .addLast(new InvocationDecoder()) // 消息分发器 .addLast(messageDispatcher) // 客户端处理器 .addLast(nettyClientHandler) ; } } ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-client/src/main/java/cn/iocoder/springboot/lab67/nettyclientdemo/config/NettyClientConfig.java ================================================ package cn.iocoder.springboot.lab67.nettyclientdemo.config; import cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.MessageDispatcher; import cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.MessageHandlerContainer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class NettyClientConfig { @Bean public MessageDispatcher messageDispatcher() { return new MessageDispatcher(); } @Bean public MessageHandlerContainer messageHandlerContainer() { return new MessageHandlerContainer(); } } ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-client/src/main/java/cn/iocoder/springboot/lab67/nettyclientdemo/controller/TestController.java ================================================ package cn.iocoder.springboot.lab67.nettyclientdemo.controller; import cn.iocoder.springboot.lab67.nettyclientdemo.client.NettyClient; import cn.iocoder.springboot.lab67.nettycommondemo.codec.Invocation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/test") public class TestController { @Autowired private NettyClient nettyClient; @PostMapping("/mock") public String mock(String type, String message) { // 创建 Invocation 对象 Invocation invocation = new Invocation(type, message); // 发送消息 nettyClient.send(invocation); return "success"; } } ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-client/src/main/java/cn/iocoder/springboot/lab67/nettyclientdemo/message/auth/AuthRequest.java ================================================ package cn.iocoder.springboot.lab67.nettyclientdemo.message.auth; import cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.Message; /** * 用户认证请求 */ public class AuthRequest implements Message { public static final String TYPE = "AUTH_REQUEST"; /** * 认证 Token */ private String accessToken; public String getAccessToken() { return accessToken; } public AuthRequest setAccessToken(String accessToken) { this.accessToken = accessToken; return this; } @Override public String toString() { return "AuthRequest{" + "accessToken='" + accessToken + '\'' + '}'; } } ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-client/src/main/java/cn/iocoder/springboot/lab67/nettyclientdemo/message/auth/AuthResponse.java ================================================ package cn.iocoder.springboot.lab67.nettyclientdemo.message.auth; import cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.Message; /** * 用户认证响应 */ public class AuthResponse implements Message { public static final String TYPE = "AUTH_RESPONSE"; /** * 响应状态码 */ private Integer code; /** * 响应提示 */ private String message; public Integer getCode() { return code; } public AuthResponse setCode(Integer code) { this.code = code; return this; } public String getMessage() { return message; } public AuthResponse setMessage(String message) { this.message = message; return this; } @Override public String toString() { return "AuthResponse{" + "code=" + code + ", message='" + message + '\'' + '}'; } } ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-client/src/main/java/cn/iocoder/springboot/lab67/nettyclientdemo/message/chat/ChatRedirectToUserRequest.java ================================================ package cn.iocoder.springboot.lab67.nettyclientdemo.message.chat; import cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.Message; /** * 转发消息给一个用户的 Message */ public class ChatRedirectToUserRequest implements Message { public static final String TYPE = "CHAT_REDIRECT_TO_USER_REQUEST"; /** * 消息编号 */ private String msgId; /** * 内容 */ private String content; public String getMsgId() { return msgId; } public ChatRedirectToUserRequest setMsgId(String msgId) { this.msgId = msgId; return this; } public String getContent() { return content; } public ChatRedirectToUserRequest setContent(String content) { this.content = content; return this; } @Override public String toString() { return "ChatRedirectToUserRequest{" + "msgId='" + msgId + '\'' + ", content='" + content + '\'' + '}'; } } ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-client/src/main/java/cn/iocoder/springboot/lab67/nettyclientdemo/message/chat/ChatSendResponse.java ================================================ package cn.iocoder.springboot.lab67.nettyclientdemo.message.chat; import cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.Message; /** * 聊天发送消息结果的 Response */ public class ChatSendResponse implements Message { public static final String TYPE = "CHAT_SEND_RESPONSE"; /** * 消息编号 */ private String msgId; /** * 响应状态码 */ private Integer code; /** * 响应提示 */ private String message; public String getMsgId() { return msgId; } public ChatSendResponse setMsgId(String msgId) { this.msgId = msgId; return this; } public Integer getCode() { return code; } public ChatSendResponse setCode(Integer code) { this.code = code; return this; } public String getMessage() { return message; } public ChatSendResponse setMessage(String message) { this.message = message; return this; } @Override public String toString() { return "ChatSendResponse{" + "msgId='" + msgId + '\'' + ", code=" + code + ", message='" + message + '\'' + '}'; } } ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-client/src/main/java/cn/iocoder/springboot/lab67/nettyclientdemo/message/chat/ChatSendToAllRequest.java ================================================ package cn.iocoder.springboot.lab67.nettyclientdemo.message.chat; import cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.Message; /** * 发送给所有人的群聊消息的 Message */ public class ChatSendToAllRequest implements Message { public static final String TYPE = "CHAT_SEND_TO_ALL_REQUEST"; /** * 消息编号 */ private String msgId; /** * 内容 */ private String content; public String getContent() { return content; } public ChatSendToAllRequest setContent(String content) { this.content = content; return this; } public String getMsgId() { return msgId; } public ChatSendToAllRequest setMsgId(String msgId) { this.msgId = msgId; return this; } @Override public String toString() { return "ChatSendToAllRequest{" + "msgId='" + msgId + '\'' + ", content='" + content + '\'' + '}'; } } ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-client/src/main/java/cn/iocoder/springboot/lab67/nettyclientdemo/message/chat/ChatSendToOneRequest.java ================================================ package cn.iocoder.springboot.lab67.nettyclientdemo.message.chat; import cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.Message; /** * 发送给指定人的私聊消息 Request */ public class ChatSendToOneRequest implements Message { public static final String TYPE = "CHAT_SEND_TO_ONE_REQUEST"; /** * 发送给的用户 */ private String toUser; /** * 消息编号 */ private String msgId; /** * 内容 */ private String content; public String getToUser() { return toUser; } public ChatSendToOneRequest setToUser(String toUser) { this.toUser = toUser; return this; } public String getMsgId() { return msgId; } public ChatSendToOneRequest setMsgId(String msgId) { this.msgId = msgId; return this; } public String getContent() { return content; } public ChatSendToOneRequest setContent(String content) { this.content = content; return this; } @Override public String toString() { return "ChatSendToOneRequest{" + "toUser='" + toUser + '\'' + ", msgId='" + msgId + '\'' + ", content='" + content + '\'' + '}'; } } ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-client/src/main/java/cn/iocoder/springboot/lab67/nettyclientdemo/message/heartbeat/HeartbeatRequest.java ================================================ package cn.iocoder.springboot.lab67.nettyclientdemo.message.heartbeat; import cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.Message; /** * 消息 - 心跳请求 */ public class HeartbeatRequest implements Message { /** * 类型 - 心跳请求 */ public static final String TYPE = "HEARTBEAT_REQUEST"; @Override public String toString() { return "HeartbeatRequest{}"; } } ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-client/src/main/java/cn/iocoder/springboot/lab67/nettyclientdemo/message/heartbeat/HeartbeatResponse.java ================================================ package cn.iocoder.springboot.lab67.nettyclientdemo.message.heartbeat; import cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.Message; /** * 消息 - 心跳响应 */ public class HeartbeatResponse implements Message { /** * 类型 - 心跳响应 */ public static final String TYPE = "HEARTBEAT_RESPONSE"; @Override public String toString() { return "HeartbeatResponse{}"; } } ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-client/src/main/java/cn/iocoder/springboot/lab67/nettyclientdemo/messagehandler/auth/AuthResponseHandler.java ================================================ package cn.iocoder.springboot.lab67.nettyclientdemo.messagehandler.auth; import cn.iocoder.springboot.lab67.nettyclientdemo.message.auth.AuthResponse; import cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.MessageHandler; import io.netty.channel.Channel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component public class AuthResponseHandler implements MessageHandler { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public void execute(Channel channel, AuthResponse message) { logger.info("[execute][认证结果:{}]", message); } @Override public String getType() { return AuthResponse.TYPE; } } ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-client/src/main/java/cn/iocoder/springboot/lab67/nettyclientdemo/messagehandler/chat/ChatRedirectToUserRequestHandler.java ================================================ package cn.iocoder.springboot.lab67.nettyclientdemo.messagehandler.chat; import cn.iocoder.springboot.lab67.nettyclientdemo.message.chat.ChatRedirectToUserRequest; import cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.MessageHandler; import io.netty.channel.Channel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component public class ChatRedirectToUserRequestHandler implements MessageHandler { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public void execute(Channel channel, ChatRedirectToUserRequest message) { logger.info("[execute][收到消息:{}]", message); } @Override public String getType() { return ChatRedirectToUserRequest.TYPE; } } ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-client/src/main/java/cn/iocoder/springboot/lab67/nettyclientdemo/messagehandler/chat/ChatSendResponseHandler.java ================================================ package cn.iocoder.springboot.lab67.nettyclientdemo.messagehandler.chat; import cn.iocoder.springboot.lab67.nettyclientdemo.message.chat.ChatSendResponse; import cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.MessageHandler; import io.netty.channel.Channel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component public class ChatSendResponseHandler implements MessageHandler { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public void execute(Channel channel, ChatSendResponse message) { logger.info("[execute][发送结果:{}]", message); } @Override public String getType() { return ChatSendResponse.TYPE; } } ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-client/src/main/java/cn/iocoder/springboot/lab67/nettyclientdemo/messagehandler/heartbeat/HeartbeatResponseHandler.java ================================================ package cn.iocoder.springboot.lab67.nettyclientdemo.messagehandler.heartbeat; import cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.MessageHandler; import cn.iocoder.springboot.lab67.nettyclientdemo.message.heartbeat.HeartbeatResponse; import io.netty.channel.Channel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component public class HeartbeatResponseHandler implements MessageHandler { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public void execute(Channel channel, HeartbeatResponse message) { logger.info("[execute][收到连接({}) 的心跳响应]", channel.id()); } @Override public String getType() { return HeartbeatResponse.TYPE; } } ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-client/src/main/resources/application.yml ================================================ netty: server: host: 127.0.0.1 # Netty Server 地址 port: 8888 # Netty Server 端口 ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-common/pom.xml ================================================ lab-67-netty-demo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-67-netty-demo-common 1.8 1.8 io.netty netty-all 4.1.50.Final com.alibaba fastjson 1.2.71 org.springframework spring-aop 5.2.5.RELEASE org.springframework spring-context 5.2.5.RELEASE org.slf4j slf4j-api 1.7.30 ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-common/src/main/java/cn/iocoder/springboot/lab67/nettycommondemo/codec/Invocation.java ================================================ package cn.iocoder.springboot.lab67.nettycommondemo.codec; import cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.Message; import com.alibaba.fastjson.JSON; /** * 通信协议的消息体 */ public class Invocation { /** * 类型 */ private String type; /** * 消息,JSON 格式 */ private String message; // 空构造方法 public Invocation() { } public Invocation(String type, String message) { this.type = type; this.message = message; } public Invocation(String type, Message message) { this.type = type; this.message = JSON.toJSONString(message); } public String getType() { return type; } public Invocation setType(String type) { this.type = type; return this; } public String getMessage() { return message; } public Invocation setMessage(String message) { this.message = message; return this; } @Override public String toString() { return "Invocation{" + "type='" + type + '\'' + ", message='" + message + '\'' + '}'; } } ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-common/src/main/java/cn/iocoder/springboot/lab67/nettycommondemo/codec/InvocationDecoder.java ================================================ package cn.iocoder.springboot.lab67.nettycommondemo.codec; import com.alibaba.fastjson.JSON; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.handler.codec.CorruptedFrameException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.List; /** * {@link Invocation} 解码器 */ public class InvocationDecoder extends ByteToMessageDecoder { private Logger logger = LoggerFactory.getLogger(getClass()); @Override protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) { // 标记当前读取位置 in.markReaderIndex(); // 判断是否能够读取 length 长度 if (in.readableBytes() <= 4) { return; } // 读取长度 int length = in.readInt(); if (length < 0) { throw new CorruptedFrameException("negative length: " + length); } // 如果 message 不够可读,则退回到原读取位置 if (in.readableBytes() < length) { in.resetReaderIndex(); return; } // 读取内容 byte[] content = new byte[length]; in.readBytes(content); // 解析成 Invocation Invocation invocation = JSON.parseObject(content, Invocation.class); out.add(invocation); logger.info("[decode][连接({}) 解析到一条消息({})]", ctx.channel().id(), invocation.toString()); } } ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-common/src/main/java/cn/iocoder/springboot/lab67/nettycommondemo/codec/InvocationEncoder.java ================================================ package cn.iocoder.springboot.lab67.nettycommondemo.codec; import com.alibaba.fastjson.JSON; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * {@link Invocation} 编码器 */ public class InvocationEncoder extends MessageToByteEncoder { private Logger logger = LoggerFactory.getLogger(getClass()); @Override protected void encode(ChannelHandlerContext ctx, Invocation invocation, ByteBuf out) { // 将 Invocation 转换成 byte[] 数组 byte[] content = JSON.toJSONBytes(invocation); // 写入 length out.writeInt(content.length); // 写入内容 out.writeBytes(content); logger.info("[encode][连接({}) 编码了一条消息({})]", ctx.channel().id(), invocation.toString()); } } ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-common/src/main/java/cn/iocoder/springboot/lab67/nettycommondemo/dispatcher/Message.java ================================================ package cn.iocoder.springboot.lab67.nettycommondemo.dispatcher; /** * 消息接口 */ public interface Message { } ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-common/src/main/java/cn/iocoder/springboot/lab67/nettycommondemo/dispatcher/MessageDispatcher.java ================================================ package cn.iocoder.springboot.lab67.nettycommondemo.dispatcher; import cn.iocoder.springboot.lab67.nettycommondemo.codec.Invocation; import com.alibaba.fastjson.JSON; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import org.springframework.beans.factory.annotation.Autowired; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @ChannelHandler.Sharable public class MessageDispatcher extends SimpleChannelInboundHandler { @Autowired private MessageHandlerContainer messageHandlerContainer; private final ExecutorService executor = Executors.newFixedThreadPool(200); @Override protected void channelRead0(ChannelHandlerContext ctx, Invocation invocation) { // 获得 type 对应的 MessageHandler 处理器 MessageHandler messageHandler = messageHandlerContainer.getMessageHandler(invocation.getType()); // 获得 MessageHandler 处理器 的消息类 Class messageClass = MessageHandlerContainer.getMessageClass(messageHandler); // 解析消息 Message message = JSON.parseObject(invocation.getMessage(), messageClass); // 执行逻辑 executor.submit(new Runnable() { @Override public void run() { // noinspection unchecked messageHandler.execute(ctx.channel(), message); } }); } } ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-common/src/main/java/cn/iocoder/springboot/lab67/nettycommondemo/dispatcher/MessageHandler.java ================================================ package cn.iocoder.springboot.lab67.nettycommondemo.dispatcher; import io.netty.channel.Channel; public interface MessageHandler { /** * 执行处理消息 * * @param channel 通道 * @param message 消息 */ void execute(Channel channel, T message); /** * @return 消息类型,即每个 Message 实现类上的 TYPE 静态字段 */ String getType(); } ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-common/src/main/java/cn/iocoder/springboot/lab67/nettycommondemo/dispatcher/MessageHandlerContainer.java ================================================ package cn.iocoder.springboot.lab67.nettycommondemo.dispatcher; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.aop.framework.AopProxyUtils; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.HashMap; import java.util.Map; import java.util.Objects; public class MessageHandlerContainer implements InitializingBean { private Logger logger = LoggerFactory.getLogger(getClass()); /** * 消息类型与 MessageHandler 的映射 */ private final Map handlers = new HashMap<>(); @Autowired private ApplicationContext applicationContext; @Override public void afterPropertiesSet() throws Exception { // 通过 ApplicationContext 获得所有 MessageHandler Bean applicationContext.getBeansOfType(MessageHandler.class).values() // 获得所有 MessageHandler Bean .forEach(messageHandler -> handlers.put(messageHandler.getType(), messageHandler)); // 添加到 handlers 中 logger.info("[afterPropertiesSet][消息处理器数量:{}]", handlers.size()); } /** * 获得类型对应的 MessageHandler * * @param type 类型 * @return MessageHandler */ MessageHandler getMessageHandler(String type) { MessageHandler handler = handlers.get(type); if (handler == null) { throw new IllegalArgumentException(String.format("类型(%s) 找不到匹配的 MessageHandler 处理器", type)); } return handler; } /** * 获得 MessageHandler 处理的消息类 * * @param handler 处理器 * @return 消息类 */ static Class getMessageClass(MessageHandler handler) { // 获得 Bean 对应的 Class 类名。因为有可能被 AOP 代理过。 Class targetClass = AopProxyUtils.ultimateTargetClass(handler); // 获得接口的 Type 数组 Type[] interfaces = targetClass.getGenericInterfaces(); Class superclass = targetClass.getSuperclass(); while ((Objects.isNull(interfaces) || 0 == interfaces.length) && Objects.nonNull(superclass)) { // 此处,是以父类的接口为准 interfaces = superclass.getGenericInterfaces(); superclass = targetClass.getSuperclass(); } if (Objects.nonNull(interfaces)) { // 遍历 interfaces 数组 for (Type type : interfaces) { // 要求 type 是泛型参数 if (type instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) type; // 要求是 MessageHandler 接口 if (Objects.equals(parameterizedType.getRawType(), MessageHandler.class)) { Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); // 取首个元素 if (Objects.nonNull(actualTypeArguments) && actualTypeArguments.length > 0) { return (Class) actualTypeArguments[0]; } else { throw new IllegalStateException(String.format("类型(%s) 获得不到消息类型", handler)); } } } } } throw new IllegalStateException(String.format("类型(%s) 获得不到消息类型", handler)); } } ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-server/pom.xml ================================================ lab-67-netty-demo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-67-netty-demo-server 2.2.4.RELEASE 1.8 1.8 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.boot spring-boot-starter io.netty netty-all 4.1.50.Final cn.iocoder.springboot.labs lab-67-netty-demo-common 1.0-SNAPSHOT ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-server/src/main/java/cn/iocoder/springboot/lab67/nettyserverdemo/NettyServerApplication.java ================================================ package cn.iocoder.springboot.lab67.nettyserverdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class NettyServerApplication { public static void main(String[] args) { SpringApplication.run(NettyServerApplication.class, args); } } ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-server/src/main/java/cn/iocoder/springboot/lab67/nettyserverdemo/config/NettyServerConfig.java ================================================ package cn.iocoder.springboot.lab67.nettyserverdemo.config; import cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.MessageDispatcher; import cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.MessageHandlerContainer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class NettyServerConfig { @Bean public MessageDispatcher messageDispatcher() { return new MessageDispatcher(); } @Bean public MessageHandlerContainer messageHandlerContainer() { return new MessageHandlerContainer(); } } ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-server/src/main/java/cn/iocoder/springboot/lab67/nettyserverdemo/message/auth/AuthRequest.java ================================================ package cn.iocoder.springboot.lab67.nettyserverdemo.message.auth; import cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.Message; /** * 用户认证请求 */ public class AuthRequest implements Message { public static final String TYPE = "AUTH_REQUEST"; /** * 认证 Token */ private String accessToken; public String getAccessToken() { return accessToken; } public AuthRequest setAccessToken(String accessToken) { this.accessToken = accessToken; return this; } @Override public String toString() { return "AuthRequest{" + "accessToken='" + accessToken + '\'' + '}'; } } ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-server/src/main/java/cn/iocoder/springboot/lab67/nettyserverdemo/message/auth/AuthResponse.java ================================================ package cn.iocoder.springboot.lab67.nettyserverdemo.message.auth; import cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.Message; /** * 用户认证响应 */ public class AuthResponse implements Message { public static final String TYPE = "AUTH_RESPONSE"; /** * 响应状态码 */ private Integer code; /** * 响应提示 */ private String message; public Integer getCode() { return code; } public AuthResponse setCode(Integer code) { this.code = code; return this; } public String getMessage() { return message; } public AuthResponse setMessage(String message) { this.message = message; return this; } @Override public String toString() { return "AuthResponse{" + "code=" + code + ", message='" + message + '\'' + '}'; } } ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-server/src/main/java/cn/iocoder/springboot/lab67/nettyserverdemo/message/chat/ChatRedirectToUserRequest.java ================================================ package cn.iocoder.springboot.lab67.nettyserverdemo.message.chat; import cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.Message; /** * 转发消息给一个用户的 Message */ public class ChatRedirectToUserRequest implements Message { public static final String TYPE = "CHAT_REDIRECT_TO_USER_REQUEST"; /** * 消息编号 */ private String msgId; /** * 内容 */ private String content; public String getMsgId() { return msgId; } public ChatRedirectToUserRequest setMsgId(String msgId) { this.msgId = msgId; return this; } public String getContent() { return content; } public ChatRedirectToUserRequest setContent(String content) { this.content = content; return this; } @Override public String toString() { return "ChatRedirectToUserRequest{" + "msgId='" + msgId + '\'' + ", content='" + content + '\'' + '}'; } } ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-server/src/main/java/cn/iocoder/springboot/lab67/nettyserverdemo/message/chat/ChatSendResponse.java ================================================ package cn.iocoder.springboot.lab67.nettyserverdemo.message.chat; import cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.Message; /** * 聊天发送消息结果的 Response */ public class ChatSendResponse implements Message { public static final String TYPE = "CHAT_SEND_RESPONSE"; /** * 消息编号 */ private String msgId; /** * 响应状态码 */ private Integer code; /** * 响应提示 */ private String message; public String getMsgId() { return msgId; } public ChatSendResponse setMsgId(String msgId) { this.msgId = msgId; return this; } public Integer getCode() { return code; } public ChatSendResponse setCode(Integer code) { this.code = code; return this; } public String getMessage() { return message; } public ChatSendResponse setMessage(String message) { this.message = message; return this; } @Override public String toString() { return "ChatSendResponse{" + "msgId='" + msgId + '\'' + ", code=" + code + ", message='" + message + '\'' + '}'; } } ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-server/src/main/java/cn/iocoder/springboot/lab67/nettyserverdemo/message/chat/ChatSendToAllRequest.java ================================================ package cn.iocoder.springboot.lab67.nettyserverdemo.message.chat; import cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.Message; /** * 发送给所有人的群聊消息的 Message */ public class ChatSendToAllRequest implements Message { public static final String TYPE = "CHAT_SEND_TO_ALL_REQUEST"; /** * 消息编号 */ private String msgId; /** * 内容 */ private String content; public String getContent() { return content; } public ChatSendToAllRequest setContent(String content) { this.content = content; return this; } public String getMsgId() { return msgId; } public ChatSendToAllRequest setMsgId(String msgId) { this.msgId = msgId; return this; } @Override public String toString() { return "ChatSendToAllRequest{" + "msgId='" + msgId + '\'' + ", content='" + content + '\'' + '}'; } } ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-server/src/main/java/cn/iocoder/springboot/lab67/nettyserverdemo/message/chat/ChatSendToOneRequest.java ================================================ package cn.iocoder.springboot.lab67.nettyserverdemo.message.chat; import cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.Message; /** * 发送给指定人的私聊消息 Request */ public class ChatSendToOneRequest implements Message { public static final String TYPE = "CHAT_SEND_TO_ONE_REQUEST"; /** * 发送给的用户 */ private String toUser; /** * 消息编号 */ private String msgId; /** * 内容 */ private String content; public String getToUser() { return toUser; } public ChatSendToOneRequest setToUser(String toUser) { this.toUser = toUser; return this; } public String getMsgId() { return msgId; } public ChatSendToOneRequest setMsgId(String msgId) { this.msgId = msgId; return this; } public String getContent() { return content; } public ChatSendToOneRequest setContent(String content) { this.content = content; return this; } @Override public String toString() { return "ChatSendToOneRequest{" + "toUser='" + toUser + '\'' + ", msgId='" + msgId + '\'' + ", content='" + content + '\'' + '}'; } } ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-server/src/main/java/cn/iocoder/springboot/lab67/nettyserverdemo/message/heartbeat/HeartbeatRequest.java ================================================ package cn.iocoder.springboot.lab67.nettyserverdemo.message.heartbeat; import cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.Message; /** * 消息 - 心跳请求 */ public class HeartbeatRequest implements Message { /** * 类型 - 心跳请求 */ public static final String TYPE = "HEARTBEAT_REQUEST"; @Override public String toString() { return "HeartbeatRequest{}"; } } ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-server/src/main/java/cn/iocoder/springboot/lab67/nettyserverdemo/message/heartbeat/HeartbeatResponse.java ================================================ package cn.iocoder.springboot.lab67.nettyserverdemo.message.heartbeat; import cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.Message; /** * 消息 - 心跳响应 */ public class HeartbeatResponse implements Message { /** * 类型 - 心跳响应 */ public static final String TYPE = "HEARTBEAT_RESPONSE"; @Override public String toString() { return "HeartbeatResponse{}"; } } ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-server/src/main/java/cn/iocoder/springboot/lab67/nettyserverdemo/messagehandler/auth/AuthRequestHandler.java ================================================ package cn.iocoder.springboot.lab67.nettyserverdemo.messagehandler.auth; import cn.iocoder.springboot.lab67.nettycommondemo.codec.Invocation; import cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.MessageHandler; import cn.iocoder.springboot.lab67.nettyserverdemo.message.auth.AuthRequest; import cn.iocoder.springboot.lab67.nettyserverdemo.message.auth.AuthResponse; import cn.iocoder.springboot.lab67.nettyserverdemo.server.NettyChannelManager; import io.netty.channel.Channel; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; @Component public class AuthRequestHandler implements MessageHandler { @Autowired private NettyChannelManager nettyChannelManager; @Override public void execute(Channel channel, AuthRequest authRequest) { // 如果未传递 accessToken if (StringUtils.isEmpty(authRequest.getAccessToken())) { AuthResponse authResponse = new AuthResponse().setCode(1).setMessage("认证 accessToken 未传入"); channel.writeAndFlush(new Invocation(AuthResponse.TYPE, authResponse)); return; } // ... 此处应有一段 // 将用户和 Channel 绑定 // 考虑到代码简化,我们先直接使用 accessToken 作为 User nettyChannelManager.addUser(channel, authRequest.getAccessToken()); // 响应认证成功 AuthResponse authResponse = new AuthResponse().setCode(0); channel.writeAndFlush(new Invocation(AuthResponse.TYPE, authResponse)); } @Override public String getType() { return AuthRequest.TYPE; } } ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-server/src/main/java/cn/iocoder/springboot/lab67/nettyserverdemo/messagehandler/chat/ChatSendToAllHandler.java ================================================ package cn.iocoder.springboot.lab67.nettyserverdemo.messagehandler.chat; import cn.iocoder.springboot.lab67.nettycommondemo.codec.Invocation; import cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.MessageHandler; import cn.iocoder.springboot.lab67.nettyserverdemo.message.chat.ChatSendResponse; import cn.iocoder.springboot.lab67.nettyserverdemo.message.chat.ChatSendToAllRequest; import cn.iocoder.springboot.lab67.nettyserverdemo.message.chat.ChatRedirectToUserRequest; import cn.iocoder.springboot.lab67.nettyserverdemo.server.NettyChannelManager; import io.netty.channel.Channel; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class ChatSendToAllHandler implements MessageHandler { @Autowired private NettyChannelManager nettyChannelManager; @Override public void execute(Channel channel, ChatSendToAllRequest message) { // 这里,假装直接成功 ChatSendResponse sendResponse = new ChatSendResponse().setMsgId(message.getMsgId()).setCode(0); channel.writeAndFlush(new Invocation(ChatSendResponse.TYPE, sendResponse)); // 创建转发的消息,并广播发送 ChatRedirectToUserRequest sendToUserRequest = new ChatRedirectToUserRequest().setMsgId(message.getMsgId()) .setContent(message.getContent()); nettyChannelManager.sendAll(new Invocation(ChatRedirectToUserRequest.TYPE, sendToUserRequest)); } @Override public String getType() { return ChatSendToAllRequest.TYPE; } } ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-server/src/main/java/cn/iocoder/springboot/lab67/nettyserverdemo/messagehandler/chat/ChatSendToOneHandler.java ================================================ package cn.iocoder.springboot.lab67.nettyserverdemo.messagehandler.chat; import cn.iocoder.springboot.lab67.nettycommondemo.codec.Invocation; import cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.MessageHandler; import cn.iocoder.springboot.lab67.nettyserverdemo.message.chat.ChatSendResponse; import cn.iocoder.springboot.lab67.nettyserverdemo.message.chat.ChatSendToOneRequest; import cn.iocoder.springboot.lab67.nettyserverdemo.message.chat.ChatRedirectToUserRequest; import cn.iocoder.springboot.lab67.nettyserverdemo.server.NettyChannelManager; import io.netty.channel.Channel; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class ChatSendToOneHandler implements MessageHandler { @Autowired private NettyChannelManager nettyChannelManager; @Override public void execute(Channel channel, ChatSendToOneRequest message) { // 这里,假装直接成功 ChatSendResponse sendResponse = new ChatSendResponse().setMsgId(message.getMsgId()).setCode(0); channel.writeAndFlush(new Invocation(ChatSendResponse.TYPE, sendResponse)); // 创建转发的消息,发送给指定用户 ChatRedirectToUserRequest sendToUserRequest = new ChatRedirectToUserRequest().setMsgId(message.getMsgId()) .setContent(message.getContent()); nettyChannelManager.send(message.getToUser(), new Invocation(ChatRedirectToUserRequest.TYPE, sendToUserRequest)); } @Override public String getType() { return ChatSendToOneRequest.TYPE; } } ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-server/src/main/java/cn/iocoder/springboot/lab67/nettyserverdemo/messagehandler/heartbeat/HeartbeatRequestHandler.java ================================================ package cn.iocoder.springboot.lab67.nettyserverdemo.messagehandler.heartbeat; import cn.iocoder.springboot.lab67.nettycommondemo.codec.Invocation; import cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.MessageHandler; import cn.iocoder.springboot.lab67.nettyserverdemo.message.heartbeat.HeartbeatRequest; import cn.iocoder.springboot.lab67.nettyserverdemo.message.heartbeat.HeartbeatResponse; import io.netty.channel.Channel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component public class HeartbeatRequestHandler implements MessageHandler { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public void execute(Channel channel, HeartbeatRequest message) { logger.info("[execute][收到连接({}) 的心跳请求]", channel.id()); // 响应心跳 HeartbeatResponse response = new HeartbeatResponse(); channel.writeAndFlush(new Invocation(HeartbeatResponse.TYPE, response)); } @Override public String getType() { return HeartbeatRequest.TYPE; } } ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-server/src/main/java/cn/iocoder/springboot/lab67/nettyserverdemo/server/NettyChannelManager.java ================================================ package cn.iocoder.springboot.lab67.nettyserverdemo.server; import cn.iocoder.springboot.lab67.nettycommondemo.codec.Invocation; import io.netty.channel.Channel; import io.netty.channel.ChannelId; import io.netty.util.AttributeKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * 客户端 Channel 管理器。提供两种功能: * 1. 客户端 Channel 的管理 * 2. 向客户端 Channel 发送消息 */ @Component public class NettyChannelManager { /** * {@link Channel#attr(AttributeKey)} 属性中,表示 Channel 对应的用户 */ private static final AttributeKey CHANNEL_ATTR_KEY_USER = AttributeKey.newInstance("user"); private Logger logger = LoggerFactory.getLogger(getClass()); /** * Channel 映射 */ private ConcurrentMap channels = new ConcurrentHashMap<>(); /** * 用户与 Channel 的映射。 * * 通过它,可以获取用户对应的 Channel。这样,我们可以向指定用户发送消息。 */ private ConcurrentMap userChannels = new ConcurrentHashMap<>(); /** * 添加 Channel 到 {@link #channels} 中 * * @param channel Channel */ public void add(Channel channel) { channels.put(channel.id(), channel); logger.info("[add][一个连接({})加入]", channel.id()); } /** * 添加指定用户到 {@link #userChannels} 中 * * @param channel Channel * @param user 用户 */ public void addUser(Channel channel, String user) { Channel existChannel = channels.get(channel.id()); if (existChannel == null) { logger.error("[addUser][连接({}) 不存在]", channel.id()); return; } // 设置属性 channel.attr(CHANNEL_ATTR_KEY_USER).set(user); // 添加到 userChannels userChannels.put(user, channel); } /** * 将 Channel 从 {@link #channels} 和 {@link #userChannels} 中移除 * * @param channel Channel */ public void remove(Channel channel) { // 移除 channels channels.remove(channel.id()); // 移除 userChannels if (channel.hasAttr(CHANNEL_ATTR_KEY_USER)) { userChannels.remove(channel.attr(CHANNEL_ATTR_KEY_USER).get()); } logger.info("[remove][一个连接({})离开]", channel.id()); } /** * 向指定用户发送消息 * * @param user 用户 * @param invocation 消息体 */ public void send(String user, Invocation invocation) { // 获得用户对应的 Channel Channel channel = userChannels.get(user); if (channel == null) { logger.error("[send][连接不存在]"); return; } if (!channel.isActive()) { logger.error("[send][连接({})未激活]", channel.id()); return; } // 发送消息 channel.writeAndFlush(invocation); } /** * 向所有用户发送消息 * * @param invocation 消息体 */ public void sendAll(Invocation invocation) { for (Channel channel : channels.values()) { if (!channel.isActive()) { logger.error("[send][连接({})未激活]", channel.id()); return; } // 发送消息 channel.writeAndFlush(invocation); } } } ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-server/src/main/java/cn/iocoder/springboot/lab67/nettyserverdemo/server/NettyServer.java ================================================ package cn.iocoder.springboot.lab67.nettyserverdemo.server; import cn.iocoder.springboot.lab67.nettyserverdemo.server.handler.NettyServerHandlerInitializer; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import java.net.InetSocketAddress; @Component public class NettyServer { private Logger logger = LoggerFactory.getLogger(getClass()); @Value("${netty.port}") private Integer port; @Autowired private NettyServerHandlerInitializer nettyServerHandlerInitializer; /** * boss 线程组,用于服务端接受客户端的连接 */ private EventLoopGroup bossGroup = new NioEventLoopGroup(); /** * worker 线程组,用于服务端接受客户端的数据读写 */ private EventLoopGroup workerGroup = new NioEventLoopGroup(); /** * Netty Server Channel */ private Channel channel; /** * 启动 Netty Server */ @PostConstruct public void start() throws InterruptedException { // 创建 ServerBootstrap 对象,用于 Netty Server 启动 ServerBootstrap bootstrap = new ServerBootstrap(); // 设置 ServerBootstrap 的各种属性 bootstrap.group(bossGroup, workerGroup) // 设置两个 EventLoopGroup 对象 .channel(NioServerSocketChannel.class) // 指定 Channel 为服务端 NioServerSocketChannel .localAddress(new InetSocketAddress(port)) // 设置 Netty Server 的端口 .option(ChannelOption.SO_BACKLOG, 1024) // 服务端 accept 队列的大小 .childOption(ChannelOption.SO_KEEPALIVE, true) // TCP Keepalive 机制,实现 TCP 层级的心跳保活功能 .childOption(ChannelOption.TCP_NODELAY, true) // 允许较小的数据包的发送,降低延迟 .childHandler(nettyServerHandlerInitializer); // 绑定端口,并同步等待成功,即启动服务端 ChannelFuture future = bootstrap.bind().sync(); if (future.isSuccess()) { channel = future.channel(); logger.info("[start][Netty Server 启动在 {} 端口]", port); } } /** * 关闭 Netty Server */ @PreDestroy public void shutdown() { // 关闭 Netty Server if (channel != null) { channel.close(); } // 优雅关闭两个 EventLoopGroup 对象 bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-server/src/main/java/cn/iocoder/springboot/lab67/nettyserverdemo/server/handler/NettyServerHandler.java ================================================ package cn.iocoder.springboot.lab67.nettyserverdemo.server.handler; import cn.iocoder.springboot.lab67.nettyserverdemo.server.NettyChannelManager; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * 服务端 Channel 实现类,提供对客户端 Channel 建立连接、断开连接、异常时的处理 */ @Component @ChannelHandler.Sharable public class NettyServerHandler extends ChannelInboundHandlerAdapter { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private NettyChannelManager channelManager; @Override public void channelActive(ChannelHandlerContext ctx) { // 从管理器中添加 channelManager.add(ctx.channel()); } @Override public void channelUnregistered(ChannelHandlerContext ctx) { // 从管理器中移除 channelManager.remove(ctx.channel()); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { logger.error("[exceptionCaught][连接({}) 发生异常]", ctx.channel().id(), cause); // 断开连接 ctx.channel().close(); } } ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-server/src/main/java/cn/iocoder/springboot/lab67/nettyserverdemo/server/handler/NettyServerHandlerInitializer.java ================================================ package cn.iocoder.springboot.lab67.nettyserverdemo.server.handler; import cn.iocoder.springboot.lab67.nettycommondemo.codec.InvocationDecoder; import cn.iocoder.springboot.lab67.nettycommondemo.codec.InvocationEncoder; import cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.MessageDispatcher; import io.netty.channel.Channel; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.handler.timeout.ReadTimeoutHandler; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.concurrent.TimeUnit; @Component public class NettyServerHandlerInitializer extends ChannelInitializer { /** * 心跳超时时间 */ private static final Integer READ_TIMEOUT_SECONDS = 3 * 60; @Autowired private MessageDispatcher messageDispatcher; @Autowired private NettyServerHandler nettyServerHandler; @Override protected void initChannel(Channel ch) { // 获得 Channel 对应的 ChannelPipeline ChannelPipeline channelPipeline = ch.pipeline(); // 添加一堆 NettyServerHandler 到 ChannelPipeline 中 channelPipeline // 空闲检测 .addLast(new ReadTimeoutHandler(READ_TIMEOUT_SECONDS, TimeUnit.SECONDS)) // 编码器 .addLast(new InvocationEncoder()) // 解码器 .addLast(new InvocationDecoder()) // 消息分发器 .addLast(messageDispatcher) // 服务端处理器 .addLast(nettyServerHandler) ; } } ================================================ FILE: lab-67/lab-67-netty-demo/lab-67-netty-demo-server/src/main/resources/application.yml ================================================ netty: port: 8888 # Netty Server 端口 ================================================ FILE: lab-67/lab-67-netty-demo/pom.xml ================================================ lab-67 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-67-netty-demo pom lab-67-netty-demo-server lab-67-netty-demo-client lab-67-netty-demo-common ================================================ FILE: lab-67/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-67 pom lab-67-netty-demo ================================================ FILE: lab-67/《芋道 Spring Boot Netty 入门》.md ================================================ ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo01-authorization-code-server/pom.xml ================================================ lab-68-spring-security-oauth cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-68-demo01-authorization-code-server 2.2.4.RELEASE 1.8 1.8 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.security.oauth.boot spring-security-oauth2-autoconfigure ${spring.boot.version} ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo01-authorization-code-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/ResourceServerApplication.java ================================================ package cn.iocoder.springboot.lab68.resourceserverdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ResourceServerApplication { public static void main(String[] args) { SpringApplication.run(ResourceServerApplication.class, args); } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo01-authorization-code-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/config/OAuth2AuthorizationServerConfig.java ================================================ package cn.iocoder.springboot.lab68.resourceserverdemo.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; /** * 授权服务器配置 */ @Configuration @EnableAuthorizationServer public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { // 用户认证 @Autowired private AuthenticationManager authenticationManager; @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.authenticationManager(authenticationManager); } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("clientapp").secret("112233") // Client 账号、密码。 .authorizedGrantTypes("authorization_code") // 授权码模式 .redirectUris("http://127.0.0.1:9090/callback") // 配置回调地址,选填。 .scopes("read_userinfo", "read_contacts") // 可授权的 Scope // .and().withClient() // 可以继续配置新的 Client ; } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo01-authorization-code-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/config/OAuth2ResourceServerConfig.java ================================================ package cn.iocoder.springboot.lab68.resourceserverdemo.config; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; /** * 资源服务器配置 */ @Configuration @EnableResourceServer public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter { @Override public void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest().authenticated() // 设置 /api/ 开头的 URL 需要保护 .and().requestMatchers().antMatchers("/api/**"); } } // 实际,OAuth2ResourceServer 不是和 OAuth2AuthorizationServer 一起。 // 主要考虑,简化 demo ,所以改成这样。 ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo01-authorization-code-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/config/SecurityConfig.java ================================================ package cn.iocoder.springboot.lab68.resourceserverdemo.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.BeanIds; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.password.NoOpPasswordEncoder; @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override @Bean(name = BeanIds.AUTHENTICATION_MANAGER) public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Bean public static NoOpPasswordEncoder passwordEncoder() { return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth. // 使用内存中的 InMemoryUserDetailsManager inMemoryAuthentication() // 不使用 PasswordEncoder 密码编码器 .passwordEncoder(passwordEncoder()) // 配置 yunai 用户 .withUser("yunai").password("1024").roles("USER"); } // @Override // protected void configure(HttpSecurity http) throws Exception { // http.authorizeRequests() // // 对所有 URL 都进行认证 // .anyRequest() // .authenticated(); // } // @Override // public void configure(HttpSecurity http) throws Exception { // http.csrf() // .disable() // .authorizeRequests() // .antMatchers("/oauth/**", "/login/**", "/logout/**").permitAll() // .anyRequest().authenticated() // .and().formLogin().permitAll(); // } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo01-authorization-code-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/controller/ExampleController.java ================================================ package cn.iocoder.springboot.lab68.resourceserverdemo.controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * 示例模块 Controller */ @RestController @RequestMapping("/api/example") public class ExampleController { @RequestMapping("/hello") public String hello() { return "world"; } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo01-client-credentials-server/pom.xml ================================================ lab-68-spring-security-oauth cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-68-demo01-client-credentials-server 2.2.4.RELEASE 1.8 1.8 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.security.oauth.boot spring-security-oauth2-autoconfigure ${spring.boot.version} ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo01-client-credentials-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/ResourceServerApplication.java ================================================ package cn.iocoder.springboot.lab68.resourceserverdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ResourceServerApplication { public static void main(String[] args) { SpringApplication.run(ResourceServerApplication.class, args); } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo01-client-credentials-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/config/OAuth2AuthorizationServerConfig.java ================================================ package cn.iocoder.springboot.lab68.resourceserverdemo.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.crypto.password.NoOpPasswordEncoder; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; /** * 授权服务器配置 */ @Configuration @EnableAuthorizationServer public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Bean public static NoOpPasswordEncoder passwordEncoder() { return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance(); } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("clientapp").secret("112233") // Client 账号、密码。 .authorizedGrantTypes("client_credentials") // 客户端模式 .scopes("read_userinfo", "read_contacts") // 可授权的 Scope // .and().withClient() // 可以继续配置新的 Client ; } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo01-client-credentials-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/config/OAuth2ResourceServerConfig.java ================================================ package cn.iocoder.springboot.lab68.resourceserverdemo.config; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; /** * 资源服务器配置 */ @Configuration @EnableResourceServer public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter { @Override public void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest().authenticated() // 设置 /api/ 开头的 URL 需要保护 .and().requestMatchers().antMatchers("/api/**"); } } // 实际,OAuth2ResourceServer 不是和 OAuth2AuthorizationServer 一起。 // 主要考虑,简化 demo ,所以改成这样。 ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo01-client-credentials-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/controller/ExampleController.java ================================================ package cn.iocoder.springboot.lab68.resourceserverdemo.controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * 示例模块 Controller */ @RestController @RequestMapping("/api/example") public class ExampleController { @RequestMapping("/hello") public String hello() { return "world"; } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo01-implicit-server/pom.xml ================================================ lab-68-spring-security-oauth cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-68-demo01-implicit-server 2.2.4.RELEASE 1.8 1.8 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.security.oauth.boot spring-security-oauth2-autoconfigure ${spring.boot.version} ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo01-implicit-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/ResourceServerApplication.java ================================================ package cn.iocoder.springboot.lab68.resourceserverdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ResourceServerApplication { public static void main(String[] args) { SpringApplication.run(ResourceServerApplication.class, args); } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo01-implicit-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/config/OAuth2AuthorizationServerConfig.java ================================================ package cn.iocoder.springboot.lab68.resourceserverdemo.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; /** * 授权服务器配置 */ @Configuration @EnableAuthorizationServer public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { // 用户认证 @Autowired private AuthenticationManager authenticationManager; @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.authenticationManager(authenticationManager); } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("clientapp").secret("112233") // Client 账号、密码。 .authorizedGrantTypes("implicit") // 授权码模式 .redirectUris("http://127.0.0.1:9090/callback02") // 配置回调地址,选填。 .scopes("read_userinfo", "read_contacts") // 可授权的 Scope // .and().withClient() // 可以继续配置新的 Client ; } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo01-implicit-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/config/OAuth2ResourceServerConfig.java ================================================ package cn.iocoder.springboot.lab68.resourceserverdemo.config; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; /** * 资源服务器配置 */ @Configuration @EnableResourceServer public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter { @Override public void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest().authenticated() // 设置 /api/ 开头的 URL 需要保护 .and().requestMatchers().antMatchers("/api/**"); } } // 实际,OAuth2ResourceServer 不是和 OAuth2AuthorizationServer 一起。 // 主要考虑,简化 demo ,所以改成这样。 ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo01-implicit-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/config/SecurityConfig.java ================================================ package cn.iocoder.springboot.lab68.resourceserverdemo.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.BeanIds; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.password.NoOpPasswordEncoder; @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override @Bean(name = BeanIds.AUTHENTICATION_MANAGER) public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Bean public static NoOpPasswordEncoder passwordEncoder() { return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth. // 使用内存中的 InMemoryUserDetailsManager inMemoryAuthentication() // 不使用 PasswordEncoder 密码编码器 .passwordEncoder(passwordEncoder()) // 配置 yunai 用户 .withUser("yunai").password("1024").roles("USER"); } // @Override // protected void configure(HttpSecurity http) throws Exception { // http.authorizeRequests() // // 对所有 URL 都进行认证 // .anyRequest() // .authenticated(); // } // @Override // public void configure(HttpSecurity http) throws Exception { // http.csrf() // .disable() // .authorizeRequests() // .antMatchers("/oauth/**", "/login/**", "/logout/**").permitAll() // .anyRequest().authenticated() // .and().formLogin().permitAll(); // } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo01-implicit-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/controller/ExampleController.java ================================================ package cn.iocoder.springboot.lab68.resourceserverdemo.controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * 示例模块 Controller */ @RestController @RequestMapping("/api/example") public class ExampleController { @RequestMapping("/hello") public String hello() { return "world"; } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo01-resource-owner-password-credentials-server/pom.xml ================================================ lab-68-spring-security-oauth cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-68-demo01-resource-owner-password-credentials-server 2.2.4.RELEASE 1.8 1.8 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.security.oauth.boot spring-security-oauth2-autoconfigure ${spring.boot.version} ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo01-resource-owner-password-credentials-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/ResourceServerApplication.java ================================================ package cn.iocoder.springboot.lab68.resourceserverdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ResourceServerApplication { public static void main(String[] args) { SpringApplication.run(ResourceServerApplication.class, args); } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo01-resource-owner-password-credentials-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/config/OAuth2AuthorizationServerConfig.java ================================================ package cn.iocoder.springboot.lab68.resourceserverdemo.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; /** * 授权服务器配置 */ @Configuration @EnableAuthorizationServer public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { // 用户认证 @Autowired private AuthenticationManager authenticationManager; @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.authenticationManager(authenticationManager); } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("clientapp").secret("112233") // Client 账号、密码。 .authorizedGrantTypes("password") // 密码模式 .scopes("read_userinfo", "read_contacts") // 可授权的 Scope // .and().withClient() // 可以继续配置新的 Client ; } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo01-resource-owner-password-credentials-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/config/OAuth2ResourceServerConfig.java ================================================ package cn.iocoder.springboot.lab68.resourceserverdemo.config; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; /** * 资源服务器配置 */ @Configuration @EnableResourceServer public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter { @Override public void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest().authenticated() // 设置 /api/ 开头的 URL 需要保护 .and().requestMatchers().antMatchers("/api/**"); } } // 实际,OAuth2ResourceServer 不是和 OAuth2AuthorizationServer 一起。 // 主要考虑,简化 demo ,所以改成这样。 ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo01-resource-owner-password-credentials-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/config/SecurityConfig.java ================================================ package cn.iocoder.springboot.lab68.resourceserverdemo.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.BeanIds; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.password.NoOpPasswordEncoder; @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override @Bean(name = BeanIds.AUTHENTICATION_MANAGER) public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Bean public static NoOpPasswordEncoder passwordEncoder() { return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth. // 使用内存中的 InMemoryUserDetailsManager inMemoryAuthentication() // 不使用 PasswordEncoder 密码编码器 .passwordEncoder(passwordEncoder()) // 配置 yunai 用户 .withUser("yunai").password("1024").roles("USER"); } // @Override // protected void configure(HttpSecurity http) throws Exception { // http.authorizeRequests() // // 对所有 URL 都进行认证 // .anyRequest() // .authenticated(); // } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo01-resource-owner-password-credentials-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/controller/ExampleController.java ================================================ package cn.iocoder.springboot.lab68.resourceserverdemo.controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * 示例模块 Controller */ @RestController @RequestMapping("/api/example") public class ExampleController { @RequestMapping("/hello") public String hello() { return "world"; } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo02-authorization-server-with-authorization-code/pom.xml ================================================ lab-68-spring-security-oauth cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-68-demo02-authorization-server-with-authorization-code 2.2.4.RELEASE 1.8 1.8 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.security.oauth.boot spring-security-oauth2-autoconfigure ${spring.boot.version} ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo02-authorization-server-with-authorization-code/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/AuthorizationServerApplication.java ================================================ package cn.iocoder.springboot.lab68.authorizationserverdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class AuthorizationServerApplication { public static void main(String[] args) { SpringApplication.run(AuthorizationServerApplication.class, args); } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo02-authorization-server-with-authorization-code/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/config/OAuth2AuthorizationServerConfig.java ================================================ package cn.iocoder.springboot.lab68.authorizationserverdemo.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; /** * 授权服务器配置 */ @Configuration @EnableAuthorizationServer public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { /** * 用户认证 Manager */ @Autowired private AuthenticationManager authenticationManager; @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.authenticationManager(authenticationManager); } @Override public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception { oauthServer.checkTokenAccess("isAuthenticated()") // .tokenKeyAccess("permitAll()") ; } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("clientapp").secret("112233") // Client 账号、密码。 .authorizedGrantTypes("authorization_code") // 授权码模式 .redirectUris("http://127.0.0.1:9090/callback") // 配置回调地址,选填。 .scopes("read_userinfo", "read_contacts") // 可授权的 Scope // .and().withClient() // 可以继续配置新的 Client ; } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo02-authorization-server-with-authorization-code/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/config/SecurityConfig.java ================================================ package cn.iocoder.springboot.lab68.authorizationserverdemo.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.BeanIds; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.password.NoOpPasswordEncoder; @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override @Bean(name = BeanIds.AUTHENTICATION_MANAGER) public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Bean public static NoOpPasswordEncoder passwordEncoder() { return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth. // 使用内存中的 InMemoryUserDetailsManager inMemoryAuthentication() // 不使用 PasswordEncoder 密码编码器 .passwordEncoder(passwordEncoder()) // 配置 yunai 用户 .withUser("yunai").password("1024").roles("USER"); } // @Override // protected void configure(HttpSecurity http) throws Exception { // http // .authorizeRequests() // .antMatchers("/oauth/**").permitAll() // 允许无权限访问 // .anyRequest().authenticated() // .and() // .formLogin().and() // .httpBasic(); // } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo02-authorization-server-with-client-credentials/pom.xml ================================================ lab-68-spring-security-oauth cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-68-demo02-authorization-server-with-client-credentials 2.2.4.RELEASE 1.8 1.8 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.security.oauth.boot spring-security-oauth2-autoconfigure ${spring.boot.version} ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo02-authorization-server-with-client-credentials/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/AuthorizationServerApplication.java ================================================ package cn.iocoder.springboot.lab68.authorizationserverdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class AuthorizationServerApplication { public static void main(String[] args) { SpringApplication.run(AuthorizationServerApplication.class, args); } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo02-authorization-server-with-client-credentials/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/config/OAuth2AuthorizationServerConfig.java ================================================ package cn.iocoder.springboot.lab68.authorizationserverdemo.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.crypto.password.NoOpPasswordEncoder; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; /** * 授权服务器配置 */ @Configuration @EnableAuthorizationServer public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { /** * 创建 PasswordEncoder Bean */ @Bean public static NoOpPasswordEncoder passwordEncoder() { return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance(); } @Override public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception { oauthServer.checkTokenAccess("isAuthenticated()"); // oauthServer.tokenKeyAccess("isAuthenticated()") // .checkTokenAccess("isAuthenticated()"); // oauthServer.tokenKeyAccess("permitAll()") // .checkTokenAccess("permitAll()"); } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("clientapp").secret("112233") // Client 账号、密码。 .authorizedGrantTypes("client_credentials") // 客户端模式 .scopes("read_userinfo", "read_contacts") // 可授权的 Scope // .and().withClient() // 可以继续配置新的 Client ; } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo02-authorization-server-with-implicit/pom.xml ================================================ lab-68-spring-security-oauth cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-68-demo02-authorization-server-with-implicit 2.2.4.RELEASE 1.8 1.8 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.security.oauth.boot spring-security-oauth2-autoconfigure ${spring.boot.version} ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo02-authorization-server-with-implicit/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/AuthorizationServerApplication.java ================================================ package cn.iocoder.springboot.lab68.authorizationserverdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class AuthorizationServerApplication { public static void main(String[] args) { SpringApplication.run(AuthorizationServerApplication.class, args); } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo02-authorization-server-with-implicit/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/config/OAuth2AuthorizationServerConfig.java ================================================ package cn.iocoder.springboot.lab68.authorizationserverdemo.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; /** * 授权服务器配置 */ @Configuration @EnableAuthorizationServer public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { /** * 用户认证 Manager */ @Autowired private AuthenticationManager authenticationManager; @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.authenticationManager(authenticationManager); } @Override public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception { oauthServer.checkTokenAccess("isAuthenticated()") // .tokenKeyAccess("permitAll()") ; } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("clientapp").secret("112233") // Client 账号、密码。 .authorizedGrantTypes("implicit") // 简化模式 .redirectUris("http://127.0.0.1:9090/callback02") // 配置回调地址,选填。 .scopes("read_userinfo", "read_contacts") // 可授权的 Scope // .and().withClient() // 可以继续配置新的 Client ; } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo02-authorization-server-with-implicit/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/config/SecurityConfig.java ================================================ package cn.iocoder.springboot.lab68.authorizationserverdemo.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.BeanIds; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.password.NoOpPasswordEncoder; @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override @Bean(name = BeanIds.AUTHENTICATION_MANAGER) public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Bean public static NoOpPasswordEncoder passwordEncoder() { return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth. // 使用内存中的 InMemoryUserDetailsManager inMemoryAuthentication() // 不使用 PasswordEncoder 密码编码器 .passwordEncoder(passwordEncoder()) // 配置 yunai 用户 .withUser("yunai").password("1024").roles("USER"); } // @Override // protected void configure(HttpSecurity http) throws Exception { // http // .authorizeRequests() // .antMatchers("/oauth/**").permitAll() // 允许无权限访问 // .anyRequest().authenticated() // .and() // .formLogin().and() // .httpBasic(); // } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo02-authorization-server-with-resource-owner-password-credentials/pom.xml ================================================ lab-68-spring-security-oauth cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-68-demo02-authorization-server-with-resource-owner-password-credentials 2.2.4.RELEASE 1.8 1.8 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.security.oauth.boot spring-security-oauth2-autoconfigure ${spring.boot.version} ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab68.authorizationserverdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class AuthorizationServerApplication { public static void main(String[] args) { SpringApplication.run(AuthorizationServerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab68.authorizationserverdemo.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; /** * 授权服务器配置 */ @Configuration @EnableAuthorizationServer public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { /** * 用户认证 Manager */ @Autowired private AuthenticationManager authenticationManager; @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.authenticationManager(authenticationManager); } @Override public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception { oauthServer.checkTokenAccess("isAuthenticated()"); // oauthServer.tokenKeyAccess("isAuthenticated()") // .checkTokenAccess("isAuthenticated()"); // oauthServer.tokenKeyAccess("permitAll()") // .checkTokenAccess("permitAll()"); } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("clientapp").secret("112233") // Client 账号、密码。 .authorizedGrantTypes("password") // 密码模式 .scopes("read_userinfo", "read_contacts") // 可授权的 Scope // .and().withClient() // 可以继续配置新的 Client ; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab68.authorizationserverdemo.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.BeanIds; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.password.NoOpPasswordEncoder; @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override @Bean(name = BeanIds.AUTHENTICATION_MANAGER) public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Bean public static NoOpPasswordEncoder passwordEncoder() { return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth. // 使用内存中的 InMemoryUserDetailsManager inMemoryAuthentication() // 不使用 PasswordEncoder 密码编码器 .passwordEncoder(passwordEncoder()) // 配置 yunai 用户 .withUser("yunai").password("1024").roles("USER"); } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo02-resource-server/pom.xml ================================================ lab-68-spring-security-oauth cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-68-demo02-resource-server 2.2.4.RELEASE 1.8 1.8 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.security.oauth.boot spring-security-oauth2-autoconfigure ${spring.boot.version} ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo02-resource-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/ResourceServerApplication.java ================================================ package cn.iocoder.springboot.lab68.resourceserverdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ResourceServerApplication { public static void main(String[] args) { SpringApplication.run(ResourceServerApplication.class, args); } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo02-resource-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/config/OAuth2ResourceServerConfig.java ================================================ package cn.iocoder.springboot.lab68.resourceserverdemo.config; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; /** * 资源服务器配置 */ @Configuration @EnableResourceServer public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter { @Override public void configure(HttpSecurity http) throws Exception { http.authorizeRequests() // 设置 /login 无需权限访问 .antMatchers("/login").permitAll() // 设置 /client-login 无需权限访问 .antMatchers("/client-login").permitAll() /// 设置 /callback 无需权限访问 .antMatchers("/callback").permitAll() // 设置 /callback02 无需权限访问 .antMatchers("/callback02").permitAll() // 设置其它请求,需要认证后访问 .anyRequest().authenticated() ; } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo02-resource-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/controller/Callback02Controller.java ================================================ package cn.iocoder.springboot.lab68.resourceserverdemo.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/") public class Callback02Controller { @GetMapping("/callback02") public String login() { return "假装这里有一个页面"; } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo02-resource-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/controller/CallbackController.java ================================================ package cn.iocoder.springboot.lab68.resourceserverdemo.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.security.oauth2.OAuth2ClientProperties; import org.springframework.security.oauth2.client.OAuth2RestTemplate; import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider; import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/") public class CallbackController { @Autowired private OAuth2ClientProperties oauth2ClientProperties; @Value("${security.oauth2.access-token-uri}") private String accessTokenUri; @GetMapping("/callback") public OAuth2AccessToken login(@RequestParam("code") String code) { // 创建 AuthorizationCodeResourceDetails 对象 AuthorizationCodeResourceDetails resourceDetails = new AuthorizationCodeResourceDetails(); resourceDetails.setAccessTokenUri(accessTokenUri); resourceDetails.setClientId(oauth2ClientProperties.getClientId()); resourceDetails.setClientSecret(oauth2ClientProperties.getClientSecret()); // 创建 OAuth2RestTemplate 对象 OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails); restTemplate.getOAuth2ClientContext().getAccessTokenRequest().setAuthorizationCode(code); // 设置 code restTemplate.getOAuth2ClientContext().getAccessTokenRequest().setPreservedState("http://127.0.0.1:9090/callback"); // 通过这个方式,设置 redirect_uri 参数 restTemplate.setAccessTokenProvider(new AuthorizationCodeAccessTokenProvider()); // 获取访问令牌 return restTemplate.getAccessToken(); } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo02-resource-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/controller/ClientLoginController.java ================================================ package cn.iocoder.springboot.lab68.resourceserverdemo.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.security.oauth2.OAuth2ClientProperties; import org.springframework.security.oauth2.client.OAuth2RestTemplate; import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsAccessTokenProvider; import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/") public class ClientLoginController { @Autowired private OAuth2ClientProperties oauth2ClientProperties; @Value("${security.oauth2.access-token-uri}") private String accessTokenUri; @PostMapping("/client-login") public OAuth2AccessToken login() { // 创建 ClientCredentialsResourceDetails 对象 ClientCredentialsResourceDetails resourceDetails = new ClientCredentialsResourceDetails(); resourceDetails.setAccessTokenUri(accessTokenUri); resourceDetails.setClientId(oauth2ClientProperties.getClientId()); resourceDetails.setClientSecret(oauth2ClientProperties.getClientSecret()); // 创建 OAuth2RestTemplate 对象 OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails); restTemplate.setAccessTokenProvider(new ClientCredentialsAccessTokenProvider()); // 获取访问令牌 return restTemplate.getAccessToken(); } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo02-resource-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/controller/ExampleController.java ================================================ package cn.iocoder.springboot.lab68.resourceserverdemo.controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * 示例模块 Controller */ @RestController @RequestMapping("/api/example") public class ExampleController { @RequestMapping("/hello") public String hello() { return "world"; } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo02-resource-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/controller/LoginController.java ================================================ package cn.iocoder.springboot.lab68.resourceserverdemo.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.security.oauth2.OAuth2ClientProperties; import org.springframework.security.oauth2.client.OAuth2RestTemplate; import org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordAccessTokenProvider; import org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordResourceDetails; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/") public class LoginController { @Autowired private OAuth2ClientProperties oauth2ClientProperties; @Value("${security.oauth2.access-token-uri}") private String accessTokenUri; @PostMapping("/login") public OAuth2AccessToken login(@RequestParam("username") String username, @RequestParam("password") String password) { // 创建 ResourceOwnerPasswordResourceDetails 对象 ResourceOwnerPasswordResourceDetails resourceDetails = new ResourceOwnerPasswordResourceDetails(); resourceDetails.setAccessTokenUri(accessTokenUri); resourceDetails.setClientId(oauth2ClientProperties.getClientId()); resourceDetails.setClientSecret(oauth2ClientProperties.getClientSecret()); resourceDetails.setUsername(username); resourceDetails.setPassword(password); // 创建 OAuth2RestTemplate 对象 OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails); restTemplate.setAccessTokenProvider(new ResourceOwnerPasswordAccessTokenProvider()); // 获取访问令牌 return restTemplate.getAccessToken(); } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo02-resource-server/src/main/resources/application.yml ================================================ server: port: 9090 security: oauth2: # OAuth2 Client 配置,对应 OAuth2ClientProperties 类 client: client-id: clientapp client-secret: 112233 # OAuth2 Resource 配置,对应 ResourceServerProperties 类 resource: token-info-uri: http://127.0.0.1:8080/oauth/check_token # 获得 Token 信息的 URL # 访问令牌获取 URL,自定义的 access-token-uri: http://127.0.0.1:8080/oauth/token ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo03-authorization-server-with-resource-owner-password-credentials/pom.xml ================================================ lab-68-spring-security-oauth cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-68-demo03-authorization-server-with-resource-owner-password-credentials 2.2.4.RELEASE 1.8 1.8 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.security.oauth.boot spring-security-oauth2-autoconfigure ${spring.boot.version} ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab68.authorizationserverdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class AuthorizationServerApplication { public static void main(String[] args) { SpringApplication.run(AuthorizationServerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab68.authorizationserverdemo.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; /** * 授权服务器配置 */ @Configuration @EnableAuthorizationServer public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { /** * 用户认证 Manager */ @Autowired private AuthenticationManager authenticationManager; /** * 用户详情 Service */ @Autowired private UserDetailsService userDetailsService; @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.authenticationManager(authenticationManager) .userDetailsService(userDetailsService) ; } @Override public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception { oauthServer.checkTokenAccess("isAuthenticated()"); // oauthServer.tokenKeyAccess("isAuthenticated()") // .checkTokenAccess("isAuthenticated()"); // oauthServer.tokenKeyAccess("permitAll()") // .checkTokenAccess("permitAll()"); } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("clientapp").secret("112233") // Client 账号、密码。 .authorizedGrantTypes("password", "refresh_token") // 密码模式 .scopes("read_userinfo", "read_contacts") // 可授权的 Scope .accessTokenValiditySeconds(3600) // 访问令牌的有效期为 3600 秒 = 2 小时 .refreshTokenValiditySeconds(864000) // 刷新令牌的有效期为 864000 秒 = 10 天 // .and().withClient() // 可以继续配置新的 Client ; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab68.authorizationserverdemo.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.BeanIds; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.password.NoOpPasswordEncoder; @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override @Bean(name = BeanIds.AUTHENTICATION_MANAGER) public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Override @Bean(name = BeanIds.USER_DETAILS_SERVICE) public UserDetailsService userDetailsServiceBean() throws Exception { return super.userDetailsServiceBean(); } @Bean public static NoOpPasswordEncoder passwordEncoder() { return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth. // 使用内存中的 InMemoryUserDetailsManager inMemoryAuthentication() // 不使用 PasswordEncoder 密码编码器 .passwordEncoder(passwordEncoder()) // 配置 yunai 用户 .withUser("yunai").password("1024").roles("USER"); } @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() // 设置 /token/demo/revoke 无需授权 .mvcMatchers("/token/demo/revoke").permitAll() // 设置其它接口需要授权 .anyRequest().authenticated(); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springboot.lab68.authorizationserverdemo.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.oauth2.provider.token.ConsumerTokenServices; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; /** * Token 示例 Controller */ @RestController @RequestMapping("/token/demo") public class TokenDemoController { @Autowired private ConsumerTokenServices tokenServices; @PostMapping(value = "/revoke") public boolean revokeToken(@RequestParam("token") String token) { return tokenServices.revokeToken(token); } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo11-authorization-server-by-jdbc-store/pom.xml ================================================ lab-68-spring-security-oauth cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-68-demo11-authorization-server-by-jdbc-store 2.2.4.RELEASE 1.8 1.8 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.security.oauth.boot spring-security-oauth2-autoconfigure ${spring.boot.version} org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 5.1.48 ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo11-authorization-server-by-jdbc-store/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/AuthorizationServerApplication.java ================================================ package cn.iocoder.springboot.lab68.authorizationserverdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class AuthorizationServerApplication { public static void main(String[] args) { SpringApplication.run(AuthorizationServerApplication.class, args); } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo11-authorization-server-by-jdbc-store/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/config/OAuth2AuthorizationServerConfig.java ================================================ package cn.iocoder.springboot.lab68.authorizationserverdemo.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; import org.springframework.security.oauth2.provider.ClientDetailsService; import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore; import javax.sql.DataSource; /** * 授权服务器配置 */ @Configuration @EnableAuthorizationServer public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { /** * 用户认证 Manager */ @Autowired private AuthenticationManager authenticationManager; /** * 数据源 DataSource */ @Autowired private DataSource dataSource; @Bean public TokenStore jdbcTokenStore() { return new JdbcTokenStore(dataSource); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.authenticationManager(authenticationManager) .tokenStore(jdbcTokenStore()); } @Override public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception { oauthServer.checkTokenAccess("isAuthenticated()"); // oauthServer.tokenKeyAccess("isAuthenticated()") // .checkTokenAccess("isAuthenticated()"); // oauthServer.tokenKeyAccess("permitAll()") // .checkTokenAccess("permitAll()"); } @Bean public ClientDetailsService jdbcClientDetailsService() { return new JdbcClientDetailsService(dataSource); } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.withClientDetails(jdbcClientDetailsService()); } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo11-authorization-server-by-jdbc-store/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/config/SecurityConfig.java ================================================ package cn.iocoder.springboot.lab68.authorizationserverdemo.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.BeanIds; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.password.NoOpPasswordEncoder; @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override @Bean(name = BeanIds.AUTHENTICATION_MANAGER) public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Bean public static NoOpPasswordEncoder passwordEncoder() { return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth. // 使用内存中的 InMemoryUserDetailsManager inMemoryAuthentication() // 不使用 PasswordEncoder 密码编码器 .passwordEncoder(passwordEncoder()) // 配置 yunai 用户 .withUser("yunai").password("1024").roles("USER"); } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo11-authorization-server-by-jdbc-store/src/main/resources/application.yaml ================================================ spring: # datasource 数据源配置内容,对应 DataSourceProperties 配置属性类 datasource: url: jdbc:mysql://127.0.0.1:43063/demo-68-authorization-server?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root # 数据库账号 password: 123456 # 数据库密码 ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo11-authorization-server-by-jdbc-store/src/main/resources/db/data.sql ================================================ INSERT INTO oauth_client_details (client_id, client_secret, scope, authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, refresh_token_validity, additional_information, autoapprove) VALUES ('clientapp', '112233', 'read_userinfo,read_contacts', 'password,refresh_token', null, null, 3600, 864000, null, true); ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo11-authorization-server-by-jdbc-store/src/main/resources/db/schema.sql ================================================ --------------- MySQL --------------- drop table if exists oauth_client_details; create table oauth_client_details ( client_id VARCHAR(255) PRIMARY KEY, resource_ids VARCHAR(255), client_secret VARCHAR(255), scope VARCHAR(255), authorized_grant_types VARCHAR(255), web_server_redirect_uri VARCHAR(255), authorities VARCHAR(255), access_token_validity INTEGER, refresh_token_validity INTEGER, additional_information VARCHAR(4096), autoapprove VARCHAR(255) ); create table if not exists oauth_client_token ( token_id VARCHAR(255), token LONG VARBINARY, authentication_id VARCHAR(255) PRIMARY KEY, user_name VARCHAR(255), client_id VARCHAR(255) ); create table if not exists oauth_access_token ( token_id VARCHAR(255), token LONG VARBINARY, authentication_id VARCHAR(255) PRIMARY KEY, user_name VARCHAR(255), client_id VARCHAR(255), authentication LONG VARBINARY, refresh_token VARCHAR(255) ); create table if not exists oauth_refresh_token ( token_id VARCHAR(255), token LONG VARBINARY, authentication LONG VARBINARY ); create table if not exists oauth_code ( code VARCHAR(255), authentication LONG VARBINARY ); create table if not exists oauth_approvals ( userId VARCHAR(255), clientId VARCHAR(255), scope VARCHAR(255), status VARCHAR(10), expiresAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, lastModifiedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo11-authorization-server-by-jwt-store/pom.xml ================================================ lab-68-spring-security-oauth cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-68-demo11-authorization-server-by-jwt-store 2.2.4.RELEASE 1.8 1.8 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.security.oauth.boot spring-security-oauth2-autoconfigure ${spring.boot.version} ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo11-authorization-server-by-jwt-store/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/AuthorizationServerApplication.java ================================================ package cn.iocoder.springboot.lab68.authorizationserverdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class AuthorizationServerApplication { public static void main(String[] args) { SpringApplication.run(AuthorizationServerApplication.class, args); } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo11-authorization-server-by-jwt-store/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/config/OAuth2AuthorizationServerConfig.java ================================================ package cn.iocoder.springboot.lab68.authorizationserverdemo.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; import org.springframework.security.oauth2.provider.token.store.JwtTokenStore; /** * 授权服务器配置 */ @Configuration @EnableAuthorizationServer public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { /** * 用户认证 Manager */ @Autowired private AuthenticationManager authenticationManager; @Bean public JwtAccessTokenConverter jwtAccessTokenConverter() { JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); converter.setSigningKey("nainai_zui_shuai"); // JWT 秘钥 return converter; } @Bean public JwtTokenStore jwtTokenStore() { return new JwtTokenStore(jwtAccessTokenConverter()); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.authenticationManager(authenticationManager) .tokenStore(jwtTokenStore()) .accessTokenConverter(jwtAccessTokenConverter()); } @Override public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception { oauthServer.checkTokenAccess("isAuthenticated()"); // oauthServer.tokenKeyAccess("isAuthenticated()") // .checkTokenAccess("isAuthenticated()"); // oauthServer.tokenKeyAccess("permitAll()") // .checkTokenAccess("permitAll()"); } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("clientapp").secret("112233") // Client 账号、密码。 .authorizedGrantTypes("password", "refresh_token") // 密码模式 .scopes("read_userinfo", "read_contacts") // 可授权的 Scope // .and().withClient() // 可以继续配置新的 Client ; } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo11-authorization-server-by-jwt-store/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/config/SecurityConfig.java ================================================ package cn.iocoder.springboot.lab68.authorizationserverdemo.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.BeanIds; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.password.NoOpPasswordEncoder; @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override @Bean(name = BeanIds.AUTHENTICATION_MANAGER) public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Bean public static NoOpPasswordEncoder passwordEncoder() { return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth. // 使用内存中的 InMemoryUserDetailsManager inMemoryAuthentication() // 不使用 PasswordEncoder 密码编码器 .passwordEncoder(passwordEncoder()) // 配置 yunai 用户 .withUser("yunai").password("1024").roles("USER"); } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo11-authorization-server-by-redis-store/pom.xml ================================================ lab-68-spring-security-oauth cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-68-demo11-authorization-server-by-redis-store 2.2.4.RELEASE 1.8 1.8 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.security.oauth.boot spring-security-oauth2-autoconfigure ${spring.boot.version} org.springframework.boot spring-boot-starter-data-redis ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo11-authorization-server-by-redis-store/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/AuthorizationServerApplication.java ================================================ package cn.iocoder.springboot.lab68.authorizationserverdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class AuthorizationServerApplication { public static void main(String[] args) { SpringApplication.run(AuthorizationServerApplication.class, args); } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo11-authorization-server-by-redis-store/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/config/OAuth2AuthorizationServerConfig.java ================================================ package cn.iocoder.springboot.lab68.authorizationserverdemo.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore; /** * 授权服务器配置 */ @Configuration @EnableAuthorizationServer public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { /** * 用户认证 Manager */ @Autowired private AuthenticationManager authenticationManager; /** * Redis 连接的工厂 */ @Autowired private RedisConnectionFactory redisConnectionFactory; @Bean public TokenStore redisTokenStore() { return new RedisTokenStore(redisConnectionFactory); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.authenticationManager(authenticationManager) .tokenStore(redisTokenStore()); } @Override public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception { oauthServer.checkTokenAccess("isAuthenticated()"); // oauthServer.tokenKeyAccess("isAuthenticated()") // .checkTokenAccess("isAuthenticated()"); // oauthServer.tokenKeyAccess("permitAll()") // .checkTokenAccess("permitAll()"); } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("clientapp").secret("112233") // Client 账号、密码。 .authorizedGrantTypes("password", "refresh_token") // 密码模式 .scopes("read_userinfo", "read_contacts") // 可授权的 Scope // .and().withClient() // 可以继续配置新的 Client ; } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo11-authorization-server-by-redis-store/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/config/SecurityConfig.java ================================================ package cn.iocoder.springboot.lab68.authorizationserverdemo.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.BeanIds; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.password.NoOpPasswordEncoder; @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override @Bean(name = BeanIds.AUTHENTICATION_MANAGER) public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Bean public static NoOpPasswordEncoder passwordEncoder() { return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth. // 使用内存中的 InMemoryUserDetailsManager inMemoryAuthentication() // 不使用 PasswordEncoder 密码编码器 .passwordEncoder(passwordEncoder()) // 配置 yunai 用户 .withUser("yunai").password("1024").roles("USER"); } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo11-authorization-server-by-redis-store/src/main/resources/application.yml ================================================ spring: # 对应 RedisProperties 类 redis: host: 127.0.0.1 port: 6379 ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo21-authorization-server-on-sso/pom.xml ================================================ lab-68-spring-security-oauth cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-68-demo21-authorization-server-on-sso 2.2.4.RELEASE 1.8 1.8 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.security.oauth.boot spring-security-oauth2-autoconfigure ${spring.boot.version} org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 5.1.48 ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo21-authorization-server-on-sso/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/AuthorizationServerApplication.java ================================================ package cn.iocoder.springboot.lab68.authorizationserverdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class AuthorizationServerApplication { public static void main(String[] args) { SpringApplication.run(AuthorizationServerApplication.class, args); } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo21-authorization-server-on-sso/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/config/OAuth2AuthorizationServerConfig.java ================================================ package cn.iocoder.springboot.lab68.authorizationserverdemo.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; import org.springframework.security.oauth2.provider.ClientDetailsService; import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore; import javax.sql.DataSource; /** * 授权服务器配置 */ @Configuration @EnableAuthorizationServer public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { /** * 用户认证 Manager */ @Autowired private AuthenticationManager authenticationManager; /** * 数据源 DataSource */ @Autowired private DataSource dataSource; @Bean public TokenStore jdbcTokenStore() { return new JdbcTokenStore(dataSource); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.authenticationManager(authenticationManager) .tokenStore(jdbcTokenStore()); } @Override public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception { oauthServer.checkTokenAccess("isAuthenticated()"); // oauthServer.tokenKeyAccess("isAuthenticated()") // .checkTokenAccess("isAuthenticated()"); // oauthServer.tokenKeyAccess("permitAll()") // .checkTokenAccess("permitAll()"); } @Bean public ClientDetailsService jdbcClientDetailsService() { return new JdbcClientDetailsService(dataSource); } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.withClientDetails(jdbcClientDetailsService()); } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo21-authorization-server-on-sso/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/config/SecurityConfig.java ================================================ package cn.iocoder.springboot.lab68.authorizationserverdemo.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.BeanIds; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.password.NoOpPasswordEncoder; import javax.sql.DataSource; @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { /** * 数据源 DataSource */ @Autowired private DataSource dataSource; @Override @Bean(name = BeanIds.AUTHENTICATION_MANAGER) public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Bean public static NoOpPasswordEncoder passwordEncoder() { return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.jdbcAuthentication() .dataSource(dataSource); } // @Override // protected void configure(HttpSecurity http) throws Exception { // http // .authorizeRequests() // .antMatchers("/oauth/**").permitAll() // 允许无权限访问 // .anyRequest().authenticated() // .and() // .formLogin().and() // .httpBasic(); // } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo21-authorization-server-on-sso/src/main/resources/application.yaml ================================================ spring: # datasource 数据源配置内容,对应 DataSourceProperties 配置属性类 datasource: url: jdbc:mysql://127.0.0.1:43063/demo-68-authorization-server-sso?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root # 数据库账号 password: 123456 # 数据库密码 ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo21-authorization-server-on-sso/src/main/resources/db/oauth_data.sql ================================================ INSERT INTO oauth_client_details (client_id, client_secret, scope, authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, refresh_token_validity, additional_information, autoapprove) VALUES ('clientapp', '112233', 'read_userinfo,read_contacts', 'password,authorization_code,refresh_token', 'http://127.0.0.1:9090/login', null, 3600, 864000, null, true); ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo21-authorization-server-on-sso/src/main/resources/db/oauth_schema.sql ================================================ drop table if exists oauth_client_details; create table oauth_client_details ( client_id VARCHAR(255) PRIMARY KEY, resource_ids VARCHAR(255), client_secret VARCHAR(255), scope VARCHAR(255), authorized_grant_types VARCHAR(255), web_server_redirect_uri VARCHAR(255), authorities VARCHAR(255), access_token_validity INTEGER, refresh_token_validity INTEGER, additional_information VARCHAR(4096), autoapprove VARCHAR(255) ); create table if not exists oauth_client_token ( token_id VARCHAR(255), token LONG VARBINARY, authentication_id VARCHAR(255) PRIMARY KEY, user_name VARCHAR(255), client_id VARCHAR(255) ); create table if not exists oauth_access_token ( token_id VARCHAR(255), token LONG VARBINARY, authentication_id VARCHAR(255) PRIMARY KEY, user_name VARCHAR(255), client_id VARCHAR(255), authentication LONG VARBINARY, refresh_token VARCHAR(255) ); create table if not exists oauth_refresh_token ( token_id VARCHAR(255), token LONG VARBINARY, authentication LONG VARBINARY ); create table if not exists oauth_code ( code VARCHAR(255), authentication LONG VARBINARY ); create table if not exists oauth_approvals ( userId VARCHAR(255), clientId VARCHAR(255), scope VARCHAR(255), status VARCHAR(10), expiresAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, lastModifiedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo21-authorization-server-on-sso/src/main/resources/db/user_data.sql ================================================ INSERT INTO `authorities` VALUES ('yunai', 'ROLE_USER'); INSERT INTO `users` VALUES ('yunai', '112233', '1'); ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo21-authorization-server-on-sso/src/main/resources/db/user_schema.sql ================================================ DROP TABLE IF EXISTS `authorities`; CREATE TABLE `authorities` ( `username` varchar(50) NOT NULL, `authority` varchar(50) NOT NULL, UNIQUE KEY `ix_auth_username` (`username`,`authority`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; DROP TABLE IF EXISTS `users`; CREATE TABLE `users` ( `username` varchar(50) NOT NULL, `password` varchar(500) NOT NULL, `enabled` tinyint(1) NOT NULL, PRIMARY KEY (`username`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo21-resource-server-on-sso/pom.xml ================================================ lab-68-spring-security-oauth cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-68-demo21-resource-server-on-sso 2.2.4.RELEASE 1.8 1.8 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.security.oauth.boot spring-security-oauth2-autoconfigure ${spring.boot.version} ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo21-resource-server-on-sso/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/ResourceServerApplication.java ================================================ package cn.iocoder.springboot.lab68.resourceserverdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ResourceServerApplication { public static void main(String[] args) { SpringApplication.run(ResourceServerApplication.class, args); } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo21-resource-server-on-sso/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/config/OAuthSsoConfig.java ================================================ package cn.iocoder.springboot.lab68.resourceserverdemo.config; import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso; import org.springframework.context.annotation.Configuration; /** * SSO 配置 * * 推荐看 SsoSecurityConfigurer 类 */ @Configuration @EnableOAuth2Sso // 开启 Sso 功能 public class OAuthSsoConfig { } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo21-resource-server-on-sso/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/config/SecurityConfig.java ================================================ package cn.iocoder.springboot.lab68.resourceserverdemo.config; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @Configuration @EnableGlobalMethodSecurity(prePostEnabled = true) // 开启对 Spring Security 注解的方法,进行权限验证。 @Order(101) // OAuth2SsoDefaultConfiguration 使用了 Order(100),所以这里设置为 Order(101),防止相同顺序导致报错 public class SecurityConfig extends WebSecurityConfigurerAdapter { } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo21-resource-server-on-sso/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.lab68.resourceserverdemo.controller; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * 示例 Controller */ @RestController @RequestMapping("/demo") public class DemoController { @GetMapping("/admin-list") @PreAuthorize("hasRole('ADMIN')") // 要求管理员 ROLE_ADMIN 角色 public String adminList() { return "管理员列表"; } @GetMapping("/user-list") @PreAuthorize("hasRole('USER')") // 要求普通用户 ROLE_USER 角色 public String userList() { return "用户列表"; } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo21-resource-server-on-sso/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/controller/UserController.java ================================================ package cn.iocoder.springboot.lab68.resourceserverdemo.controller; import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * 用户 Controller */ @RestController @RequestMapping("/user") public class UserController { @RequestMapping("/info") public Authentication info(Authentication authentication) { return authentication; } } ================================================ FILE: lab-68-spring-security-oauth/lab-68-demo21-resource-server-on-sso/src/main/resources/application.yml ================================================ server: port: 9090 servlet: session: cookie: name: SSO-SESSIONID # 自定义 Session 的 Cookie 名字,防止冲突。冲突后,会导致 SSO 登陆失败。 security: oauth2: # OAuth2 Client 配置,对应 OAuth2ClientProperties 类 client: client-id: clientapp client-secret: 112233 user-authorization-uri: http://127.0.0.1:8080/oauth/authorize # 获取用户的授权码地址 access-token-uri: http://127.0.0.1:8080/oauth/token # 获取访问令牌的地址 # OAuth2 Resource 配置,对应 ResourceServerProperties 类 resource: token-info-uri: http://127.0.0.1:8080/oauth/check_token # 校验访问令牌是否有效的地址 ================================================ FILE: lab-68-spring-security-oauth/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-68-spring-security-oauth pom lab-68-demo01-resource-owner-password-credentials-server lab-68-demo01-authorization-code-server lab-68-demo01-implicit-server lab-68-demo01-client-credentials-server lab-68-demo02-resource-server lab-68-demo02-authorization-server-with-resource-owner-password-credentials lab-68-demo02-authorization-server-with-authorization-code lab-68-demo02-authorization-server-with-implicit lab-68-demo02-authorization-server-with-client-credentials lab-68-demo03-authorization-server-with-resource-owner-password-credentials lab-68-demo11-authorization-server-by-jdbc-store lab-68-demo11-authorization-server-by-redis-store lab-68-demo11-authorization-server-by-jwt-store lab-68-demo21-authorization-server-on-sso lab-68-demo21-resource-server-on-sso ================================================ FILE: lab-68-spring-security-oauth/《芋道 Spring Security OAuth2 入门》.md ================================================ ================================================ FILE: lab-68-spring-security-oauth/《芋道 Spring Security OAuth2 单点登陆》.md ================================================ ================================================ FILE: lab-68-spring-security-oauth/《芋道 Spring Security OAuth2 存储器》.md ================================================ ================================================ FILE: lab-69-proxy/lab-69-proxy-cglib/pom.xml ================================================ lab-69 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-69-proxy-cglib cglib cglib 3.3.0 ================================================ FILE: lab-69-proxy/lab-69-proxy-cglib/src/main/java/cn/iocoder/springboot/labs/lab69/TestProxyMain.java ================================================ package cn.iocoder.springboot.labs.lab69; import cn.iocoder.springboot.labs.lab69.intercept.UserServiceMethodInterceptor; import cn.iocoder.springboot.labs.lab69.service.UserServiceImpl; import net.sf.cglib.proxy.Enhancer; public class TestProxyMain { public static void main(String[] args) { // 创建 cglib 增强对象 Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(UserServiceImpl.class); // 设置父类 enhancer.setCallback(new UserServiceMethodInterceptor()); // 创建代理 UserServiceImpl userService = (UserServiceImpl) enhancer.create(); userService.create("yunai", "buzhidao"); } } ================================================ FILE: lab-69-proxy/lab-69-proxy-cglib/src/main/java/cn/iocoder/springboot/labs/lab69/intercept/UserServiceMethodInterceptor.java ================================================ package cn.iocoder.springboot.labs.lab69.intercept; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class UserServiceMethodInterceptor implements MethodInterceptor { public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("before invoke"); Object ret = methodProxy.invokeSuper(object, args); System.out.println("after invoke"); return ret; } } ================================================ FILE: lab-69-proxy/lab-69-proxy-cglib/src/main/java/cn/iocoder/springboot/labs/lab69/service/UserServiceImpl.java ================================================ package cn.iocoder.springboot.labs.lab69.service; public class UserServiceImpl { public void create(String username, String password) { System.out.println(String.format("登陆的用户名(%s) 密码(%s)", username, password)); } } ================================================ FILE: lab-69-proxy/lab-69-proxy-jdk/pom.xml ================================================ lab-69 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-69-proxy-jdk commons-io commons-io 2.7 ================================================ FILE: lab-69-proxy/lab-69-proxy-jdk/src/main/java/cn/iocoder/springboot/labs/lab69/GenerateProxyMain.java ================================================ package cn.iocoder.springboot.labs.lab69; import cn.iocoder.springboot.labs.lab69.service.UserServiceImpl; import org.apache.commons.io.IOUtils; import sun.misc.ProxyGenerator; import java.io.FileOutputStream; import java.io.IOException; import java.lang.reflect.Proxy; /** * 生成 JDK {@link Proxy} 的示例代码 * * 生成后,我们可以反编译查看具体的类 */ public class GenerateProxyMain { public static void main(String[] args) throws IOException { // 生成字节码 byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy11", UserServiceImpl.class.getInterfaces()); // 写入到磁盘 IOUtils.write(classFile, new FileOutputStream("/Users/yunai/ls/$Proxy11.class")); } } ================================================ FILE: lab-69-proxy/lab-69-proxy-jdk/src/main/java/cn/iocoder/springboot/labs/lab69/TestProxyMain.java ================================================ package cn.iocoder.springboot.labs.lab69; import cn.iocoder.springboot.labs.lab69.handler.UserServiceHandler; import cn.iocoder.springboot.labs.lab69.service.UserService; import cn.iocoder.springboot.labs.lab69.service.UserServiceImpl; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; /** * 使用 JDK {@link Proxy} 实现一个动态代理的示例 */ public class TestProxyMain { public static void main(String[] args) { // 创建 UserService 对象 UserService userService = new UserServiceImpl(); // JDK 处理器 InvocationHandler handler = new UserServiceHandler(userService); // 创建代理 UserService userServiceProxy = (UserService) Proxy.newProxyInstance(TestProxyMain.class.getClassLoader(), userService.getClass().getInterfaces(), handler); // 执行 userServiceProxy.create("yunai", "buzhidao"); } } ================================================ FILE: lab-69-proxy/lab-69-proxy-jdk/src/main/java/cn/iocoder/springboot/labs/lab69/handler/UserServiceHandler.java ================================================ package cn.iocoder.springboot.labs.lab69.handler; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class UserServiceHandler implements InvocationHandler { /** * 被代理的对象 */ private final Object object; public UserServiceHandler(Object object) { this.object = object; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before invoke"); Object ret = method.invoke(object, args); System.out.println("after invoke"); return ret; } } ================================================ FILE: lab-69-proxy/lab-69-proxy-jdk/src/main/java/cn/iocoder/springboot/labs/lab69/service/UserService.java ================================================ package cn.iocoder.springboot.labs.lab69.service; public interface UserService { void create(String username, String password); } ================================================ FILE: lab-69-proxy/lab-69-proxy-jdk/src/main/java/cn/iocoder/springboot/labs/lab69/service/UserServiceImpl.java ================================================ package cn.iocoder.springboot.labs.lab69.service; public class UserServiceImpl implements UserService { public void create(String username, String password) { System.out.println(String.format("登陆的用户名(%s) 密码(%s)", username, password)); } } ================================================ FILE: lab-69-proxy/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-69 pom lab-69-proxy-jdk lab-69-proxy-cglib ================================================ FILE: lab-70-db-doc/lab-70-db-doc-screw-01/pom.xml ================================================ lab-70-db-doc cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-70-db-doc-screw-01 cn.smallbun.screw screw-core 1.0.5 com.zaxxer HikariCP 3.4.5 mysql mysql-connector-java 8.0.22 ================================================ FILE: lab-70-db-doc/lab-70-db-doc-screw-01/src/main/java/ScrewMain.java ================================================ import cn.smallbun.screw.core.Configuration; import cn.smallbun.screw.core.engine.EngineConfig; import cn.smallbun.screw.core.engine.EngineFileType; import cn.smallbun.screw.core.engine.EngineTemplateType; import cn.smallbun.screw.core.execute.DocumentationExecute; import cn.smallbun.screw.core.process.ProcessConfig; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; import javax.sql.DataSource; import java.util.Arrays; import java.util.Collections; public class ScrewMain { private static final String DB_URL = "jdbc:mysql://400-infra.server.iocoder.cn:3306"; private static final String DB_NAME = "mall_system"; private static final String DB_USERNAME = "root"; private static final String DB_PASSWORD = "3WLiVUBEwTbvAfsh"; private static final String FILE_OUTPUT_DIR = "/Users/yunai/screw_test"; private static final EngineFileType FILE_OUTPUT_TYPE = EngineFileType.HTML; // 可以设置 Word 或者 Markdown 格式 private static final String DOC_FILE_NAME = "数据库文档"; private static final String DOC_VERSION = "1.0.0"; private static final String DOC_DESCRIPTION = "文档描述"; public static void main(String[] args) { // 创建 screw 的配置 Configuration config = Configuration.builder() .version(DOC_VERSION) // 版本 .description(DOC_DESCRIPTION) // 描述 .dataSource(buildDataSource()) // 数据源 .engineConfig(buildEngineConfig()) // 引擎配置 .produceConfig(buildProcessConfig()) // 处理配置 .build(); // 执行 screw,生成数据库文档 new DocumentationExecute(config).execute(); } /** * 创建数据源 */ private static DataSource buildDataSource() { // 创建 HikariConfig 配置类 HikariConfig hikariConfig = new HikariConfig(); hikariConfig.setDriverClassName("com.mysql.cj.jdbc.Driver"); hikariConfig.setJdbcUrl(DB_URL + "/" + DB_NAME); hikariConfig.setUsername(DB_USERNAME); hikariConfig.setPassword(DB_PASSWORD); hikariConfig.addDataSourceProperty("useInformationSchema", "true"); // 设置可以获取 tables remarks 信息 // 创建数据源 return new HikariDataSource(hikariConfig); } /** * 创建 screw 的引擎配置 */ private static EngineConfig buildEngineConfig() { return EngineConfig.builder() .fileOutputDir(FILE_OUTPUT_DIR) // 生成文件路径 .openOutputDir(false) // 打开目录 .fileType(FILE_OUTPUT_TYPE) // 文件类型 .produceType(EngineTemplateType.freemarker) // 文件类型 .fileName(DOC_FILE_NAME) // 自定义文件名称 .build(); } /** * 创建 screw 的处理配置,一般可忽略 * 指定生成逻辑、当存在指定表、指定表前缀、指定表后缀时,将生成指定表,其余表不生成、并跳过忽略表配置 */ private static ProcessConfig buildProcessConfig() { return ProcessConfig.builder() .designatedTableName(Collections.emptyList()) // 根据名称指定表生成 .designatedTablePrefix(Collections.emptyList()) //根据表前缀生成 .designatedTableSuffix(Collections.emptyList()) // 根据表后缀生成 .ignoreTableName(Arrays.asList("test_user", "test_group")) // 忽略表名 .ignoreTablePrefix(Collections.singletonList("test_")) // 忽略表前缀 .ignoreTableSuffix(Collections.singletonList("_test")) // 忽略表后缀 .build(); } } ================================================ FILE: lab-70-db-doc/lab-70-db-doc-screw-02/doc/测试文档名称.html ================================================ 数据库文档

数据库文档

数据库名:mall_system
文档版本:1.0-SNAPSHOT
文档描述:数据库文档生成
序号表名说明
1admin管理员
2admin_department部门
3oauth2_access_token访问令牌
4oauth2_refresh_token刷新令牌
5permission_admin_role管理员角色
6permission_resource资源
7permission_role角色
8permission_role_resource角色资源
9system_access_log系统访问日志
10system_data_dict数据字典
11system_error_code错误码
12system_exception_log系统异常日志
13_operation_log操作日志
返回目录表名:admin
说明:管理员
数据列:
序号名称数据类型长度小数位允许空值主键默认值说明
1idint100NY管理员编号
2namevarchar100NN真实名字
3avatarvarchar2550YN头像
4department_idint100YN部门id
5statustinyint40NN在职状态
6usernamevarchar160NN登陆账号
7passwordvarchar2550NN加密后的密码
8password_saltvarchar640NN密码的盐
9create_admin_idint100NN创建管理员编号
10create_ipvarchar320NN创建 IP
11create_timedatetime190NNCURRENT_TIMESTAMP创建时间
12update_timedatetime190NNCURRENT_TIMESTAMP最后更新时间
返回目录表名:admin_department
说明:部门
数据列:
序号名称数据类型长度小数位允许空值主键默认值说明
1idint100NY部门编号
2namevarchar1000NN部门名称
3sortint100NN0排序字段
4pidint100NN0父级部门编号
5create_timetimestamp190NNCURRENT_TIMESTAMP创建时间
6update_timetimestamp190NNCURRENT_TIMESTAMP更新时间
7deletedbit10NNb'0'删除标记
返回目录表名:oauth2_access_token
说明:访问令牌
数据列:
序号名称数据类型长度小数位允许空值主键默认值说明
1idvarchar320NY访问令牌
2user_idint100NN用户编号
3user_typetinyint40NN用户类型
4refresh_tokenvarchar320NN刷新令牌
5expires_timedatetime190NN过期时间
6create_ipvarchar320NN创建 IP
7create_timedatetime190NNCURRENT_TIMESTAMP创建时间
8update_timedatetime190NNCURRENT_TIMESTAMP最后更新时间
9deletedbit10NNb'0'是否删除
返回目录表名:oauth2_refresh_token
说明:刷新令牌
数据列:
序号名称数据类型长度小数位允许空值主键默认值说明
1idvarchar320NY编号,刷新令牌
2user_idint100NN用户编号
3user_typetinyint40NN用户类型
4expires_timedatetime190NN过期时间
5create_ipvarchar320NN创建 IP
6create_timedatetime190NNCURRENT_TIMESTAMP创建时间
7update_timedatetime190NNCURRENT_TIMESTAMP最后更新时间
8deletedbit10NNb'0'是否删除
返回目录表名:permission_admin_role
说明:管理员角色
数据列:
序号名称数据类型长度小数位允许空值主键默认值说明
1idint100NY编号
2admin_idint100NN管理员编号
3role_idint100NN角色编号
4create_timedatetime190NNCURRENT_TIMESTAMP创建时间
5update_timedatetime190NNCURRENT_TIMESTAMP更新时间
6deletedbit10YNb'0'是否删除
返回目录表名:permission_resource
说明:资源
数据列:
序号名称数据类型长度小数位允许空值主键默认值说明
1idint100NY资源编号
2namevarchar500NN菜单名
3permissionvarchar2550YN权限标识
4typeint100NN资源类型
5sortint100NN排序
6pidint100NN0父级资源编号(外键:{@link ResourceDO#id})
7routevarchar500YN前端路由
8iconvarchar500YN菜单图标
9viewvarchar500YN前端界面
10create_admin_idint100NN创建管理员编号
11create_timedatetime190NNCURRENT_TIMESTAMP添加时间
12update_timedatetime190NNCURRENT_TIMESTAMP更新时间
13deletedbit10NNb'0'是否删除
返回目录表名:permission_role
说明:角色
数据列:
序号名称数据类型长度小数位允许空值主键默认值说明
1idint100NY角色编号
2namevarchar500NN角色名
3codevarchar500YN角色编码
4typetinyint40NN角色类型
5create_admin_idint100NN创建管理员编号
6create_timedatetime190NNCURRENT_TIMESTAMP创建时间
7update_timedatetime190YNCURRENT_TIMESTAMP最后更新时间
8deletedbit10NNb'0'是否删除
返回目录表名:permission_role_resource
说明:角色资源
数据列:
序号名称数据类型长度小数位允许空值主键默认值说明
1idint100NY编号
2role_idint100NN-1角色编号(外键:{@link RoleDO}
3resource_idint100NN-1资源编号
4create_timedatetime190NNCURRENT_TIMESTAMP创建时间
5update_timedatetime190NNCURRENT_TIMESTAMP更新时间
6deletedbit10NNb'0'是否删除
返回目录表名:system_access_log
说明:系统访问日志
数据列:
序号名称数据类型长度小数位允许空值主键默认值说明
1idint100NY编号
2user_idint100YN用户编号
3user_typetinyint40YN用户类型
4trace_idvarchar640YN链路追踪编号
5application_namevarchar500NN应用名
6urivarchar40960NN访问地址
7query_stringvarchar40960NN参数
8methodvarchar500NNhttp 方法
9user_agentvarchar10240NNuserAgent
10ipvarchar500NNip
11start_timedatetime190NN请求时间
12response_timeint100NN响应时长 -- 毫秒级
13error_codeint100NN错误码
14error_messagevarchar5120YN错误提示
15create_timedatetime190NNCURRENT_TIMESTAMP创建时间
16update_timedatetime190NNCURRENT_TIMESTAMP最后更新时间
返回目录表名:system_data_dict
说明:数据字典
数据列:
序号名称数据类型长度小数位允许空值主键默认值说明
1idint100NY编号
2enum_valuevarchar500NN大类枚举值
3valuevarchar500NN小类数值
4display_namevarchar500NN展示名
5sortint100NN-1排序值
6memovarchar500YN备注
7create_timedatetime190NNCURRENT_TIMESTAMP创建时间
8update_timedatetime190NNCURRENT_TIMESTAMP最后更新时间
9deletedbit10NNb'0'是否删除
返回目录表名:system_error_code
说明:错误码
数据列:
序号名称数据类型长度小数位允许空值主键默认值说明
1idint100NY错误码编号
2codeint100NN0错误码编码
3messagevarchar2550NN错误码错误提示
4typetinyint40NN错误码类型
5groupvarchar640NN错误码分组
6memovarchar2550YN错误码备注
7create_timedatetime190NNCURRENT_TIMESTAMP创建时间
8update_timedatetime190NNCURRENT_TIMESTAMP最后更新时间
9deletedbit10NNb'0'是否删除
返回目录表名:system_exception_log
说明:系统异常日志
数据列:
序号名称数据类型长度小数位允许空值主键默认值说明
1idint100NY编号
2user_idint100YN用户编号
3user_typetinyint40YN用户类型
4trace_idvarchar640NN链路追踪编号 * * 一般来说,通过链路追踪编号,可以将访问日志,错误日志,链路追踪日志,logger 打印日志等,结合在一起,从而进行排错。
5application_namevarchar500NN应用名 * * 目前读取 spring.application.name
6urivarchar40960NN访问地址
7query_stringvarchar40960NN参数
8methodvarchar500NNhttp 方法
9user_agentvarchar10240NNuserAgent
10ipvarchar500NNip
11exception_timedatetime190NN异常发生时间
12exception_namevarchar1280NN异常名 * * {@link Throwable#getClass()} 的类全名
13exception_messagetext655350NN异常导致的消息 * * {@link cn.iocoder.common.framework.util.ExceptionUtil#getMessage(Throwable)}
14exception_root_cause_messagetext655350NN异常导致的根消息 * * {@link cn.iocoder.common.framework.util.ExceptionUtil#getRootCauseMessage(Throwable)}
15exception_stack_tracetext655350NN异常的栈轨迹 * * {@link cn.iocoder.common.framework.util.ExceptionUtil#getServiceException(Exception)}
16exception_class_namevarchar5120NN异常发生的类全名 * * {@link StackTraceElement#getClassName()}
17exception_file_namevarchar5120NN异常发生的类文件 * * {@link StackTraceElement#getFileName()}
18exception_method_namevarchar5120NN异常发生的方法名 * * {@link StackTraceElement#getMethodName()}
19exception_line_numberint100NN异常发生的方法所在行 * * {@link StackTraceElement#getLineNumber()}
20process_statustinyint40NN0处理状态
21process_timedatetime190YN处理时间
22process_admin_idint100YN处理管理员编号
23create_timedatetime190NNCURRENT_TIMESTAMP创建时间
24update_timedatetime190NNCURRENT_TIMESTAMP最后更新时间
返回目录表名:_operation_log
说明:操作日志
数据列:
序号名称数据类型长度小数位允许空值主键默认值说明
1idint100NY编号
2trace_idvarchar640YN链路追踪编号
3account_idint100NN-1账号编号
4application_namevarchar500YN应用名
5urivarchar40960NN访问地址
6paramsvarchar40960NN参数
7methodvarchar500NNhttp 方法
8user_agentvarchar10240NNuserAgent
9ipvarchar500NNip
10start_timedatetime190NN请求时间
11response_timeint100NN响应时长 -- 毫秒级
12msgvarchar2550YN日志消息
13statusbit10NN操作状态
14operatorvarchar640YN创建者
15create_timedatetime190NN创建时间
================================================ FILE: lab-70-db-doc/lab-70-db-doc-screw-02/pom.xml ================================================ lab-70-db-doc cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-70-db-doc-screw-02 cn.smallbun.screw screw-maven-plugin 1.0.5 com.zaxxer HikariCP 3.4.5 mysql mysql-connector-java 8.0.22 com.mysql.cj.jdbc.Driver jdbc:mysql://400-infra.server.iocoder.cn:3306/mall_system root 3WLiVUBEwTbvAfsh HTML 数据库文档 测试文档名称 数据库文档生成 ${project.version} false freemarker compile run ================================================ FILE: lab-70-db-doc/lab-70-db-doc-screw-03/pom.xml ================================================ lab-70-db-doc cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-70-db-doc-screw-03 cn.smallbun.screw screw-core 1.0.5 cn.smallbun.screw screw-extension 1.0.5 com.zaxxer HikariCP 3.4.5 mysql mysql-connector-java 8.0.22 ================================================ FILE: lab-70-db-doc/lab-70-db-doc-screw-03/src/main/java/ScrewMain.java ================================================ import cn.smallbun.screw.core.process.ProcessConfig; import cn.smallbun.screw.extension.pojo.PojoConfiguration; import cn.smallbun.screw.extension.pojo.execute.PojoExecute; import cn.smallbun.screw.extension.pojo.strategy.HumpNameStrategy; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; import javax.sql.DataSource; import java.util.Arrays; import java.util.Collections; public class ScrewMain { private static final String DB_URL = "jdbc:mysql://400-infra.server.iocoder.cn:3306"; private static final String DB_NAME = "mall_system"; private static final String DB_USERNAME = "root"; private static final String DB_PASSWORD = "3WLiVUBEwTbvAfsh"; private static final String FILE_OUTPUT_DIR = "/Users/yunai/screw_test"; private static final String JAVA_CLASS_PACKAGE = "cn.iocoder.dataobject"; public static void main(String[] args) { // 创建 screw 的配置 PojoConfiguration config = PojoConfiguration.builder() .path(FILE_OUTPUT_DIR) // 生成 POJO 相关的目录 .packageName(JAVA_CLASS_PACKAGE) // 包名 .nameStrategy(new HumpNameStrategy()) // 包名策略 .useLombok(false) // 是否使用 Lombok .dataSource(buildDataSource()) // 数据源 .processConfig(buildProcessConfig()) // 处理配置 .build(); // 执行 screw,生成 POJO 实体类 new PojoExecute(config).execute(); } /** * 创建数据源 */ private static DataSource buildDataSource() { // 创建 HikariConfig 配置类 HikariConfig hikariConfig = new HikariConfig(); hikariConfig.setDriverClassName("com.mysql.cj.jdbc.Driver"); hikariConfig.setJdbcUrl(DB_URL + "/" + DB_NAME); hikariConfig.setUsername(DB_USERNAME); hikariConfig.setPassword(DB_PASSWORD); hikariConfig.addDataSourceProperty("useInformationSchema", "true"); // 设置可以获取 tables remarks 信息 // 创建数据源 return new HikariDataSource(hikariConfig); } /** * 创建 screw 的处理配置,一般可忽略 * 指定生成逻辑、当存在指定表、指定表前缀、指定表后缀时,将生成指定表,其余表不生成、并跳过忽略表配置 */ private static ProcessConfig buildProcessConfig() { return ProcessConfig.builder() .designatedTableName(Collections.emptyList()) // 根据名称指定表生成 .designatedTablePrefix(Collections.emptyList()) //根据表前缀生成 .designatedTableSuffix(Collections.emptyList()) // 根据表后缀生成 .ignoreTableName(Arrays.asList("test_user", "test_group")) // 忽略表名 .ignoreTablePrefix(Collections.singletonList("test_")) // 忽略表前缀 .ignoreTableSuffix(Collections.singletonList("_test")) // 忽略表后缀 .build(); } } ================================================ FILE: lab-70-db-doc/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-70-db-doc pom lab-70-db-doc-screw-01 lab-70-db-doc-screw-02 lab-70-db-doc-screw-03 ================================================ FILE: lab-70-db-doc/《芋道 Spring Boot 数据表结构文档》.md ================================================ ================================================ FILE: lab-71-http-debug/lab-71-idea-http-client/http-client.env.json ================================================ { "local": { "baseUrl": "http://127.0.0.1:8080" }, "dev": { "baseUrl": "http://192.168.100.200:8080" } } ================================================ FILE: lab-71-http-debug/lab-71-idea-http-client/http-client.private.env.json ================================================ { "local": { "username": "yudaoyuanma", "password": "123456", "token": "token001" }, "dev": { "username": "yutou", "password": "buzhidao", "token": "aoteman" } } ================================================ FILE: lab-71-http-debug/lab-71-idea-http-client/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE 4.0.0 lab-71-idea-http-client org.springframework.boot spring-boot-starter-web ================================================ FILE: lab-71-http-debug/lab-71-idea-http-client/src/main/java/cn/iocoder/springboot/lab71/Application.java ================================================ package cn.iocoder.springboot.lab71; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ================================================ FILE: lab-71-http-debug/lab-71-idea-http-client/src/main/java/cn/iocoder/springboot/lab71/controller/UserController.http ================================================ ### 测试 /user/login:登陆成功 POST http://127.0.0.1:8080/user/login Content-Type: application/x-www-form-urlencoded username=yudaoyuanma&password=123456 ### 测试 /user/get-current:获取成功 GET http://127.0.0.1:8080/user/get-current?full=true Authorization: token001 ### 测试 /user/update:更新成功 POST http://127.0.0.1:8080/user/update Content-Type: application/json { "nickname": "我是昵称", "gender": 1 } ================================================ FILE: lab-71-http-debug/lab-71-idea-http-client/src/main/java/cn/iocoder/springboot/lab71/controller/UserController.java ================================================ package cn.iocoder.springboot.lab71.controller; import cn.iocoder.springboot.lab71.vo.UserUpdateVO; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.*; import java.util.HashMap; import java.util.Map; /** * 用户 Controller * * 示例代码,纯粹为了演示。 */ @RestController public class UserController { private final Logger logger = LoggerFactory.getLogger(getClass()); @PostMapping("/user/login") public Map login(@RequestParam("username") String username, @RequestParam("password") String password) { if ("yudaoyuanma".equals(username) && "123456".equals(password)) { Map tokenMap = new HashMap<>(); tokenMap.put("userId", 1); tokenMap.put("token", "token001"); return tokenMap; } throw new RuntimeException("小朋友,你的账号密码不正确哟!"); } @GetMapping("/user/get-current") public Map getCurrentUser(@RequestHeader("Authorization") String authorization, @RequestParam("full") boolean full) { if ("token001".equals(authorization)) { Map userInfo = new HashMap<>(); userInfo.put("id", 1); // full 为 true 时,获得完整信息 if (full) { userInfo.put("nickname", "芋道源码"); userInfo.put("gender", 1); } return userInfo; } throw new RuntimeException("小朋友,你没有登录哟!"); } @PostMapping("/user/update") public Boolean update(@RequestBody UserUpdateVO updateVO) { logger.info("[update][收到更新请求:{}]", updateVO.toString()); return true; } } ================================================ FILE: lab-71-http-debug/lab-71-idea-http-client/src/main/java/cn/iocoder/springboot/lab71/controller/UserController2.http ================================================ ### 测试 /user/login:登陆成功 POST {{baseUrl}}/user/login Content-Type: application/x-www-form-urlencoded username={{username}}&password={{password}} ### 测试 /user/get-current:获取成功 GET {{baseUrl}}/user/get-current?full=true Authorization: {{token}} ### 测试 /user/update:更新成功 POST {{baseUrl}}/user/update Content-Type: application/json { "nickname": "我是昵称", "gender": 1 } ================================================ FILE: lab-71-http-debug/lab-71-idea-http-client/src/main/java/cn/iocoder/springboot/lab71/controller/UserController3.http ================================================ ### 001 测试 /user/login:登陆成功 POST http://127.0.0.1:8080/user/login Content-Type: application/x-www-form-urlencoded username=yudaoyuanma&password=123456 > {% client.test("验证登陆成功", function (){ client.assert(response.status === 200, "响应状态应该是 200,结果是 " + response.status) client.assert(response.body.userId === 1, "响应的 userId 应该是 1,结果是 " + response.body.userId) client.assert(response.body.token === "token001", "响应的 token 应该是 token001,记过是 " + response.body.token) }); %} ### 002 测试 /user/login:登陆失败,密码不正确 POST http://127.0.0.1:8080/user/login Content-Type: application/x-www-form-urlencoded username=yudaoyuanma&password=buzhidao > {% client.test("验证登陆失败", function (){ client.assert(response.status === 200, "响应状态应该是 200,结果是 " + response.status) }); %} ================================================ FILE: lab-71-http-debug/lab-71-idea-http-client/src/main/java/cn/iocoder/springboot/lab71/controller/UserController4.http ================================================ ### 测试 /user/login:登陆成功 POST http://127.0.0.1:8080/user/login Content-Type: application/x-www-form-urlencoded username=yudaoyuanma&password=123456 > {% client.global.set("token_from_server", response.body.token) %} ### 测试 /user/get-current:获取成功 GET http://127.0.0.1:8080/user/get-current?full=true Authorization: {{token_from_server}} ================================================ FILE: lab-71-http-debug/lab-71-idea-http-client/src/main/java/cn/iocoder/springboot/lab71/vo/UserUpdateVO.java ================================================ package cn.iocoder.springboot.lab71.vo; /** * 用户更新 VO */ public class UserUpdateVO { /** * 昵称 */ private String nickname; /** * 性别 */ private Integer gender; public String getNickname() { return nickname; } public UserUpdateVO setNickname(String nickname) { this.nickname = nickname; return this; } public Integer getGender() { return gender; } public UserUpdateVO setGender(Integer gender) { this.gender = gender; return this; } @Override public String toString() { return "UserUpdateVO{" + "nickname='" + nickname + '\'' + ", gender=" + gender + '}'; } } ================================================ FILE: lab-71-http-debug/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 lab-71-http-debug pom lab-71-idea-http-client ================================================ FILE: lab-71-http-debug/《芋道 Spring Boot API 接口调试 IDEA HTTP Client》.md ================================================ ================================================ FILE: lab-72-minio/pom.xml ================================================ org.springframework.boot spring-boot-starter-parent 2.6.4 4.0.0 lab-72-minio org.springframework.boot spring-boot-starter-web io.minio minio 8.2.2 ================================================ FILE: lab-72-minio/src/main/java/cn/iocoder/springboot/lab72/MinIOApplication.java ================================================ package cn.iocoder.springboot.lab72; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class MinIOApplication { public static void main(String[] args) { SpringApplication.run(MinIOApplication.class, args); } } ================================================ FILE: lab-72-minio/src/main/java/cn/iocoder/springboot/lab72/config/MinIOConfiguration.java ================================================ package cn.iocoder.springboot.lab72.config; import io.minio.MinioClient; import org.springframework.context.annotation.*; @Configuration public class MinIOConfiguration { @Bean public MinioClient minioClient() { // Minio 配置。实际项目中,定义到 application.yml 配置文件中 String endpoint = "http://127.0.0.1:9000"; String accessKey = "admin"; String secretKey = "password"; // 创建 MinioClient 客户端 return MinioClient.builder() .endpoint(endpoint) .credentials(accessKey, secretKey) .build(); } } ================================================ FILE: lab-72-minio/src/main/java/cn/iocoder/springboot/lab72/controller/FileController.java ================================================ package cn.iocoder.springboot.lab72.controller; import io.minio.*; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; import java.util.UUID; @RestController @RequestMapping("/file") public class FileController { @Resource private MinioClient minioClient; // Minio 配置。实际项目中,定义到 application.yml 配置文件中 private String endpoint = "http://127.0.0.1:9000"; private String bucket = "yudaoyuanma"; /** * 上传文件 */ @PostMapping("/upload") public String upload(@RequestParam("file") MultipartFile file) throws Exception { // 上传 String path = UUID.randomUUID().toString(); // 文件名,使用 UUID 随机 minioClient.putObject(PutObjectArgs.builder() .bucket(bucket) // 存储桶 .object(path) // 文件名 .stream(file.getInputStream(), file.getSize(), -1) // 文件内容 .contentType(file.getContentType()) // 文件类型 .build()); // 拼接路径 return String.format("%s/%s/%s", endpoint, bucket, path); } /** * 删除文件 */ @DeleteMapping("/delete") public void delete(@RequestParam("path") String path) throws Exception { minioClient.removeObject(RemoveObjectArgs.builder() .bucket(bucket) // 存储桶 .object(path) // 文件名 .build()); } } ================================================ FILE: lab-72-minio/《芋道 Spring Boot 对象存储 MinIO 入门》.md ================================================ ================================================ FILE: labx-01-spring-cloud-alibaba-nacos-discovery/labx-01-sca-nacos-discovery-demo01-consumer/pom.xml ================================================ labx-01-spring-cloud-alibaba-nacos-discovery cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-01-sca-nacos-discovery-demo01-consumer 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx01.nacosdemo.consumer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import java.util.List; @SpringBootApplication // @EnableDiscoveryClient public class DemoConsumerApplication { public static void main(String[] args) { SpringApplication.run(DemoConsumerApplication.class, args); } @Configuration public class RestTemplateConfiguration { @Bean public RestTemplate restTemplate() { return new RestTemplate(); } } @RestController static class TestController { @Autowired private DiscoveryClient discoveryClient; @Autowired private RestTemplate restTemplate; @Autowired private LoadBalancerClient loadBalancerClient; @GetMapping("/hello") public String hello(String name) { // 获得服务 `demo-provider` 的一个实例 ServiceInstance instance; if (true) { // 获取服务 `demo-provider` 对应的实例列表 List instances = discoveryClient.getInstances("demo-provider"); // 选择第一个 instance = instances.size() > 0 ? instances.get(0) : null; } else { instance = loadBalancerClient.choose("demo-provider"); } // 发起调用 if (instance == null) { throw new IllegalStateException("获取不到实例"); } String targetUrl = instance.getUri() + "/echo?name=" + name; String response = restTemplate.getForObject(targetUrl, String.class); // 返回结果 return "consumer:" + response; } } } ================================================ FILE: labx-01-spring-cloud-alibaba-nacos-discovery/labx-01-sca-nacos-discovery-demo01-consumer/src/main/resources/application.yaml ================================================ spring: application: name: demo-consumer # Spring 应用名 cloud: nacos: # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 service: ${spring.application.name} # 注册到 Nacos 的服务名。默认值为 ${spring.application.name}。 server: port: 28080 # 服务器端口。默认为 8080 ================================================ FILE: labx-01-spring-cloud-alibaba-nacos-discovery/labx-01-sca-nacos-discovery-demo01-provider/pom.xml ================================================ labx-01-spring-cloud-alibaba-nacos-discovery cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-01-sca-nacos-discovery-demo01-provider 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx01.nacosdemo.provider; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @SpringBootApplication @EnableDiscoveryClient public class DemoProviderApplication { public static void main(String[] args) { SpringApplication.run(DemoProviderApplication.class, args); } @RestController static class TestController { @GetMapping("/echo") public String echo(String name) { return "provider:" + name; } } } ================================================ FILE: labx-01-spring-cloud-alibaba-nacos-discovery/labx-01-sca-nacos-discovery-demo01-provider/src/main/resources/application.yaml ================================================ spring: application: name: demo-provider # Spring 应用名 cloud: nacos: # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 service: ${spring.application.name} # 注册到 Nacos 的服务名。默认值为 ${spring.application.name}。 server: port: 18080 # 服务器端口。默认为 8080 ================================================ FILE: labx-01-spring-cloud-alibaba-nacos-discovery/labx-01-sca-nacos-discovery-demo02-consumer/pom.xml ================================================ labx-01-spring-cloud-alibaba-nacos-discovery cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-01-sca-nacos-discovery-demo02-consumer 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx01.nacosdemo.consumer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import java.util.List; @SpringBootApplication // @EnableDiscoveryClient public class DemoConsumerApplication { public static void main(String[] args) { SpringApplication.run(DemoConsumerApplication.class, args); } @Configuration public class RestTemplateConfiguration { @Bean public RestTemplate restTemplate() { return new RestTemplate(); } } @RestController static class TestController { @Autowired private DiscoveryClient discoveryClient; @Autowired private RestTemplate restTemplate; @Autowired private LoadBalancerClient loadBalancerClient; @GetMapping("/hello") public String hello(String name) { // 获得服务 `demo-provider` 的一个实例 ServiceInstance instance; if (true) { // 获取服务 `demo-provider` 对应的实例列表 List instances = discoveryClient.getInstances("demo-provider"); // 选择第一个 instance = instances.size() > 0 ? instances.get(0) : null; } else { instance = loadBalancerClient.choose("demo-provider"); } // 发起调用 if (instance == null) { throw new IllegalStateException("获取不到实例"); } String targetUrl = instance.getUri() + "/echo?name=" + name; String response = restTemplate.getForObject(targetUrl, String.class); // 返回结果 return "consumer:" + response; } } } ================================================ FILE: labx-01-spring-cloud-alibaba-nacos-discovery/labx-01-sca-nacos-discovery-demo02-consumer/src/main/resources/application-dev.yaml ================================================ spring: cloud: nacos: # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 namespace: 14226a0d-799f-424d-8905-162f6a8bf409 # Nacos 命名空间 dev 的编号 ================================================ FILE: labx-01-spring-cloud-alibaba-nacos-discovery/labx-01-sca-nacos-discovery-demo02-consumer/src/main/resources/application-uat.yaml ================================================ spring: cloud: nacos: # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 namespace: bc8c8c2d-bd85-42bb-ada3-1a8f940ceb20 # Nacos 命名空间 uat 的编号 ================================================ FILE: labx-01-spring-cloud-alibaba-nacos-discovery/labx-01-sca-nacos-discovery-demo02-consumer/src/main/resources/application.yaml ================================================ spring: application: name: demo-consumer # Spring 应用名 server: port: 28080 # 服务器端口。默认为 8080 ================================================ FILE: labx-01-spring-cloud-alibaba-nacos-discovery/labx-01-sca-nacos-discovery-demo02-provider/pom.xml ================================================ labx-01-spring-cloud-alibaba-nacos-discovery cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-01-sca-nacos-discovery-demo02-provider 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx01.nacosdemo.provider; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @SpringBootApplication @EnableDiscoveryClient public class DemoProviderApplication { public static void main(String[] args) { SpringApplication.run(DemoProviderApplication.class, args); } @RestController static class TestController { @GetMapping("/echo") public String echo(String name) { return "provider:" + name; } } } ================================================ FILE: labx-01-spring-cloud-alibaba-nacos-discovery/labx-01-sca-nacos-discovery-demo02-provider/src/main/resources/application-dev.yaml ================================================ spring: cloud: nacos: # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 namespace: 14226a0d-799f-424d-8905-162f6a8bf409 # Nacos 命名空间 dev 的编号 ================================================ FILE: labx-01-spring-cloud-alibaba-nacos-discovery/labx-01-sca-nacos-discovery-demo02-provider/src/main/resources/application-uat.yaml ================================================ spring: cloud: nacos: # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 namespace: bc8c8c2d-bd85-42bb-ada3-1a8f940ceb20 # Nacos 命名空间 uat 的编号 ================================================ FILE: labx-01-spring-cloud-alibaba-nacos-discovery/labx-01-sca-nacos-discovery-demo02-provider/src/main/resources/application.yaml ================================================ spring: application: name: demo-provider # Spring 应用名 server: port: 18080 # 服务器端口。默认为 8080 ================================================ FILE: labx-01-spring-cloud-alibaba-nacos-discovery/labx-01-sca-nacos-discovery-demo03-consumer/pom.xml ================================================ labx-01-spring-cloud-alibaba-nacos-discovery cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-01-sca-nacos-discovery-demo03-consumer 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery org.springframework.boot spring-boot-starter-actuator ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx01.nacosdemo.consumer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import java.util.List; @SpringBootApplication // @EnableDiscoveryClient public class DemoConsumerApplication { public static void main(String[] args) { SpringApplication.run(DemoConsumerApplication.class, args); } @Configuration public class RestTemplateConfiguration { @Bean public RestTemplate restTemplate() { return new RestTemplate(); } } @RestController static class TestController { @Autowired private DiscoveryClient discoveryClient; @Autowired private RestTemplate restTemplate; @Autowired private LoadBalancerClient loadBalancerClient; @GetMapping("/hello") public String hello(String name) { // 获得服务 `demo-provider` 的一个实例 ServiceInstance instance; if (true) { // 获取服务 `demo-provider` 对应的实例列表 List instances = discoveryClient.getInstances("demo-provider"); // 选择第一个 instance = instances.size() > 0 ? instances.get(0) : null; } else { instance = loadBalancerClient.choose("demo-provider"); } // 发起调用 if (instance == null) { throw new IllegalStateException("获取不到实例"); } String targetUrl = instance.getUri() + "/echo?name=" + name; String response = restTemplate.getForObject(targetUrl, String.class); // 返回结果 return "consumer:" + response; } } } ================================================ FILE: labx-01-spring-cloud-alibaba-nacos-discovery/labx-01-sca-nacos-discovery-demo03-consumer/src/main/resources/application.yaml ================================================ spring: application: name: demo-consumer # Spring 应用名 cloud: nacos: # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 service: ${spring.application.name} # 注册到 Nacos 的服务名。默认值为 ${spring.application.name}。 server: port: 28080 # 服务器端口。默认为 8080 management: endpoints: web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 endpoint: # Health 端点配置项,对应 HealthProperties 配置类 health: enabled: true # 是否开启。默认为 true 开启。 show-details: ALWAYS # 何时显示完整的健康信息。默认为 NEVER 都不展示。可选 WHEN_AUTHORIZED 当经过授权的用户;可选 ALWAYS 总是展示。 ================================================ FILE: labx-01-spring-cloud-alibaba-nacos-discovery/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-01-spring-cloud-alibaba-nacos-discovery pom labx-01-sca-nacos-discovery-demo01-provider labx-01-sca-nacos-discovery-demo01-consumer labx-01-sca-nacos-discovery-demo02-provider labx-01-sca-nacos-discovery-demo02-consumer labx-01-sca-nacos-discovery-demo03-consumer ================================================ FILE: labx-01-spring-cloud-alibaba-nacos-discovery/《芋道 Spring Cloud Alibaba 注册中心 Nacos 入门》.md ================================================ ================================================ FILE: labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo01-consumer/pom.xml ================================================ labx-02-spring-cloud-netflix-ribbon cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-02-scn-ribbon-demo01-consumer 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery ================================================ FILE: labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo01-consumer/src/main/java/cn/iocoder/springcloudnetflix/labx02/ribbondemo/consumer/DemoConsumerApplication.java ================================================ package cn.iocoder.springcloudnetflix.labx02.ribbondemo.consumer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @SpringBootApplication public class DemoConsumerApplication { public static void main(String[] args) { SpringApplication.run(DemoConsumerApplication.class, args); } @Configuration public class RestTemplateConfiguration { @Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); } } @RestController static class TestController { @Autowired private RestTemplate restTemplate; @Autowired private LoadBalancerClient loadBalancerClient; @GetMapping("/hello") public String hello(String name) { // 获得服务 `demo-provider` 的一个实例 ServiceInstance instance = loadBalancerClient.choose("demo-provider"); // 发起调用 String targetUrl = instance.getUri() + "/echo?name=" + name; String response = restTemplate.getForObject(targetUrl, String.class); // 返回结果 return "consumer:" + response; } @GetMapping("/hello02") public String hello02(String name) { // 直接使用 RestTemplate 调用服务 `demo-provider` String targetUrl = "http://demo-provider/echo?name=" + name; String response = restTemplate.getForObject(targetUrl, String.class); // 返回结果 return "consumer:" + response; } } } ================================================ FILE: labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo01-consumer/src/main/resources/application.yaml ================================================ spring: application: name: demo-consumer # Spring 应用名 cloud: nacos: # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 server: port: 28080 # 服务器端口。默认为 8080 #demo-provider: # ribbon: # NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #ribbon: # NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule ================================================ FILE: labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo01-provider/pom.xml ================================================ labx-02-spring-cloud-netflix-ribbon cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-02-scn-ribbon-demo01-provider 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery ================================================ FILE: labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo01-provider/src/main/java/cn/iocoder/springcloudnetflix/labx02/ribbondemo/provider/DemoProviderApplication.java ================================================ package cn.iocoder.springcloudnetflix.labx02.ribbondemo.provider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @SpringBootApplication public class DemoProviderApplication { public static void main(String[] args) { SpringApplication.run(DemoProviderApplication.class, args); } @RestController static class TestController { private Logger logger = LoggerFactory.getLogger(TestController.class); @Value("${server.port}") private Integer serverPort; @GetMapping("/echo") public String echo(String name) throws InterruptedException { // 模拟执行 100ms 时长。方便后续我们测试请求超时 Thread.sleep(100L); // 记录被调用的日志 logger.info("[echo][被调用啦 name({})]", name); return serverPort + "-provider:" + name; } } } ================================================ FILE: labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo01-provider/src/main/resources/application.yaml ================================================ spring: application: name: demo-provider # Spring 应用名 cloud: nacos: # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 server: port: ${random.int[10000,19999]} # 服务器端口。默认为 8080 # port: 18080 # 服务器端口。默认为 8080 ================================================ FILE: labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo02A-consumer/pom.xml ================================================ labx-02-spring-cloud-netflix-ribbon cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-02-scn-ribbon-demo02A-consumer 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery ================================================ FILE: labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo02A-consumer/src/main/java/cn/iocoder/springcloudnetflix/labx02/ribbondemo/consumer/DemoConsumerApplication.java ================================================ package cn.iocoder.springcloudnetflix.labx02.ribbondemo.consumer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @SpringBootApplication public class DemoConsumerApplication { public static void main(String[] args) { SpringApplication.run(DemoConsumerApplication.class, args); } @Configuration public class RestTemplateConfiguration { @Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); } } @RestController static class TestController { @Autowired private RestTemplate restTemplate; @Autowired private LoadBalancerClient loadBalancerClient; @GetMapping("/hello") public String hello(String name) { // 获得服务 `demo-provider` 的一个实例 ServiceInstance instance = loadBalancerClient.choose("demo-provider"); // 发起调用 String targetUrl = instance.getUri() + "/echo?name=" + name; String response = restTemplate.getForObject(targetUrl, String.class); // 返回结果 return "consumer:" + response; } @GetMapping("/hello02") public String hello02(String name) { // 直接使用 RestTemplate 调用服务 `demo-provider` String targetUrl = "http://demo-provider/echo?name=" + name; String response = restTemplate.getForObject(targetUrl, String.class); // 返回结果 return "consumer:" + response; } } } ================================================ FILE: labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo02A-consumer/src/main/resources/application.yaml ================================================ spring: application: name: demo-consumer # Spring 应用名 cloud: nacos: # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 server: port: 28080 # 服务器端口。默认为 8080 # 设置 Ribbon 客户端的自定义配置 #ribbon: # NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule demo-provider: ribbon: NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 负载均衡规则全类名 ================================================ FILE: labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo02B-consumer/pom.xml ================================================ labx-02-spring-cloud-netflix-ribbon cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-02-scn-ribbon-demo02B-consumer 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery ================================================ FILE: labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo02B-consumer/src/main/java/cn/iocoder/springcloudnetflix/labx02/ribbondemo/consumer/DemoConsumerApplication.java ================================================ package cn.iocoder.springcloudnetflix.labx02.ribbondemo.consumer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @SpringBootApplication public class DemoConsumerApplication { public static void main(String[] args) { SpringApplication.run(DemoConsumerApplication.class, args); } @Configuration public class RestTemplateConfiguration { @Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); } } @RestController static class TestController { @Autowired private RestTemplate restTemplate; @Autowired private LoadBalancerClient loadBalancerClient; @GetMapping("/hello") public String hello(String name) { // 获得服务 `demo-provider` 的一个实例 ServiceInstance instance = loadBalancerClient.choose("demo-provider"); // 发起调用 String targetUrl = instance.getUri() + "/echo?name=" + name; String response = restTemplate.getForObject(targetUrl, String.class); // 返回结果 return "consumer:" + response; } @GetMapping("/hello02") public String hello02(String name) { // 直接使用 RestTemplate 调用服务 `demo-provider` String targetUrl = "http://demo-provider/echo?name=" + name; String response = restTemplate.getForObject(targetUrl, String.class); // 返回结果 return "consumer:" + response; } // @GetMapping("/hello03") // public String hello03(String name) { // // 直接使用 RestTemplate 调用服务 `demo-provider` // String targetUrl = "http://demo-provider-2/echo?name=" + name; // String response = restTemplate.getForObject(targetUrl, String.class); // // 返回结果 // return "consumer:" + response; // } } } ================================================ FILE: labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo02B-consumer/src/main/java/cn/iocoder/springcloudnetflix/labx02/ribbondemo/consumer/config/RibbonConfiguration.java ================================================ package cn.iocoder.springcloudnetflix.labx02.ribbondemo.consumer.config; import org.springframework.cloud.netflix.ribbon.RibbonClient; import org.springframework.cloud.netflix.ribbon.RibbonClients; import org.springframework.context.annotation.Configuration; import ribbon.DefaultRibbonClientConfiguration; import ribbon.UserProviderRibbonClientConfiguration; @Configuration @RibbonClients( value = { @RibbonClient(name = "demo-provider", configuration = UserProviderRibbonClientConfiguration.class) // 客户端级别的配置 }, defaultConfiguration = DefaultRibbonClientConfiguration.class // 全局配置 ) public class RibbonConfiguration { } ================================================ FILE: labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo02B-consumer/src/main/java/ribbon/DefaultRibbonClientConfiguration.java ================================================ package ribbon; import com.netflix.loadbalancer.IRule; import com.netflix.loadbalancer.RoundRobinRule; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class DefaultRibbonClientConfiguration { @Bean public IRule ribbonDefaultRule() { return new RoundRobinRule(); } } ================================================ FILE: labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo02B-consumer/src/main/java/ribbon/UserProviderRibbonClientConfiguration.java ================================================ package ribbon; import com.netflix.loadbalancer.IRule; import com.netflix.loadbalancer.RandomRule; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; @Configuration public class UserProviderRibbonClientConfiguration { @Bean @Primary public IRule ribbonCustomRule() { return new RandomRule(); } } ================================================ FILE: labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo02B-consumer/src/main/resources/application.yaml ================================================ spring: application: name: demo-consumer # Spring 应用名 cloud: nacos: # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 server: port: 28080 # 服务器端口。默认为 8080 ================================================ FILE: labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo03-consumer/pom.xml ================================================ labx-02-spring-cloud-netflix-ribbon cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-02-scn-ribbon-demo03-consumer 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery ================================================ FILE: labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo03-consumer/src/main/java/cn/iocoder/springcloudnetflix/labx02/ribbondemo/consumer/DemoConsumerApplication.java ================================================ package cn.iocoder.springcloudnetflix.labx02.ribbondemo.consumer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @SpringBootApplication public class DemoConsumerApplication { public static void main(String[] args) { SpringApplication.run(DemoConsumerApplication.class, args); } @Configuration public class RestTemplateConfiguration { @Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); } } @RestController static class TestController { @Autowired private RestTemplate restTemplate; @Autowired private LoadBalancerClient loadBalancerClient; @GetMapping("/hello") public String hello(String name) { // 获得服务 `demo-provider` 的一个实例 ServiceInstance instance = loadBalancerClient.choose("demo-provider"); // 发起调用 String targetUrl = instance.getUri() + "/echo?name=" + name; String response = restTemplate.getForObject(targetUrl, String.class); // 返回结果 return "consumer:" + response; } @GetMapping("/hello02") public String hello02(String name) { // 直接使用 RestTemplate 调用服务 `demo-provider` String targetUrl = "http://demo-provider/echo?name=" + name; String response = restTemplate.getForObject(targetUrl, String.class); // 返回结果 return "consumer:" + response; } } } ================================================ FILE: labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo03-consumer/src/main/resources/application.yaml ================================================ spring: application: name: demo-consumer # Spring 应用名 cloud: nacos: # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 server: port: 28080 # 服务器端口。默认为 8080 demo-provider: ribbon: NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule #ribbon: # NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule ================================================ FILE: labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo04-consumer/pom.xml ================================================ labx-02-spring-cloud-netflix-ribbon cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-02-scn-ribbon-demo04-consumer 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery ================================================ FILE: labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo04-consumer/src/main/java/cn/iocoder/springcloudnetflix/labx02/ribbondemo/consumer/DemoConsumerApplication.java ================================================ package cn.iocoder.springcloudnetflix.labx02.ribbondemo.consumer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @SpringBootApplication public class DemoConsumerApplication { public static void main(String[] args) { SpringApplication.run(DemoConsumerApplication.class, args); } @Configuration public class RestTemplateConfiguration { @Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); } } @RestController static class TestController { @Autowired private RestTemplate restTemplate; @Autowired private LoadBalancerClient loadBalancerClient; @GetMapping("/hello") public String hello(String name) { // 获得服务 `demo-provider` 的一个实例 ServiceInstance instance = loadBalancerClient.choose("demo-provider"); // 发起调用 String targetUrl = instance.getUri() + "/echo?name=" + name; String response = restTemplate.getForObject(targetUrl, String.class); // 返回结果 return "consumer:" + response; } @GetMapping("/hello02") public String hello02(String name) { // 直接使用 RestTemplate 调用服务 `demo-provider` String targetUrl = "http://demo-provider/echo?name=" + name; String response = restTemplate.getForObject(targetUrl, String.class); // 返回结果 return "consumer:" + response; } } } ================================================ FILE: labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo04-consumer/src/main/resources/application.yaml ================================================ spring: application: name: demo-consumer # Spring 应用名 cloud: nacos: # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 server: port: 28080 # 服务器端口。默认为 8080 ribbon: # Ribbon 饥饿加载配置项,对应 RibbonEagerLoadProperties 配置类 eager-load: enabled: true # 是否开启饥饿加载。默认为 false 不开启 clients: user-provider # 开启饥饿加载的 Ribbon 客户端名字。如果有多个,使用 , 逗号分隔。 ================================================ FILE: labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo05-consumer/pom.xml ================================================ labx-02-spring-cloud-netflix-ribbon cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-02-scn-ribbon-demo05-consumer 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery com.squareup.okhttp3 okhttp ================================================ FILE: labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo05-consumer/src/main/java/cn/iocoder/springcloudnetflix/labx02/ribbondemo/consumer/DemoConsumerApplication.java ================================================ package cn.iocoder.springcloudnetflix.labx02.ribbondemo.consumer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @SpringBootApplication public class DemoConsumerApplication { public static void main(String[] args) { SpringApplication.run(DemoConsumerApplication.class, args); } @Configuration public class RestTemplateConfiguration { @Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); } } @RestController static class TestController { @Autowired private RestTemplate restTemplate; @Autowired private LoadBalancerClient loadBalancerClient; @GetMapping("/hello") public String hello(String name) { // 获得服务 `demo-provider` 的一个实例 ServiceInstance instance = loadBalancerClient.choose("demo-provider"); // 发起调用 String targetUrl = instance.getUri() + "/echo?name=" + name; String response = restTemplate.getForObject(targetUrl, String.class); // 返回结果 return "consumer:" + response; } @GetMapping("/hello02") public String hello02(String name) { // 直接使用 RestTemplate 调用服务 `demo-provider` String targetUrl = "http://demo-provider/echo?name=" + name; String response = restTemplate.getForObject(targetUrl, String.class); // 返回结果 return "consumer:" + response; } } } ================================================ FILE: labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo05-consumer/src/main/resources/application.yaml ================================================ spring: application: name: demo-consumer # Spring 应用名 cloud: nacos: # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 server: port: 28080 # 服务器端口。默认为 8080 ribbon: # okhttp: # enabled: true # 设置使用 OkHttp,对应 OkHttpRibbonConfiguration 配置类 restclient: enabled: true # 设置使用 Jersey Client,对应 RestClientRibbonConfiguration 配置类 # httpclient: # enabled: true # 设置使用 Apache HttpClient,对应 HttpClientRibbonConfiguration 配置类 # ConnectTimeout: 1 # 请求的连接超时时间,单位:毫秒。默认为 1000 # ReadTimeout: 1 # 请求的读取超时时间,单位:毫秒。默认为 1000 ================================================ FILE: labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo06-consumer/pom.xml ================================================ labx-02-spring-cloud-netflix-ribbon cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-02-scn-ribbon-demo06-consumer 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery com.squareup.okhttp3 okhttp ================================================ FILE: labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo06-consumer/src/main/java/cn/iocoder/springcloudnetflix/labx02/ribbondemo/consumer/DemoConsumerApplication.java ================================================ package cn.iocoder.springcloudnetflix.labx02.ribbondemo.consumer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @SpringBootApplication public class DemoConsumerApplication { public static void main(String[] args) { SpringApplication.run(DemoConsumerApplication.class, args); } @Configuration public class RestTemplateConfiguration { @Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); } } @RestController static class TestController { @Autowired private RestTemplate restTemplate; @Autowired private LoadBalancerClient loadBalancerClient; @GetMapping("/hello") public String hello(String name) { // 获得服务 `demo-provider` 的一个实例 ServiceInstance instance = loadBalancerClient.choose("demo-provider"); // 发起调用 String targetUrl = instance.getUri() + "/echo?name=" + name; String response = restTemplate.getForObject(targetUrl, String.class); // 返回结果 return "consumer:" + response; } @GetMapping("/hello02") public String hello02(String name) { // 直接使用 RestTemplate 调用服务 `demo-provider` String targetUrl = "http://demo-provider/echo?name=" + name; String response = restTemplate.getForObject(targetUrl, String.class); // 返回结果 return "consumer:" + response; } } } ================================================ FILE: labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo06-consumer/src/main/resources/application.yaml ================================================ spring: application: name: demo-consumer # Spring 应用名 cloud: nacos: # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 loadbalancer: # Spring Cloud Loadbalancer 重启配置,对应 LoadBalancerRetryProperties 配置类 retry: enabled: true # 是否开启重启,默认为 false 关闭重试。 server: port: 28080 # 服务器端口。默认为 8080 ribbon: restclient: enabled: true # 设置使用 Jersey Client,对应 RestClientRibbonConfiguration 配置类 demo-provider: ribbon: ConnectTimeout: 1000 # 请求的连接超时时间,单位:毫秒。默认为 1000 ReadTimeout: 1 # 请求的读取超时时间,单位:毫秒。默认为 1000 OkToRetryOnAllOperations: true # 是否对所有操作都进行重试,默认为 false。 MaxAutoRetries: 0 # 对当前服务的重试次数,默认为 0 次。 MaxAutoRetriesNextServer: 1 # 重新选择服务实例的次数,默认为 1 次。注意,不包含第 1 次哈。 ================================================ FILE: labx-02-spring-cloud-netflix-ribbon/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-02-spring-cloud-netflix-ribbon pom labx-02-scn-ribbon-demo01-provider labx-02-scn-ribbon-demo01-consumer labx-02-scn-ribbon-demo02A-consumer labx-02-scn-ribbon-demo02B-consumer labx-02-scn-ribbon-demo03-consumer labx-02-scn-ribbon-demo04-consumer labx-02-scn-ribbon-demo05-consumer labx-02-scn-ribbon-demo06-consumer ================================================ FILE: labx-02-spring-cloud-netflix-ribbon/《芋道 Spring Cloud Netflix 负载均衡 Ribbon 入门》.md ================================================ ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo01-consumer/pom.xml ================================================ labx-03-spring-cloud-feign cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-03-sc-feign-demo01-consumer 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery org.springframework.cloud spring-cloud-starter-openfeign ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo01-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/DemoConsumerApplication.java ================================================ package cn.iocoder.springcloud.labx03.feigndemo.consumer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication @EnableFeignClients public class DemoConsumerApplication { public static void main(String[] args) { SpringApplication.run(DemoConsumerApplication.class, args); } } ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo01-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/FeignDemo.java ================================================ /** * 用于展示纯 Feign 的示例 */ package cn.iocoder.springcloud.labx03.feigndemo.consumer; import feign.Feign; import feign.Param; import feign.RequestLine; // 商品 API interface ProductAPI { // 获得商品详情 @RequestLine("POST /products/{id}") String get(@Param("id") Integer id); } public class FeignDemo { public static void main(String[] args) { // 创建 ProductAPI 对象 ProductAPI productAPI = Feign.builder().target(ProductAPI.class, "http://www.iocoder.cn"); // 目标地址 // 调用获得商品 String product = productAPI.get(1); System.out.println(product); } } ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo01-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/controller/ConsumerController.java ================================================ package cn.iocoder.springcloud.labx03.feigndemo.consumer.controller; import cn.iocoder.springcloud.labx03.feigndemo.consumer.feign.DemoProviderFeignClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class ConsumerController { @Autowired private DemoProviderFeignClient demoProviderFeignClient; @GetMapping("/hello02") public String hello02(String name) { // 使用 Feign 调用接口 String response = demoProviderFeignClient.echo(name); // 返回结果 return "consumer:" + response; } } ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo01-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/feign/DemoProviderFeignClient.java ================================================ package cn.iocoder.springcloud.labx03.feigndemo.consumer.feign; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; @FeignClient(name = "demo-provider") public interface DemoProviderFeignClient { @GetMapping("/echo") String echo(@RequestParam("name") String name); } ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo01-consumer/src/main/resources/application.yaml ================================================ spring: application: name: demo-consumer # Spring 应用名 cloud: nacos: # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 server: port: 28080 # 服务器端口。默认为 8080 ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo01-provider/pom.xml ================================================ labx-03-spring-cloud-feign cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-03-sc-feign-demo01-provider 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo01-provider/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/provider/DemoProviderApplication.java ================================================ package cn.iocoder.springcloud.labx03.feigndemo.provider; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoProviderApplication { public static void main(String[] args) { SpringApplication.run(DemoProviderApplication.class, args); } } ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo01-provider/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/provider/controller/ProviderController.java ================================================ package cn.iocoder.springcloud.labx03.feigndemo.provider.controller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class ProviderController { private Logger logger = LoggerFactory.getLogger(ProviderController.class); @Value("${server.port}") private Integer serverPort; @GetMapping("/echo") public String echo(String name) throws InterruptedException { // 模拟执行 100ms 时长。方便后续我们测试请求超时 Thread.sleep(100L); // 记录被调用的日志 logger.info("[echo][被调用啦 name({})]", name); return serverPort + "-provider:" + name; } } ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo01-provider/src/main/resources/application.yaml ================================================ spring: application: name: demo-provider # Spring 应用名 cloud: nacos: # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 server: port: ${random.int[10000,19999]} # 服务器端口。默认为 8080 # port: 18080 # 服务器端口。默认为 8080 ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo02A-consumer/pom.xml ================================================ labx-03-spring-cloud-feign cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-03-sc-feign-demo02A-consumer 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery org.springframework.cloud spring-cloud-starter-openfeign ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo02A-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/DemoConsumerApplication.java ================================================ package cn.iocoder.springcloud.labx03.feigndemo.consumer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication @EnableFeignClients public class DemoConsumerApplication { public static void main(String[] args) { SpringApplication.run(DemoConsumerApplication.class, args); } } ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo02A-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/controller/ConsumerController.java ================================================ package cn.iocoder.springcloud.labx03.feigndemo.consumer.controller; import cn.iocoder.springcloud.labx03.feigndemo.consumer.feign.DemoProviderFeignClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class ConsumerController { @Autowired private DemoProviderFeignClient demoProviderFeignClient; @GetMapping("/hello02") public String hello02(String name) { // 使用 Feign 调用接口 String response = demoProviderFeignClient.echo(name); // 返回结果 return "consumer:" + response; } } ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo02A-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/feign/DemoProviderFeignClient.java ================================================ package cn.iocoder.springcloud.labx03.feigndemo.consumer.feign; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; @FeignClient(name = "demo-provider") public interface DemoProviderFeignClient { @GetMapping("/echo") String echo(@RequestParam("name") String name); } ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo02A-consumer/src/main/resources/application.yaml ================================================ spring: application: name: demo-consumer # Spring 应用名 cloud: nacos: # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 server: port: 28080 # 服务器端口。默认为 8080 logging: level: cn.iocoder.springcloud.labx03.feigndemo.consumer.feign: DEBUG feign: # Feign 客户端配置,对应 FeignClientProperties 配置属性类 client: # config 配置项是 Map 类型。key 为 Feign 客户端的名字,value 为 FeignClientConfiguration 对象 config: # 全局级别配置 default: logger-level: BASIC # 客户端级别配置 demo-provider: logger-level: FULL ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo02B-consumer/pom.xml ================================================ labx-03-spring-cloud-feign cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-03-sc-feign-demo02B-consumer 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery org.springframework.cloud spring-cloud-starter-openfeign ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo02B-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/DemoConsumerApplication.java ================================================ package cn.iocoder.springcloud.labx03.feigndemo.consumer; import cn.iocoder.springcloud.labx03.feigndemo.consumer.config.DefaultFeignClientConfiguration; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication @EnableFeignClients(defaultConfiguration = DefaultFeignClientConfiguration.class) public class DemoConsumerApplication { public static void main(String[] args) { SpringApplication.run(DemoConsumerApplication.class, args); } } ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo02B-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/config/DefaultFeignClientConfiguration.java ================================================ package cn.iocoder.springcloud.labx03.feigndemo.consumer.config; import feign.Logger; import org.springframework.context.annotation.Bean; /** * 全局 FeignClient 配置类 */ public class DefaultFeignClientConfiguration { @Bean public Logger.Level defaultFeignClientLoggerLevel() { return Logger.Level.BASIC; } } ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo02B-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/config/DemoProviderFeignClientConfiguration.java ================================================ package cn.iocoder.springcloud.labx03.feigndemo.consumer.config; import feign.Logger; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Primary; /** * 服务 demo-provider 的 FeignClient 配置类 */ public class DemoProviderFeignClientConfiguration { @Bean @Primary // 主 Bean public Logger.Level feignClientLoggerLevel() { return Logger.Level.FULL; } } ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo02B-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/controller/ConsumerController.java ================================================ package cn.iocoder.springcloud.labx03.feigndemo.consumer.controller; import cn.iocoder.springcloud.labx03.feigndemo.consumer.feign.DemoProviderFeignClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class ConsumerController { @Autowired private DemoProviderFeignClient demoProviderFeignClient; @GetMapping("/hello02") public String hello02(String name) { // 使用 Feign 调用接口 String response = demoProviderFeignClient.echo(name); // 返回结果 return "consumer:" + response; } } ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo02B-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/feign/DemoProviderFeignClient.java ================================================ package cn.iocoder.springcloud.labx03.feigndemo.consumer.feign; import cn.iocoder.springcloud.labx03.feigndemo.consumer.config.DemoProviderFeignClientConfiguration; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; @FeignClient(name = "demo-provider", configuration = DemoProviderFeignClientConfiguration.class) public interface DemoProviderFeignClient { @GetMapping("/echo") String echo(@RequestParam("name") String name); } ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo02B-consumer/src/main/resources/application.yaml ================================================ spring: application: name: demo-consumer # Spring 应用名 cloud: nacos: # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 server: port: 28080 # 服务器端口。默认为 8080 logging: level: cn.iocoder.springcloud.labx03.feigndemo.consumer.feign: DEBUG ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo03-consumer/pom.xml ================================================ labx-03-spring-cloud-feign cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-03-sc-feign-demo03-consumer 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import cn.iocoder.springboot.labs labx-03-sc-feign-demo03-provider-api 1.0-SNAPSHOT org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery org.springframework.cloud spring-cloud-starter-openfeign ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo03-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/DemoConsumerApplication.java ================================================ package cn.iocoder.springcloud.labx03.feigndemo.consumer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication @EnableFeignClients public class DemoConsumerApplication { public static void main(String[] args) { SpringApplication.run(DemoConsumerApplication.class, args); } } ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo03-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/controller/ConsumerController.java ================================================ package cn.iocoder.springcloud.labx03.feigndemo.consumer.controller; import cn.iocoder.springcloud.labx03.feigndemo.consumer.feign.DemoProviderFeignClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class ConsumerController { @Autowired private DemoProviderFeignClient demoProviderFeignClient; @GetMapping("/hello02") public String hello02(String name) { // 使用 Feign 调用接口 String response = demoProviderFeignClient.echo(name); // 返回结果 return "consumer:" + response; } } ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo03-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/feign/DemoProviderFeignClient.java ================================================ package cn.iocoder.springcloud.labx03.feigndemo.consumer.feign; import cn.iocoder.springcloud.labx03.feigndemo.provider.api.ProviderService; import org.springframework.cloud.openfeign.FeignClient; @FeignClient(name = "demo-provider") public interface DemoProviderFeignClient extends ProviderService { // @GetMapping("/echo") // String echo(@RequestParam("name") String name); } ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo03-consumer/src/main/resources/application.yaml ================================================ spring: application: name: demo-consumer # Spring 应用名 cloud: nacos: # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 server: port: 28080 # 服务器端口。默认为 8080 ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo03-provider/pom.xml ================================================ labx-03-spring-cloud-feign cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-03-sc-feign-demo03-provider 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import cn.iocoder.springboot.labs labx-03-sc-feign-demo03-provider-api 1.0-SNAPSHOT org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo03-provider/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/provider/DemoProviderApplication.java ================================================ package cn.iocoder.springcloud.labx03.feigndemo.provider; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoProviderApplication { public static void main(String[] args) { SpringApplication.run(DemoProviderApplication.class, args); } } ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo03-provider/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/provider/controller/ProviderController.java ================================================ package cn.iocoder.springcloud.labx03.feigndemo.provider.controller; import cn.iocoder.springcloud.labx03.feigndemo.provider.api.ProviderService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.RestController; @RestController public class ProviderController implements ProviderService { private Logger logger = LoggerFactory.getLogger(ProviderController.class); @Value("${server.port}") private Integer serverPort; @Override public String echo(String name) { // 模拟执行 100ms 时长。方便后续我们测试请求超时 try { Thread.sleep(100L); } catch (InterruptedException e) { throw new RuntimeException(e); } // 记录被调用的日志 logger.info("[echo][被调用啦 name({})]", name); return serverPort + "-provider:" + name; } // @GetMapping("/echo") // public String echo(String name) throws InterruptedException { // // 模拟执行 100ms 时长。方便后续我们测试请求超时 // Thread.sleep(100L); // // // 记录被调用的日志 // logger.info("[echo][被调用啦 name({})]", name); // // return serverPort + "-provider:" + name; // } } ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo03-provider/src/main/resources/application.yaml ================================================ spring: application: name: demo-provider # Spring 应用名 cloud: nacos: # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 server: port: ${random.int[10000,19999]} # 服务器端口。默认为 8080 # port: 18080 # 服务器端口。默认为 8080 ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo03-provider-api/pom.xml ================================================ labx-03-spring-cloud-feign cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-03-sc-feign-demo03-provider-api org.springframework spring-web 5.2.3.RELEASE provided ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo03-provider-api/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/provider/api/ProviderService.java ================================================ package cn.iocoder.springcloud.labx03.feigndemo.provider.api; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; public interface ProviderService { @GetMapping("/echo") String echo(@RequestParam("name") String name); } ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo04-consumer/pom.xml ================================================ labx-03-spring-cloud-feign cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-03-sc-feign-demo04-consumer 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery org.springframework.cloud spring-cloud-starter-openfeign ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo04-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/DemoConsumerApplication.java ================================================ package cn.iocoder.springcloud.labx03.feigndemo.consumer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication @EnableFeignClients public class DemoConsumerApplication { public static void main(String[] args) { SpringApplication.run(DemoConsumerApplication.class, args); } } ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo04-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/controller/ConsumerController.java ================================================ package cn.iocoder.springcloud.labx03.feigndemo.consumer.controller; import cn.iocoder.springcloud.labx03.feigndemo.consumer.dto.DemoDTO; import cn.iocoder.springcloud.labx03.feigndemo.consumer.feign.DemoProviderFeignClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.Map; @RestController public class ConsumerController { @Autowired private DemoProviderFeignClient demoProviderFeignClient; @GetMapping("/hello02") public String hello02(String name) { // 使用 Feign 调用接口 String response = demoProviderFeignClient.echo(name); // 返回结果 return "consumer:" + response; } @GetMapping("/test_get_demo") public DemoDTO testGetDemo(@RequestParam("type") int type, DemoDTO demoDTO) { // 方式一 if (type == 1) { return demoProviderFeignClient.getDemo(demoDTO); } else if (type == 2) { return demoProviderFeignClient.getDemo(demoDTO.getUsername(), demoDTO.getPassword()); } else { // 方式三 Map params = new HashMap<>(); params.put("username", demoDTO.getUsername()); params.put("password", demoDTO.getPassword()); return demoProviderFeignClient.getDemo(params); } } @GetMapping("/test_post_demo") public DemoDTO testPostDemo(DemoDTO demoDTO) { return demoProviderFeignClient.postDemo(demoDTO); } } ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo04-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/dto/DemoDTO.java ================================================ package cn.iocoder.springcloud.labx03.feigndemo.consumer.dto; public class DemoDTO { private String username; private String password; public String getUsername() { return username; } public DemoDTO setUsername(String username) { this.username = username; return this; } public String getPassword() { return password; } public DemoDTO setPassword(String password) { this.password = password; return this; } } ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo04-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/feign/DemoProviderFeignClient.java ================================================ package cn.iocoder.springcloud.labx03.feigndemo.consumer.feign; import cn.iocoder.springcloud.labx03.feigndemo.consumer.dto.DemoDTO; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.cloud.openfeign.SpringQueryMap; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; import java.util.Map; @FeignClient(name = "demo-provider") public interface DemoProviderFeignClient { @GetMapping("/echo") String echo(@RequestParam("name") String name); @GetMapping("/get_demo") // GET 方式一,最推荐 DemoDTO getDemo(@SpringQueryMap DemoDTO demoDTO); @GetMapping("/get_demo") // GET 方式二,相对推荐 DemoDTO getDemo(@RequestParam("username") String username, @RequestParam("password") String password); @GetMapping("/get_demo") // GET 方式三,不推荐 DemoDTO getDemo(@RequestParam Map params); @PostMapping("/post_demo") // POST 方式 DemoDTO postDemo(@RequestBody DemoDTO demoDTO); } ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo04-consumer/src/main/resources/application.yaml ================================================ spring: application: name: demo-consumer # Spring 应用名 cloud: nacos: # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 server: port: 28080 # 服务器端口。默认为 8080 ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo04-provider/pom.xml ================================================ labx-03-spring-cloud-feign cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-03-sc-feign-demo04-provider 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo04-provider/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/provider/DemoProviderApplication.java ================================================ package cn.iocoder.springcloud.labx03.feigndemo.provider; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoProviderApplication { public static void main(String[] args) { SpringApplication.run(DemoProviderApplication.class, args); } } ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo04-provider/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/provider/controller/ProviderController.java ================================================ package cn.iocoder.springcloud.labx03.feigndemo.provider.controller; import cn.iocoder.springcloud.labx03.feigndemo.provider.dto.DemoDTO; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @RestController public class ProviderController { private Logger logger = LoggerFactory.getLogger(ProviderController.class); @Value("${server.port}") private Integer serverPort; @GetMapping("/echo") public String echo(String name) throws InterruptedException { // 模拟执行 100ms 时长。方便后续我们测试请求超时 Thread.sleep(100L); // 记录被调用的日志 logger.info("[echo][被调用啦 name({})]", name); return serverPort + "-provider:" + name; } @GetMapping("/get_demo") public DemoDTO getDemo(DemoDTO demoDTO) { return demoDTO; } @PostMapping("/post_demo") public DemoDTO postDemo(@RequestBody DemoDTO demoDTO) { return demoDTO; } } ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo04-provider/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/provider/dto/DemoDTO.java ================================================ package cn.iocoder.springcloud.labx03.feigndemo.provider.dto; public class DemoDTO { private String username; private String password; public String getUsername() { return username; } public DemoDTO setUsername(String username) { this.username = username; return this; } public String getPassword() { return password; } public DemoDTO setPassword(String password) { this.password = password; return this; } } ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo04-provider/src/main/resources/application.yaml ================================================ spring: application: name: demo-provider # Spring 应用名 cloud: nacos: # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 server: port: ${random.int[10000,19999]} # 服务器端口。默认为 8080 # port: 18080 # 服务器端口。默认为 8080 ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo05-consumer/pom.xml ================================================ labx-03-spring-cloud-feign cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-03-sc-feign-demo05-consumer 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery org.springframework.cloud spring-cloud-starter-openfeign ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo05-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/DemoConsumerApplication.java ================================================ package cn.iocoder.springcloud.labx03.feigndemo.consumer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication @EnableFeignClients public class DemoConsumerApplication { public static void main(String[] args) { SpringApplication.run(DemoConsumerApplication.class, args); } } ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo05-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/controller/ConsumerController.java ================================================ package cn.iocoder.springcloud.labx03.feigndemo.consumer.controller; import cn.iocoder.springcloud.labx03.feigndemo.consumer.feign.DemoProviderFeignClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class ConsumerController { @Autowired private DemoProviderFeignClient demoProviderFeignClient; @GetMapping("/hello02") public String hello02(String name) { // 使用 Feign 调用接口 String response = demoProviderFeignClient.echo(name); // 返回结果 return "consumer:" + response; } } ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo05-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/feign/DemoProviderFeignClient.java ================================================ package cn.iocoder.springcloud.labx03.feigndemo.consumer.feign; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; //@FeignClient(name = "demo-provider") @FeignClient(name = "iocoder", url = "http://www.iocoder.cn") // 注意,保持 name 属性和 url 属性的 host 是一致的。 public interface DemoProviderFeignClient { // @GetMapping("/echo") // String echo(@RequestParam("name") String name); @GetMapping("/") // 请求首页 String echo(@RequestParam("name") String name); } ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo05-consumer/src/main/resources/application.yaml ================================================ spring: application: name: demo-consumer # Spring 应用名 cloud: nacos: # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 server: port: 28080 # 服务器端口。默认为 8080 ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo06A-consumer/pom.xml ================================================ labx-03-spring-cloud-feign cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-03-sc-feign-demo06A-consumer 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery org.springframework.cloud spring-cloud-starter-openfeign io.github.openfeign feign-httpclient ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo06A-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/DemoConsumerApplication.java ================================================ package cn.iocoder.springcloud.labx03.feigndemo.consumer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication @EnableFeignClients public class DemoConsumerApplication { public static void main(String[] args) { SpringApplication.run(DemoConsumerApplication.class, args); } } ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo06A-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/controller/ConsumerController.java ================================================ package cn.iocoder.springcloud.labx03.feigndemo.consumer.controller; import cn.iocoder.springcloud.labx03.feigndemo.consumer.feign.DemoProviderFeignClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class ConsumerController { @Autowired private DemoProviderFeignClient demoProviderFeignClient; @GetMapping("/hello02") public String hello02(String name) { // 使用 Feign 调用接口 String response = demoProviderFeignClient.echo(name); // 返回结果 return "consumer:" + response; } } ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo06A-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/feign/DemoProviderFeignClient.java ================================================ package cn.iocoder.springcloud.labx03.feigndemo.consumer.feign; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; @FeignClient(name = "demo-provider") public interface DemoProviderFeignClient { @GetMapping("/echo") String echo(@RequestParam("name") String name); } ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo06A-consumer/src/main/resources/application.yaml ================================================ spring: application: name: demo-consumer # Spring 应用名 cloud: nacos: # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 server: port: 28080 # 服务器端口。默认为 8080 feign: # Feign Apache HttpClient 配置项,对应 FeignHttpClientProperties 配置属性类 httpclient: enabled: true # 是否开启。默认为 true max-connections: 200 # 最大连接数。默认为 200 max-connections-per-route: 50 # 每个路由的最大连接数。默认为 50。router = host + port ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo06B-consumer/pom.xml ================================================ labx-03-spring-cloud-feign cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-03-sc-feign-demo06B-consumer 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery org.springframework.cloud spring-cloud-starter-openfeign io.github.openfeign feign-okhttp ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo06B-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/DemoConsumerApplication.java ================================================ package cn.iocoder.springcloud.labx03.feigndemo.consumer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication @EnableFeignClients public class DemoConsumerApplication { public static void main(String[] args) { SpringApplication.run(DemoConsumerApplication.class, args); } } ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo06B-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/controller/ConsumerController.java ================================================ package cn.iocoder.springcloud.labx03.feigndemo.consumer.controller; import cn.iocoder.springcloud.labx03.feigndemo.consumer.feign.DemoProviderFeignClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class ConsumerController { @Autowired private DemoProviderFeignClient demoProviderFeignClient; @GetMapping("/hello02") public String hello02(String name) { // 使用 Feign 调用接口 String response = demoProviderFeignClient.echo(name); // 返回结果 return "consumer:" + response; } } ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo06B-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/feign/DemoProviderFeignClient.java ================================================ package cn.iocoder.springcloud.labx03.feigndemo.consumer.feign; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; @FeignClient(name = "demo-provider") public interface DemoProviderFeignClient { @GetMapping("/echo") String echo(@RequestParam("name") String name); } ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo06B-consumer/src/main/resources/application.yaml ================================================ spring: application: name: demo-consumer # Spring 应用名 cloud: nacos: # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 server: port: 28080 # 服务器端口。默认为 8080 feign: httpclient: enabled: false # 是否开启。默认为 true okhttp: enabled: true # 是否开启。默认为 false ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo07-consumer/pom.xml ================================================ labx-03-spring-cloud-feign cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-03-sc-feign-demo07-consumer 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery org.springframework.cloud spring-cloud-starter-openfeign io.github.openfeign feign-httpclient ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo07-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/DemoConsumerApplication.java ================================================ package cn.iocoder.springcloud.labx03.feigndemo.consumer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication @EnableFeignClients public class DemoConsumerApplication { public static void main(String[] args) { SpringApplication.run(DemoConsumerApplication.class, args); } } ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo07-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/controller/ConsumerController.java ================================================ package cn.iocoder.springcloud.labx03.feigndemo.consumer.controller; import cn.iocoder.springcloud.labx03.feigndemo.consumer.feign.DemoProviderFeignClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class ConsumerController { @Autowired private DemoProviderFeignClient demoProviderFeignClient; @GetMapping("/hello02") public String hello02(String name) { // 使用 Feign 调用接口 String response = demoProviderFeignClient.echo(name); // 返回结果 return "consumer:" + response; } } ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo07-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/feign/DemoProviderFeignClient.java ================================================ package cn.iocoder.springcloud.labx03.feigndemo.consumer.feign; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; @FeignClient(name = "demo-provider") public interface DemoProviderFeignClient { @GetMapping("/echo") String echo(@RequestParam("name") String name); } ================================================ FILE: labx-03-spring-cloud-feign/labx-03-sc-feign-demo07-consumer/src/main/resources/application.yaml ================================================ spring: application: name: demo-consumer # Spring 应用名 cloud: nacos: # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 # loadbalancer: # # Spring Cloud Loadbalancer 重启配置,对应 LoadBalancerRetryProperties 配置类 # retry: # enabled: true # 是否开启重启,默认为 false 关闭重试。 server: port: 28080 # 服务器端口。默认为 8080 ribbon: ConnectTimeout: 1000 # 请求的连接超时时间,单位:毫秒。默认为 1000 ReadTimeout: 1 # 请求的读取超时时间,单位:毫秒。默认为 1000 OkToRetryOnAllOperations: true # 是否对所有操作都进行重试,默认为 false。 MaxAutoRetries: 0 # 对当前服务的重试次数,默认为 0 次。 MaxAutoRetriesNextServer: 1 # 重新选择服务实例的次数,默认为 1 次。注意,不包含第 1 次哈。 ================================================ FILE: labx-03-spring-cloud-feign/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-03-spring-cloud-feign pom labx-03-sc-feign-demo01-provider labx-03-sc-feign-demo01-consumer labx-03-sc-feign-demo02A-consumer labx-03-sc-feign-demo02B-consumer labx-03-sc-feign-demo03-provider-api labx-03-sc-feign-demo03-provider labx-03-sc-feign-demo03-consumer labx-03-sc-feign-demo04-provider labx-03-sc-feign-demo04-consumer labx-03-sc-feign-demo05-consumer labx-03-sc-feign-demo06A-consumer labx-03-sc-feign-demo06B-consumer labx-03-sc-feign-demo07-consumer ================================================ FILE: labx-03-spring-cloud-feign/《芋道 Spring Cloud 声明式调用 Feign 入门》.md ================================================ ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-actuator-provider/pom.xml ================================================ labx-04-spring-cloud-alibaba-sentinel cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-04-sca-sentinel-actuator-provider 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-sentinel org.springframework.boot spring-boot-starter-actuator ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-actuator-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/DemoProviderApplication.java ================================================ package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoProviderApplication { public static void main(String[] args) { SpringApplication.run(DemoProviderApplication.class, args); } } ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-actuator-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/controller/DemoController.java ================================================ package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.controller; import com.alibaba.csp.sentinel.Entry; import com.alibaba.csp.sentinel.SphU; import com.alibaba.csp.sentinel.annotation.SentinelResource; import com.alibaba.csp.sentinel.slots.block.BlockException; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { // 测试【流量控制】 @GetMapping("/echo") public String echo() { return "echo"; } @GetMapping("/test") public String test() { return "test"; } // 测试【熔断降级】 @GetMapping("/sleep") public String sleep() throws InterruptedException { Thread.sleep(100L); return "sleep"; } // 测试【热点参数限流】 @GetMapping("/product_info") @SentinelResource("demo_product_info_hot") public String productInfo(Integer id) { return "商品编号:" + id; } // 测试「Sentinel 客户端 API」 @GetMapping("/entry_demo") public String entryDemo() { Entry entry = null; try { // <1> 访问资源 entry = SphU.entry("entry_demo"); // <2> ... 执行业务逻辑 return "执行成功"; } catch (BlockException ex) { // <3> return "被拒绝"; } finally { // <4> 释放资源 if (entry != null) { entry.exit(); } } } // 测试「Sentinel @SentinelResource 注解」 @GetMapping("/annotations_demo") @SentinelResource(value = "annotations_demo_resource", blockHandler = "blockHandler", fallback = "fallback") public String annotationsDemo(@RequestParam(required = false) Integer id) throws InterruptedException { if (id == null) { throw new IllegalArgumentException("id 参数不允许为空"); } return "success..."; } // BlockHandler 处理函数,参数最后多一个 BlockException,其余与原函数一致. public String blockHandler(Integer id, BlockException ex) { return "block:" + ex.getClass().getSimpleName(); } // Fallback 处理函数,函数签名与原函数一致或加一个 Throwable 类型的参数. public String fallback(Integer id, Throwable throwable) { return "fallback:" + throwable.getMessage(); } } ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-actuator-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/web/CustomBlockExceptionHandler.java ================================================ package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.web; import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler; import com.alibaba.csp.sentinel.slots.block.BlockException; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Component public class CustomBlockExceptionHandler implements BlockExceptionHandler { @Override public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception { throw e; } } ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-actuator-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/web/CustomRequestOriginParser.java ================================================ package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.web; import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import javax.servlet.http.HttpServletRequest; @Component public class CustomRequestOriginParser implements RequestOriginParser { @Override public String parseOrigin(HttpServletRequest request) { // 从 Header 中,获得请求来源 String origin = request.getHeader("s-user"); // 如果为空,给一个默认的 if (StringUtils.isEmpty(origin)) { origin = "default"; } return origin; } } ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-actuator-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/web/GlobalExceptionHandler.java ================================================ package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.web; import com.alibaba.csp.sentinel.slots.block.BlockException; import com.alibaba.fastjson.JSONObject; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; @Component @ControllerAdvice(basePackages = "cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider") public class GlobalExceptionHandler { @ResponseBody @ExceptionHandler(value = BlockException.class) // 因为这里是示例,所以暂时使用 JSONObject,实际项目最终定义一个 CommonResult。 public JSONObject blockExceptionHandler(BlockException blockException) { return new JSONObject().fluentPut("code", 1024) .fluentPut("msg", "请求被拦截,拦截类型为 " + blockException.getClass().getSimpleName()); } } ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-actuator-provider/src/main/resources/application.yaml ================================================ spring: application: name: demo-provider cloud: # Sentinel 配置项,对应 SentinelProperties 配置属性类 sentinel: enabled: true # 是否开启。默认为 true 开启 eager: true # 是否饥饿加载。默认为 false 关闭 transport: dashboard: 127.0.0.1:7070 # Sentinel 控制台地址 filter: url-patterns: /** # 拦截请求的地址。默认为 /* management: endpoints: web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 endpoint: # Health 端点配置项,对应 HealthProperties 配置类 health: enabled: true # 是否开启。默认为 true 开启。 show-details: ALWAYS # 何时显示完整的健康信息。默认为 NEVER 都不展示。可选 WHEN_AUTHORIZED 当经过授权的用户;可选 ALWAYS 总是展示。 ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-apollo-provider/pom.xml ================================================ labx-04-spring-cloud-alibaba-sentinel cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-04-sca-sentinel-apollo-provider 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-sentinel com.alibaba.csp sentinel-datasource-apollo ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-apollo-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/DemoProviderApplication.java ================================================ package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoProviderApplication { public static void main(String[] args) { SpringApplication.run(DemoProviderApplication.class, args); } } ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-apollo-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/controller/DemoController.java ================================================ package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.controller; import com.alibaba.csp.sentinel.Entry; import com.alibaba.csp.sentinel.SphU; import com.alibaba.csp.sentinel.annotation.SentinelResource; import com.alibaba.csp.sentinel.slots.block.BlockException; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { // 测试【流量控制】 @GetMapping("/echo") public String echo() { return "echo"; } @GetMapping("/test") public String test() { return "test"; } // 测试【熔断降级】 @GetMapping("/sleep") public String sleep() throws InterruptedException { Thread.sleep(100L); return "sleep"; } // 测试【热点参数限流】 @GetMapping("/product_info") @SentinelResource("demo_product_info_hot") public String productInfo(Integer id) { return "商品编号:" + id; } // 测试「Sentinel 客户端 API」 @GetMapping("/entry_demo") public String entryDemo() { Entry entry = null; try { // <1> 访问资源 entry = SphU.entry("entry_demo"); // <2> ... 执行业务逻辑 return "执行成功"; } catch (BlockException ex) { // <3> return "被拒绝"; } finally { // <4> 释放资源 if (entry != null) { entry.exit(); } } } // 测试「Sentinel @SentinelResource 注解」 @GetMapping("/annotations_demo") @SentinelResource(value = "annotations_demo_resource", blockHandler = "blockHandler", fallback = "fallback") public String annotationsDemo(@RequestParam(required = false) Integer id) throws InterruptedException { if (id == null) { throw new IllegalArgumentException("id 参数不允许为空"); } return "success..."; } // BlockHandler 处理函数,参数最后多一个 BlockException,其余与原函数一致. public String blockHandler(Integer id, BlockException ex) { return "block:" + ex.getClass().getSimpleName(); } // Fallback 处理函数,函数签名与原函数一致或加一个 Throwable 类型的参数. public String fallback(Integer id, Throwable throwable) { return "fallback:" + throwable.getMessage(); } } ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-apollo-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/web/CustomBlockExceptionHandler.java ================================================ package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.web; import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler; import com.alibaba.csp.sentinel.slots.block.BlockException; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Component public class CustomBlockExceptionHandler implements BlockExceptionHandler { @Override public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception { throw e; } } ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-apollo-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/web/CustomRequestOriginParser.java ================================================ package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.web; import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import javax.servlet.http.HttpServletRequest; @Component public class CustomRequestOriginParser implements RequestOriginParser { @Override public String parseOrigin(HttpServletRequest request) { // 从 Header 中,获得请求来源 String origin = request.getHeader("s-user"); // 如果为空,给一个默认的 if (StringUtils.isEmpty(origin)) { origin = "default"; } return origin; } } ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-apollo-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/web/GlobalExceptionHandler.java ================================================ package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.web; import com.alibaba.csp.sentinel.slots.block.BlockException; import com.alibaba.fastjson.JSONObject; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; @Component @ControllerAdvice(basePackages = "cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider") public class GlobalExceptionHandler { @ResponseBody @ExceptionHandler(value = BlockException.class) // 因为这里是示例,所以暂时使用 JSONObject,实际项目最终定义一个 CommonResult。 public JSONObject blockExceptionHandler(BlockException blockException) { return new JSONObject().fluentPut("code", 1024) .fluentPut("msg", "请求被拦截,拦截类型为 " + blockException.getClass().getSimpleName()); } } ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-apollo-provider/src/main/resources/application.yaml ================================================ server: port: 18080 # 服务器端口,设置为 18080 避免和本地的 Apollo 端口冲突 # Apollo 相关配置项 app: id: ${spring.application.name} # 使用的 Apollo 的项目(应用)编号 apollo: meta: http://127.0.0.1:8080 # Apollo Meta Server 地址 bootstrap: enabled: true # 是否开启 Apollo 配置预加载功能。默认为 false。 eagerLoad: enable: true # 是否开启 Apollo 支持日志级别的加载时机。默认为 false。 namespaces: application # 使用的 Apollo 的命名空间,默认为 application。 spring: application: name: demo-provider cloud: # Sentinel 配置项,对应 SentinelProperties 配置属性类 sentinel: enabled: true # 是否开启。默认为 true 开启 eager: true # 是否饥饿加载。默认为 false 关闭 transport: dashboard: 127.0.0.1:7070 # Sentinel 控制台地址 filter: url-patterns: /** # 拦截请求的地址。默认为 /* # Sentinel 规则的数据源,是一个 Map 类型。key 为数据源名,可自定义;value 为数据源的具体配置 datasource: ds1: # 对应 DataSourcePropertiesConfiguration 类 apollo: namespaceName: application # Apollo 命名空间 flowRulesKey: sentinel.flow-rule # Apollo 配置 key data-type: json # 数据格式 rule-type: FLOW # 规则类型 ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-demo01-provider/pom.xml ================================================ labx-04-spring-cloud-alibaba-sentinel cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-04-sca-sentinel-demo01-provider 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-sentinel ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-demo01-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/DemoProviderApplication.java ================================================ package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoProviderApplication { public static void main(String[] args) { SpringApplication.run(DemoProviderApplication.class, args); } } ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-demo01-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/controller/DemoController.java ================================================ package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.controller; import com.alibaba.csp.sentinel.Entry; import com.alibaba.csp.sentinel.SphU; import com.alibaba.csp.sentinel.annotation.SentinelResource; import com.alibaba.csp.sentinel.slots.block.BlockException; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { // 测试【流量控制】 @GetMapping("/echo") public String echo() { return "echo"; } @GetMapping("/test") public String test() { return "test"; } // 测试【熔断降级】 @GetMapping("/sleep") public String sleep() throws InterruptedException { Thread.sleep(100L); return "sleep"; } // 测试【热点参数限流】 @GetMapping("/product_info") @SentinelResource("demo_product_info_hot") public String productInfo(Integer id) { return "商品编号:" + id; } // 测试「Sentinel 客户端 API」 @GetMapping("/entry_demo") public String entryDemo() { Entry entry = null; try { // <1> 访问资源 entry = SphU.entry("entry_demo"); // <2> ... 执行业务逻辑 return "执行成功"; } catch (BlockException ex) { // <3> return "被拒绝"; } finally { // <4> 释放资源 if (entry != null) { entry.exit(); } } } // 测试「Sentinel @SentinelResource 注解」 @GetMapping("/annotations_demo") @SentinelResource(value = "annotations_demo_resource", blockHandler = "blockHandler", fallback = "fallback") public String annotationsDemo(@RequestParam(required = false) Integer id) throws InterruptedException { if (id == null) { throw new IllegalArgumentException("id 参数不允许为空"); } return "success..."; } // BlockHandler 处理函数,参数最后多一个 BlockException,其余与原函数一致. public String blockHandler(Integer id, BlockException ex) { return "block:" + ex.getClass().getSimpleName(); } // Fallback 处理函数,函数签名与原函数一致或加一个 Throwable 类型的参数. public String fallback(Integer id, Throwable throwable) { return "fallback:" + throwable.getMessage(); } } ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-demo01-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/web/CustomBlockExceptionHandler.java ================================================ package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.web; import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler; import com.alibaba.csp.sentinel.slots.block.BlockException; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Component public class CustomBlockExceptionHandler implements BlockExceptionHandler { @Override public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception { throw e; } } ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-demo01-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/web/CustomRequestOriginParser.java ================================================ package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.web; import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import javax.servlet.http.HttpServletRequest; @Component public class CustomRequestOriginParser implements RequestOriginParser { @Override public String parseOrigin(HttpServletRequest request) { // 从 Header 中,获得请求来源 String origin = request.getHeader("s-user"); // 如果为空,给一个默认的 if (StringUtils.isEmpty(origin)) { origin = "default"; } return origin; } } ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-demo01-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/web/GlobalExceptionHandler.java ================================================ package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.web; import com.alibaba.csp.sentinel.slots.block.BlockException; import com.alibaba.fastjson.JSONObject; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; @Component @ControllerAdvice(basePackages = "cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider") public class GlobalExceptionHandler { @ResponseBody @ExceptionHandler(value = BlockException.class) // 因为这里是示例,所以暂时使用 JSONObject,实际项目最终定义一个 CommonResult。 public JSONObject blockExceptionHandler(BlockException blockException) { return new JSONObject().fluentPut("code", 1024) .fluentPut("msg", "请求被拦截,拦截类型为 " + blockException.getClass().getSimpleName()); } } ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-demo01-provider/src/main/resources/application.yaml ================================================ spring: application: name: demo-provider cloud: # Sentinel 配置项,对应 SentinelProperties 配置属性类 sentinel: enabled: true # 是否开启。默认为 true 开启 eager: true # 是否饥饿加载。默认为 false 关闭 transport: dashboard: 127.0.0.1:7070 # Sentinel 控制台地址 filter: url-patterns: /** # 拦截请求的地址。默认为 /* ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-feign-consumer/pom.xml ================================================ labx-04-spring-cloud-alibaba-sentinel cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-04-sca-sentinel-feign-consumer 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-sentinel org.springframework.cloud spring-cloud-starter-openfeign ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-feign-consumer/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/DemoConsumerApplication.java ================================================ package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication @EnableFeignClients public class DemoConsumerApplication { public static void main(String[] args) { SpringApplication.run(DemoConsumerApplication.class, args); } } ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-feign-consumer/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/controller/ConsumerController.java ================================================ package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.controller; import cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.feign.DemoProviderFeignClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/consumer") public class ConsumerController { @Autowired private DemoProviderFeignClient demoProviderFeignClient; @GetMapping("/echo") public String echo() { return demoProviderFeignClient.echo(); } } ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-feign-consumer/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/fallback/DemoProviderFeignClientFallback.java ================================================ package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.fallback; import cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.feign.DemoProviderFeignClient; public class DemoProviderFeignClientFallback implements DemoProviderFeignClient { private Throwable throwable; public DemoProviderFeignClientFallback(Throwable throwable) { this.throwable = throwable; } @Override public String echo() { return "fallback:" + throwable.getClass().getSimpleName(); } } ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-feign-consumer/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/fallback/DemoProviderFeignClientFallbackFactory.java ================================================ /* * Copyright 2013-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.fallback; import feign.hystrix.FallbackFactory; import org.springframework.stereotype.Component; @Component public class DemoProviderFeignClientFallbackFactory implements FallbackFactory { @Override public DemoProviderFeignClientFallback create(Throwable throwable) { // 可以给 DemoProviderFeignClientFallback 提供具体的 throwable 异常 return new DemoProviderFeignClientFallback(throwable); } } ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-feign-consumer/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/feign/DemoProviderFeignClient.java ================================================ package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.feign; import cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.fallback.DemoProviderFeignClientFallbackFactory; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; @FeignClient(name = "demo-provider", url = "http://127.0.0.1:8080", fallbackFactory = DemoProviderFeignClientFallbackFactory.class) public interface DemoProviderFeignClient { @GetMapping("/demo/echo") String echo(); } ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-feign-consumer/src/main/resources/application.yaml ================================================ spring: application: name: demo-consumer cloud: # Sentinel 配置项,对应 SentinelProperties 配置属性类 sentinel: enabled: true # 是否开启。默认为 true 开启 eager: true # 是否饥饿加载。默认为 false 关闭 transport: dashboard: 127.0.0.1:7070 # Sentinel 控制台地址 filter: url-patterns: /** # 拦截请求的地址。默认为 /* server: port: 8081 feign: sentinel: enabled: true # 开启 Sentinel 对 Feign 的支持,默认为 false 关闭。 ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-file-provider/pom.xml ================================================ labx-04-spring-cloud-alibaba-sentinel cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-04-sca-sentinel-file-provider 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-sentinel ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-file-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/DemoProviderApplication.java ================================================ package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoProviderApplication { public static void main(String[] args) { SpringApplication.run(DemoProviderApplication.class, args); } } ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-file-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/controller/DemoController.java ================================================ package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.controller; import com.alibaba.csp.sentinel.Entry; import com.alibaba.csp.sentinel.SphU; import com.alibaba.csp.sentinel.annotation.SentinelResource; import com.alibaba.csp.sentinel.slots.block.BlockException; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { // 测试【流量控制】 @GetMapping("/echo") public String echo() { return "echo"; } @GetMapping("/test") public String test() { return "test"; } // 测试【熔断降级】 @GetMapping("/sleep") public String sleep() throws InterruptedException { Thread.sleep(100L); return "sleep"; } // 测试【热点参数限流】 @GetMapping("/product_info") @SentinelResource("demo_product_info_hot") public String productInfo(Integer id) { return "商品编号:" + id; } // 测试「Sentinel 客户端 API」 @GetMapping("/entry_demo") public String entryDemo() { Entry entry = null; try { // <1> 访问资源 entry = SphU.entry("entry_demo"); // <2> ... 执行业务逻辑 return "执行成功"; } catch (BlockException ex) { // <3> return "被拒绝"; } finally { // <4> 释放资源 if (entry != null) { entry.exit(); } } } // 测试「Sentinel @SentinelResource 注解」 @GetMapping("/annotations_demo") @SentinelResource(value = "annotations_demo_resource", blockHandler = "blockHandler", fallback = "fallback") public String annotationsDemo(@RequestParam(required = false) Integer id) throws InterruptedException { if (id == null) { throw new IllegalArgumentException("id 参数不允许为空"); } return "success..."; } // BlockHandler 处理函数,参数最后多一个 BlockException,其余与原函数一致. public String blockHandler(Integer id, BlockException ex) { return "block:" + ex.getClass().getSimpleName(); } // Fallback 处理函数,函数签名与原函数一致或加一个 Throwable 类型的参数. public String fallback(Integer id, Throwable throwable) { return "fallback:" + throwable.getMessage(); } } ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-file-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/web/CustomBlockExceptionHandler.java ================================================ package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.web; import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler; import com.alibaba.csp.sentinel.slots.block.BlockException; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Component public class CustomBlockExceptionHandler implements BlockExceptionHandler { @Override public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception { throw e; } } ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-file-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/web/CustomRequestOriginParser.java ================================================ package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.web; import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import javax.servlet.http.HttpServletRequest; @Component public class CustomRequestOriginParser implements RequestOriginParser { @Override public String parseOrigin(HttpServletRequest request) { // 从 Header 中,获得请求来源 String origin = request.getHeader("s-user"); // 如果为空,给一个默认的 if (StringUtils.isEmpty(origin)) { origin = "default"; } return origin; } } ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-file-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/web/GlobalExceptionHandler.java ================================================ package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.web; import com.alibaba.csp.sentinel.slots.block.BlockException; import com.alibaba.fastjson.JSONObject; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; @Component @ControllerAdvice(basePackages = "cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider") public class GlobalExceptionHandler { @ResponseBody @ExceptionHandler(value = BlockException.class) // 因为这里是示例,所以暂时使用 JSONObject,实际项目最终定义一个 CommonResult。 public JSONObject blockExceptionHandler(BlockException blockException) { return new JSONObject().fluentPut("code", 1024) .fluentPut("msg", "请求被拦截,拦截类型为 " + blockException.getClass().getSimpleName()); } } ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-file-provider/src/main/resources/application.yaml ================================================ spring: application: name: demo-provider cloud: # Sentinel 配置项,对应 SentinelProperties 配置属性类 sentinel: enabled: true # 是否开启。默认为 true 开启 eager: true # 是否饥饿加载。默认为 false 关闭 transport: dashboard: 127.0.0.1:7070 # Sentinel 控制台地址 filter: url-patterns: /** # 拦截请求的地址。默认为 /* # Sentinel 规则的数据源,是一个 Map 类型。key 为数据源名,可自定义;value 为数据源的具体配置 datasource: ds1: # 对应 DataSourcePropertiesConfiguration 类 file: file: /Users/yunai/Sentinel/demo-provider/flow-rule.json # 配置规则所在文件。 recommendRefreshMs: 3000 # 定时读取实现刷新,默认为 3000 毫秒。 data-type: json # 数据格式 rule-type: FLOW # 规则类型 ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-nacos-provider/pom.xml ================================================ labx-04-spring-cloud-alibaba-sentinel cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-04-sca-sentinel-nacos-provider 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-sentinel com.alibaba.csp sentinel-datasource-nacos ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-nacos-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/DemoProviderApplication.java ================================================ package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoProviderApplication { public static void main(String[] args) { SpringApplication.run(DemoProviderApplication.class, args); } } ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-nacos-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/controller/DemoController.java ================================================ package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.controller; import com.alibaba.csp.sentinel.Entry; import com.alibaba.csp.sentinel.SphU; import com.alibaba.csp.sentinel.annotation.SentinelResource; import com.alibaba.csp.sentinel.slots.block.BlockException; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { // 测试【流量控制】 @GetMapping("/echo") public String echo() { return "echo"; } @GetMapping("/test") public String test() { return "test"; } // 测试【熔断降级】 @GetMapping("/sleep") public String sleep() throws InterruptedException { Thread.sleep(100L); return "sleep"; } // 测试【热点参数限流】 @GetMapping("/product_info") @SentinelResource("demo_product_info_hot") public String productInfo(Integer id) { return "商品编号:" + id; } // 测试「Sentinel 客户端 API」 @GetMapping("/entry_demo") public String entryDemo() { Entry entry = null; try { // <1> 访问资源 entry = SphU.entry("entry_demo"); // <2> ... 执行业务逻辑 return "执行成功"; } catch (BlockException ex) { // <3> return "被拒绝"; } finally { // <4> 释放资源 if (entry != null) { entry.exit(); } } } // 测试「Sentinel @SentinelResource 注解」 @GetMapping("/annotations_demo") @SentinelResource(value = "annotations_demo_resource", blockHandler = "blockHandler", fallback = "fallback") public String annotationsDemo(@RequestParam(required = false) Integer id) throws InterruptedException { if (id == null) { throw new IllegalArgumentException("id 参数不允许为空"); } return "success..."; } // BlockHandler 处理函数,参数最后多一个 BlockException,其余与原函数一致. public String blockHandler(Integer id, BlockException ex) { return "block:" + ex.getClass().getSimpleName(); } // Fallback 处理函数,函数签名与原函数一致或加一个 Throwable 类型的参数. public String fallback(Integer id, Throwable throwable) { return "fallback:" + throwable.getMessage(); } } ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-nacos-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/web/CustomBlockExceptionHandler.java ================================================ package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.web; import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler; import com.alibaba.csp.sentinel.slots.block.BlockException; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Component public class CustomBlockExceptionHandler implements BlockExceptionHandler { @Override public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception { throw e; } } ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-nacos-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/web/CustomRequestOriginParser.java ================================================ package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.web; import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import javax.servlet.http.HttpServletRequest; @Component public class CustomRequestOriginParser implements RequestOriginParser { @Override public String parseOrigin(HttpServletRequest request) { // 从 Header 中,获得请求来源 String origin = request.getHeader("s-user"); // 如果为空,给一个默认的 if (StringUtils.isEmpty(origin)) { origin = "default"; } return origin; } } ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-nacos-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/web/GlobalExceptionHandler.java ================================================ package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.web; import com.alibaba.csp.sentinel.slots.block.BlockException; import com.alibaba.fastjson.JSONObject; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; @Component @ControllerAdvice(basePackages = "cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider") public class GlobalExceptionHandler { @ResponseBody @ExceptionHandler(value = BlockException.class) // 因为这里是示例,所以暂时使用 JSONObject,实际项目最终定义一个 CommonResult。 public JSONObject blockExceptionHandler(BlockException blockException) { return new JSONObject().fluentPut("code", 1024) .fluentPut("msg", "请求被拦截,拦截类型为 " + blockException.getClass().getSimpleName()); } } ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-nacos-provider/src/main/resources/application.yaml ================================================ spring: application: name: demo-provider cloud: # Sentinel 配置项,对应 SentinelProperties 配置属性类 sentinel: enabled: true # 是否开启。默认为 true 开启 eager: true # 是否饥饿加载。默认为 false 关闭 transport: dashboard: 127.0.0.1:7070 # Sentinel 控制台地址 filter: url-patterns: /** # 拦截请求的地址。默认为 /* # Sentinel 规则的数据源,是一个 Map 类型。key 为数据源名,可自定义;value 为数据源的具体配置 datasource: ds1: # 对应 DataSourcePropertiesConfiguration 类 nacos: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 # data-id: demo-application-flow-rule # data-id: demo-provider-flow-rule namespace: # Nacos 命名空间 group-id: DEFAULT_GROUP # Nacos 分组 data-id: ${spring.application.name}-flow-rule # Nacos 配置集编号 data-type: json # 数据格式 rule-type: FLOW # 规则类型 ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-resttemplate-consumer/pom.xml ================================================ labx-04-spring-cloud-alibaba-sentinel cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-04-sca-sentinel-resttemplate-consumer 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-sentinel ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-resttemplate-consumer/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/DemoConsumerApplication.java ================================================ package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoConsumerApplication { public static void main(String[] args) { SpringApplication.run(DemoConsumerApplication.class, args); } } ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-resttemplate-consumer/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/config/RestTemplateConfiguration.java ================================================ package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.config; import com.alibaba.cloud.sentinel.annotation.SentinelRestTemplate; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; @Configuration public class RestTemplateConfiguration { @Bean @SentinelRestTemplate public RestTemplate restTemplate() { return new RestTemplate(); } } ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-resttemplate-consumer/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/controller/ConsumerController.java ================================================ package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @RestController @RequestMapping("/consumer") public class ConsumerController { @Autowired private RestTemplate restTemplate; @GetMapping("/echo") public String echo() { return restTemplate.getForObject("http://127.0.0.1:8080/demo/echo", String.class); } } ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-resttemplate-consumer/src/main/resources/application.yaml ================================================ spring: application: name: demo-consumer cloud: # Sentinel 配置项,对应 SentinelProperties 配置属性类 sentinel: enabled: true # 是否开启。默认为 true 开启 eager: true # 是否饥饿加载。默认为 false 关闭 transport: dashboard: 127.0.0.1:7070 # Sentinel 控制台地址 filter: url-patterns: /** # 拦截请求的地址。默认为 /* server: port: 8081 resttemplate: sentinel: enabled: true # 开启 Sentinel 对 Feign 的支持,默认为 true 开启。 ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-04-spring-cloud-alibaba-sentinel pom labx-04-sca-sentinel-demo01-provider labx-04-sca-sentinel-nacos-provider labx-04-sca-sentinel-apollo-provider labx-04-sca-sentinel-file-provider labx-04-sca-sentinel-feign-consumer labx-04-sca-sentinel-resttemplate-consumer labx-04-sca-sentinel-actuator-provider ================================================ FILE: labx-04-spring-cloud-alibaba-sentinel/《芋道 Spring Cloud Alibaba 服务容错 Sentinel 入门》.md ================================================ ================================================ FILE: labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-auto-refresh/pom.xml ================================================ labx-05-spring-cloud-alibaba-nacos-config cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-05-sca-nacos-config-auto-refresh 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-config ================================================ FILE: labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-auto-refresh/src/main/java/cn/iocoder/springcloudalibaba/labx5/nacosdemo/DemoApplication.java ================================================ package cn.iocoder.springcloudalibaba.labx5.nacosdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx5.nacosdemo.config; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component @ConfigurationProperties(prefix = "order") public class OrderProperties { /** * 订单支付超时时长,单位:秒。 */ private Integer payTimeoutSeconds; /** * 订单创建频率,单位:秒 */ private Integer createFrequencySeconds; public Integer getPayTimeoutSeconds() { return payTimeoutSeconds; } public OrderProperties setPayTimeoutSeconds(Integer payTimeoutSeconds) { this.payTimeoutSeconds = payTimeoutSeconds; return this; } public Integer getCreateFrequencySeconds() { return createFrequencySeconds; } public OrderProperties setCreateFrequencySeconds(Integer createFrequencySeconds) { this.createFrequencySeconds = createFrequencySeconds; return this; } // public String getDesc() { // return desc; // } // // public OrderProperties setDesc(String desc) { // this.desc = desc; // return this; // } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx5.nacosdemo.controller; import cn.iocoder.springcloudalibaba.labx5.nacosdemo.config.OrderProperties; import com.alibaba.fastjson.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Map; @RestController @RequestMapping("/demo") @RefreshScope public class DemoController { @Autowired private OrderProperties orderProperties; /** * 测试 @ConfigurationProperties 注解的配置属性类 */ @GetMapping("/test01") public OrderProperties test01() { return orderProperties; } @Value(value = "${order.pay-timeout-seconds}") private Integer payTimeoutSeconds; @Value(value = "${order.create-frequency-seconds}") private Integer createFrequencySeconds; /** * 测试 @Value 注解的属性 */ @GetMapping("/test02") public Map test02() { return new JSONObject().fluentPut("payTimeoutSeconds", payTimeoutSeconds) .fluentPut("createFrequencySeconds", createFrequencySeconds); } private Logger logger = LoggerFactory.getLogger(getClass()); @GetMapping("/logger") public void logger() { logger.debug("[logger][测试一下]"); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx5.nacosdemo.listener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.context.environment.EnvironmentChangeEvent; import org.springframework.context.ApplicationListener; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.stereotype.Component; @Component public class DemoEnvironmentChangeListener implements ApplicationListener { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private ConfigurableEnvironment environment; @Override public void onApplicationEvent(EnvironmentChangeEvent event) { for (String key : event.getKeys()) { logger.info("[onApplicationEvent][key({}) 最新 value 为 {}]", key, environment.getProperty(key)); } } } ================================================ FILE: labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-auto-refresh/src/main/resources/bootstrap.yaml ================================================ spring: application: name: demo-application cloud: nacos: # Nacos Config 配置项,对应 NacosConfigProperties 配置属性类 config: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 namespace: # 使用的 Nacos 的命名空间,默认为 null group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP name: # 使用的 Nacos 配置集的 dataId,默认为 spring.application.name file-extension: yaml # 使用的 Nacos 配置集的 dataId 的文件拓展名,同时也是 Nacos 配置集的配置格式,默认为 properties ================================================ FILE: labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo/pom.xml ================================================ labx-05-spring-cloud-alibaba-nacos-config cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-05-sca-nacos-config-demo 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-config ================================================ FILE: labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo/src/main/java/cn/iocoder/springcloudalibaba/labx5/nacosdemo/DemoApplication.java ================================================ package cn.iocoder.springcloudalibaba.labx5.nacosdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class); } } ================================================ FILE: labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo/src/main/java/cn/iocoder/springcloudalibaba/labx5/nacosdemo/config/OrderProperties.java ================================================ package cn.iocoder.springcloudalibaba.labx5.nacosdemo.config; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component //@NacosConfigurationProperties(prefix = "order", dataId = "${nacos.config.data-id}", type = ConfigType.YAML) @ConfigurationProperties(prefix = "order") public class OrderProperties { /** * 订单支付超时时长,单位:秒。 */ private Integer payTimeoutSeconds; /** * 订单创建频率,单位:秒 */ private Integer createFrequencySeconds; // /** // * 配置描述 // */ // private String desc; public Integer getPayTimeoutSeconds() { return payTimeoutSeconds; } public OrderProperties setPayTimeoutSeconds(Integer payTimeoutSeconds) { this.payTimeoutSeconds = payTimeoutSeconds; return this; } public Integer getCreateFrequencySeconds() { return createFrequencySeconds; } public OrderProperties setCreateFrequencySeconds(Integer createFrequencySeconds) { this.createFrequencySeconds = createFrequencySeconds; return this; } // public String getDesc() { // return desc; // } // // public OrderProperties setDesc(String desc) { // this.desc = desc; // return this; // } } ================================================ FILE: labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo/src/main/java/cn/iocoder/springcloudalibaba/labx5/nacosdemo/controller/DemoController.java ================================================ package cn.iocoder.springcloudalibaba.labx5.nacosdemo.controller; import cn.iocoder.springcloudalibaba.labx5.nacosdemo.config.OrderProperties; import com.alibaba.fastjson.JSONObject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Map; @RestController @RequestMapping("/demo") public class DemoController { @Autowired private OrderProperties orderProperties; /** * 测试 @ConfigurationProperties 注解的配置属性类 */ @GetMapping("/test01") public OrderProperties test01() { return orderProperties; } @Value(value = "${order.pay-timeout-seconds}") // @NacosValue(value = "${order.pay-timeout-seconds}") private Integer payTimeoutSeconds; @Value(value = "${order.create-frequency-seconds}") // @NacosValue(value = "${order.create-frequency-seconds}") private Integer createFrequencySeconds; /** * 测试 @Value 注解的属性 */ @GetMapping("/test02") public Map test02() { return new JSONObject().fluentPut("payTimeoutSeconds", payTimeoutSeconds) .fluentPut("createFrequencySeconds", createFrequencySeconds); } } ================================================ FILE: labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo/src/main/resources/bootstrap.yaml ================================================ spring: application: name: demo-application cloud: nacos: # Nacos Config 配置项,对应 NacosConfigProperties 配置属性类 config: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 namespace: # 使用的 Nacos 的命名空间,默认为 null group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP name: # 使用的 Nacos 配置集的 dataId,默认为 spring.application.name file-extension: yaml # 使用的 Nacos 配置集的 dataId 的文件拓展名,同时也是 Nacos 配置集的配置格式,默认为 properties ================================================ FILE: labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo-actuator/pom.xml ================================================ labx-05-spring-cloud-alibaba-nacos-config cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-05-sca-nacos-config-demo-actuator 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-config org.springframework.boot spring-boot-starter-actuator ================================================ FILE: labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo-actuator/src/main/java/cn/iocoder/springcloudalibaba/labx5/nacosdemo/DemoApplication.java ================================================ package cn.iocoder.springcloudalibaba.labx5.nacosdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx5.nacosdemo.config; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component //@NacosConfigurationProperties(prefix = "order", dataId = "${nacos.config.data-id}", type = ConfigType.YAML) @ConfigurationProperties(prefix = "order") public class OrderProperties { /** * 订单支付超时时长,单位:秒。 */ private Integer payTimeoutSeconds; /** * 订单创建频率,单位:秒 */ private Integer createFrequencySeconds; // /** // * 配置描述 // */ // private String desc; public Integer getPayTimeoutSeconds() { return payTimeoutSeconds; } public OrderProperties setPayTimeoutSeconds(Integer payTimeoutSeconds) { this.payTimeoutSeconds = payTimeoutSeconds; return this; } public Integer getCreateFrequencySeconds() { return createFrequencySeconds; } public OrderProperties setCreateFrequencySeconds(Integer createFrequencySeconds) { this.createFrequencySeconds = createFrequencySeconds; return this; } // public String getDesc() { // return desc; // } // // public OrderProperties setDesc(String desc) { // this.desc = desc; // return this; // } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx5.nacosdemo.controller; import cn.iocoder.springcloudalibaba.labx5.nacosdemo.config.OrderProperties; import com.alibaba.fastjson.JSONObject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Map; @RestController @RequestMapping("/demo") public class DemoController { @Autowired private OrderProperties orderProperties; /** * 测试 @ConfigurationProperties 注解的配置属性类 */ @GetMapping("/test01") public OrderProperties test01() { return orderProperties; } @Value(value = "${order.pay-timeout-seconds}") // @NacosValue(value = "${order.pay-timeout-seconds}") private Integer payTimeoutSeconds; @Value(value = "${order.create-frequency-seconds}") // @NacosValue(value = "${order.create-frequency-seconds}") private Integer createFrequencySeconds; /** * 测试 @Value 注解的属性 */ @GetMapping("/test02") public Map test02() { return new JSONObject().fluentPut("payTimeoutSeconds", payTimeoutSeconds) .fluentPut("createFrequencySeconds", createFrequencySeconds); } } ================================================ FILE: labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo-actuator/src/main/resources/application.yaml ================================================ management: endpoint: # Health 端点配置项,对应 HealthProperties 配置类 health: show-details: ALWAYS # 何时显示完整的健康信息。默认为 NEVER 都不展示。可选 WHEN_AUTHORIZED 当经过授权的用户;可选 ALWAYS 总是展示。 endpoints: # Actuator HTTP 配置项,对应 WebEndpointProperties 配置类 web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 ================================================ FILE: labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo-actuator/src/main/resources/bootstrap.yaml ================================================ spring: application: name: demo-application cloud: nacos: # Nacos Config 配置项,对应 NacosConfigProperties 配置属性类 config: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 namespace: # 使用的 Nacos 的命名空间,默认为 null group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP name: # 使用的 Nacos 配置集的 dataId,默认为 spring.application.name file-extension: yaml # 使用的 Nacos 配置集的 dataId 的文件拓展名,同时也是 Nacos 配置集的配置格式,默认为 properties ================================================ FILE: labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo-jasypt/pom.xml ================================================ labx-05-spring-cloud-alibaba-nacos-config cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-05-sca-nacos-config-demo-jasypt 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-config com.github.ulisesbocchio jasypt-spring-boot-starter 3.0.2 org.springframework.boot spring-boot-starter-test test ================================================ FILE: labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo-jasypt/src/main/java/cn/iocoder/springcloudalibaba/labx5/nacosdemo/DemoApplication.java ================================================ package cn.iocoder.springcloudalibaba.labx5.nacosdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx5.nacosdemo.controller; import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") @RefreshScope public class DemoController { @Value("${xxx-password:}") private String xxxPassword; @GetMapping("/test") public String test() { return xxxPassword; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx5.nacosdemo.listener; import org.apache.commons.lang3.StringUtils; import org.jasypt.encryption.StringEncryptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.context.environment.EnvironmentChangeEvent; import org.springframework.cloud.context.environment.EnvironmentManager; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; @Component public class JasyptEnvironmentChangeListener implements ApplicationListener { private Logger logger = LoggerFactory.getLogger(getClass()); // Environment 管理器,可以实现配置项的获取和修改 @Autowired private EnvironmentManager environmentManager; // Jasypt 加密器,可以对配置项进行加密和加密 @Autowired private StringEncryptor encryptor; @Override public void onApplicationEvent(EnvironmentChangeEvent event) { for (String key : event.getKeys()) { // 获得 value Object valueObj = environmentManager.getProperty(key); if (!(valueObj instanceof String)) { continue; } String value = (String) valueObj; // 判断 value 是否为加密。如果是,则进行解密 if (value.startsWith("ENC(") && value.endsWith(")")) { value = encryptor.decrypt(StringUtils.substringBetween(value, "ENC(", ")")); logger.info("[onApplicationEvent][key({}) 解密后为 {}]", key, value); // 设置到 Environment 中 environmentManager.setProperty(key, value); } } } } ================================================ FILE: labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo-jasypt/src/main/resources/bootstrap.yaml ================================================ spring: application: name: demo-application-jasypt cloud: nacos: # Nacos Config 配置项,对应 NacosConfigProperties 配置属性类 config: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 namespace: # 使用的 Nacos 的命名空间,默认为 null group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP name: # 使用的 Nacos 配置集的 dataId,默认为 spring.application.name file-extension: yaml # 使用的 Nacos 配置集的 dataId 的文件拓展名,同时也是 Nacos 配置集的配置格式,默认为 properties ================================================ FILE: labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo-jasypt/src/test/java/cn/iocoder/springcloudalibaba/labx5/nacosdemo/JasyptTest.java ================================================ package cn.iocoder.springcloudalibaba.labx5.nacosdemo; import org.jasypt.encryption.StringEncryptor; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest public class JasyptTest { @Autowired private StringEncryptor encryptor; @Test public void encode() { // 第一个加密 String password = "woshimima"; System.out.println(encryptor.encrypt(password)); // 第二个加密 password = "bushimima"; System.out.println(encryptor.encrypt(password)); } @Value("${xxx-password:}") private String xxxPassword; @Test public void print() { System.out.println(xxxPassword); } // @Value("${jasypt.encryptor.password}") // private String password; } ================================================ FILE: labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo-multi/pom.xml ================================================ labx-05-spring-cloud-alibaba-nacos-config cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-05-sca-nacos-config-demo-multi 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-config ================================================ FILE: labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo-multi/src/main/java/cn/iocoder/springcloudalibaba/labx5/nacosdemo/DemoApplication.java ================================================ package cn.iocoder.springcloudalibaba.labx5.nacosdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.env.Environment; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { // 启动 Spring Boot 应用 ConfigurableApplicationContext context = SpringApplication.run(DemoApplication.class, args); // 查看 Environment Environment environment = context.getEnvironment(); System.out.println(environment); } } ================================================ FILE: labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo-multi/src/main/resources/bootstrap.yaml ================================================ spring: application: name: demo-application cloud: nacos: # Nacos Config 配置项,对应 NacosConfigProperties 配置属性类 config: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 namespace: # 使用的 Nacos 的命名空间,默认为 null group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP name: ${spring.application.name} # 使用的 Nacos 配置集的 dataId,默认为 spring.application.name file-extension: yaml # 使用的 Nacos 配置集的 dataId 的文件拓展名,同时也是 Nacos 配置集的配置格式,默认为 properties # 拓展配置集数组,对应 Config 数组 extension-configs: - data-id: extension-dataId-01.yaml group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP refresh: true # 是否自动刷新配置,默认为 false - data-id: extension-dataId-02.yaml group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP refresh: true # 是否自动刷新配置,默认为 false # 共享配置集数组,对应 Config 数组 shared-configs: - data-id: shared-dataId-01.yaml group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP refresh: true # 是否自动刷新配置,默认为 false - data-id: shared-dataId-02.yaml group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP refresh: true # 是否自动刷新配置,默认为 false profiles: active: dev # 设置开启的 Profiles ================================================ FILE: labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo-profiles/pom.xml ================================================ labx-05-spring-cloud-alibaba-nacos-config cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-05-sca-nacos-config-demo-profiles 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-config ================================================ FILE: labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo-profiles/src/main/java/cn/iocoder/springcloudalibaba/labx5/nacosdemo/ProfilesApplication.java ================================================ package cn.iocoder.springcloudalibaba.labx5.nacosdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ProfilesApplication { public static void main(String[] args) { SpringApplication.run(ProfilesApplication.class); } } ================================================ FILE: labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo-profiles/src/main/resources/bootstrap-dev.yaml ================================================ spring: cloud: nacos: # Nacos Config 配置项,对应 NacosConfigProperties 配置属性类 config: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 namespace: 14226a0d-799f-424d-8905-162f6a8bf409 # 使用的 Nacos 的命名空间,默认为 null group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP name: # 使用的 Nacos 配置集的 dataId,默认为 spring.application.name file-extension: yaml # 使用的 Nacos 配置集的 dataId 的文件拓展名,同时也是 Nacos 配置集的配置格式,默认为 properties ================================================ FILE: labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo-profiles/src/main/resources/bootstrap-prod.yaml ================================================ spring: cloud: nacos: # Nacos Config 配置项,对应 NacosConfigProperties 配置属性类 config: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 namespace: f1686f3b-a984-4cdf-8298-7caee3455d14 # 使用的 Nacos 的命名空间,默认为 null group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP name: # 使用的 Nacos 配置集的 dataId,默认为 spring.application.name file-extension: yaml # 使用的 Nacos 配置集的 dataId 的文件拓展名,同时也是 Nacos 配置集的配置格式,默认为 properties ================================================ FILE: labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo-profiles/src/main/resources/bootstrap.yaml ================================================ spring: application: name: demo-application ================================================ FILE: labx-05-spring-cloud-alibaba-nacos-config/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-05-spring-cloud-alibaba-nacos-config pom labx-05-sca-nacos-config-demo labx-05-sca-nacos-config-demo-profiles labx-05-sca-nacos-config-auto-refresh labx-05-sca-nacos-config-demo-jasypt labx-05-sca-nacos-config-demo-actuator labx-05-sca-nacos-config-demo-multi ================================================ FILE: labx-05-spring-cloud-alibaba-nacos-config/《芋道 Spring Cloud Alibaba 配置中心 Nacos 入门》.md ================================================ ================================================ FILE: labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-actuator/pom.xml ================================================ labx-06-spring-cloud-stream-rocketmq cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-06-sca-stream-rocketmq-consumer-actuator 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-stream-rocketmq org.springframework.boot spring-boot-starter-actuator ================================================ FILE: labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-actuator/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/consumerdemo/ConsumerApplication.java ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo; import cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener.MySink; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySink.class) public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener; import cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.message.Demo01Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.messaging.handler.annotation.Payload; import org.springframework.stereotype.Component; @Component public class Demo01Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @StreamListener(MySink.DEMO01_INPUT) public void onMessage(@Payload Demo01Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener; import org.springframework.cloud.stream.annotation.Input; import org.springframework.messaging.SubscribableChannel; public interface MySink { String DEMO01_INPUT = "demo01-input"; @Input(DEMO01_INPUT) SubscribableChannel demo01Input(); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-actuator/src/main/resources/application.yml ================================================ spring: application: name: demo-consumer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binding 配置项,对应 BindingProperties Map bindings: demo01-input: destination: DEMO-TOPIC-01 # 目的地。这里使用 RocketMQ Topic content-type: application/json # 内容格式。这里使用 JSON group: demo01-consumer-group-DEMO-TOPIC-01 # 消费者分组 # Spring Cloud Stream RocketMQ 配置项 rocketmq: # RocketMQ Binder 配置项,对应 RocketMQBinderConfigurationProperties 类 binder: name-server: 127.0.0.1:9876 # RocketMQ Namesrv 地址 # RocketMQ 自定义 Binding 配置项,对应 RocketMQBindingProperties Map bindings: demo01-input: # RocketMQ Consumer 配置项,对应 RocketMQConsumerProperties 类 consumer: enabled: true # 是否开启消费,默认为 true broadcasting: false # 是否使用广播消费,默认为 false 使用集群消费 server: port: ${random.int[10000,19999]} # 随机端口,方便启动多个消费者 management: endpoints: web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 endpoint: # Health 端点配置项,对应 HealthProperties 配置类 health: enabled: true # 是否开启。默认为 true 开启。 show-details: ALWAYS # 何时显示完整的健康信息。默认为 NEVER 都不展示。可选 WHEN_AUTHORIZED 当经过授权的用户;可选 ALWAYS 总是展示。 ================================================ FILE: labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-aliyun/pom.xml ================================================ labx-06-spring-cloud-stream-rocketmq cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-06-sca-stream-rocketmq-consumer-aliyun 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-stream-rocketmq ================================================ FILE: labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-aliyun/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/consumerdemo/ConsumerApplication.java ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo; import cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener.MySink; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySink.class) public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener; import cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.message.Demo01Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.messaging.handler.annotation.Payload; import org.springframework.stereotype.Component; @Component public class Demo01Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @StreamListener(MySink.DEMO01_INPUT) public void onMessage(@Payload Demo01Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener; import org.springframework.cloud.stream.annotation.Input; import org.springframework.messaging.SubscribableChannel; public interface MySink { String DEMO01_INPUT = "demo01-input"; @Input(DEMO01_INPUT) SubscribableChannel demo01Input(); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-aliyun/src/main/resources/application.yml ================================================ spring: application: name: demo-consumer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binding 配置项,对应 BindingProperties Map bindings: demo01-input: destination: TOPIC_YUNAI_TEST # 目的地。这里使用 RocketMQ Topic content-type: application/json # 内容格式。这里使用 JSON group: GID_PRODUCER_GROUP_YUNAI_TEST # 消费者分组 # Spring Cloud Stream RocketMQ 配置项 rocketmq: # RocketMQ Binder 配置项,对应 RocketMQBinderConfigurationProperties 类 binder: name-server: onsaddr.mq-internet-access.mq-internet.aliyuncs.com:80 # RocketMQ Namesrv 地址 access-key: ${ALIYUN_ACCESS_KEY} # 阿里云账号 AccessKey secret-key: ${ALIYUN_SECRET_KEY} # 阿里云账号 SecretKey # RocketMQ 自定义 Binding 配置项,对应 RocketMQBindingProperties Map bindings: demo01-input: # RocketMQ Consumer 配置项,对应 RocketMQConsumerProperties 类 consumer: enabled: true # 是否开启消费,默认为 true broadcasting: false # 是否使用广播消费,默认为 false 使用集群消费 server: port: ${random.int[10000,19999]} # 随机端口,方便启动多个消费者 ================================================ FILE: labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-broadcasting/pom.xml ================================================ labx-06-spring-cloud-stream-rocketmq cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-06-sca-stream-rocketmq-consumer-broadcasting 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-stream-rocketmq ================================================ FILE: labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-broadcasting/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/consumerdemo/ConsumerApplication.java ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo; import cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener.MySink; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySink.class) public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener; import cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.message.Demo01Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.messaging.handler.annotation.Payload; import org.springframework.stereotype.Component; @Component public class Demo01Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @StreamListener(MySink.DEMO01_INPUT) public void onMessage(@Payload Demo01Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener; import org.springframework.cloud.stream.annotation.Input; import org.springframework.messaging.SubscribableChannel; public interface MySink { String DEMO01_INPUT = "demo01-input"; @Input(DEMO01_INPUT) SubscribableChannel demo01Input(); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-broadcasting/src/main/resources/application.yml ================================================ spring: application: name: demo-consumer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binding 配置项,对应 BindingProperties Map bindings: demo01-input: destination: DEMO-TOPIC-01 # 目的地。这里使用 RocketMQ Topic content-type: application/json # 内容格式。这里使用 JSON group: demo01-consumer-group-DEMO-TOPIC-01-X # 消费者分组 # Spring Cloud Stream RocketMQ 配置项 rocketmq: # RocketMQ Binder 配置项,对应 RocketMQBinderConfigurationProperties 类 binder: name-server: 127.0.0.1:9876 # RocketMQ Namesrv 地址 # RocketMQ 自定义 Binding 配置项,对应 RocketMQBindingProperties Map bindings: demo01-input: # RocketMQ Consumer 配置项,对应 RocketMQConsumerProperties 类 consumer: enabled: true # 是否开启消费,默认为 true broadcasting: true # 是否使用广播消费,默认为 false 使用集群消费 server: port: ${random.int[10000,19999]} # 随机端口,方便启动多个消费者 ================================================ FILE: labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-demo/pom.xml ================================================ labx-06-spring-cloud-stream-rocketmq cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-06-sca-stream-rocketmq-consumer-demo 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-stream-rocketmq ================================================ FILE: labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-demo/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/consumerdemo/ConsumerApplication.java ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo; import cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener.MySink; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySink.class) public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener; import cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.message.Demo01Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.messaging.handler.annotation.Payload; import org.springframework.stereotype.Component; @Component public class Demo01Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @StreamListener(MySink.DEMO01_INPUT) public void onMessage(@Payload Demo01Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener; import org.springframework.cloud.stream.annotation.Input; import org.springframework.messaging.SubscribableChannel; public interface MySink { String DEMO01_INPUT = "demo01-input"; @Input(DEMO01_INPUT) SubscribableChannel demo01Input(); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-demo/src/main/resources/application.yml ================================================ spring: application: name: demo-consumer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binding 配置项,对应 BindingProperties Map bindings: demo01-input: destination: DEMO-TOPIC-01 # 目的地。这里使用 RocketMQ Topic content-type: application/json # 内容格式。这里使用 JSON group: demo01-consumer-group-DEMO-TOPIC-01 # 消费者分组 # Spring Cloud Stream RocketMQ 配置项 rocketmq: # RocketMQ Binder 配置项,对应 RocketMQBinderConfigurationProperties 类 binder: name-server: 127.0.0.1:9876 # RocketMQ Namesrv 地址 # RocketMQ 自定义 Binding 配置项,对应 RocketMQBindingProperties Map bindings: demo01-input: # RocketMQ Consumer 配置项,对应 RocketMQConsumerProperties 类 consumer: enabled: true # 是否开启消费,默认为 true broadcasting: false # 是否使用广播消费,默认为 false 使用集群消费 server: port: ${random.int[10000,19999]} # 随机端口,方便启动多个消费者 ================================================ FILE: labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-error-handler/pom.xml ================================================ labx-06-spring-cloud-stream-rocketmq cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-06-sca-stream-rocketmq-consumer-error-handler 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-stream-rocketmq ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo; import cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener.MySink; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySink.class) public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener; import cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.message.Demo01Message; import org.apache.commons.lang3.exception.ExceptionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.integration.annotation.ServiceActivator; import org.springframework.integration.context.IntegrationContextUtils; import org.springframework.messaging.handler.annotation.Payload; import org.springframework.messaging.support.ErrorMessage; import org.springframework.stereotype.Component; @Component public class Demo01Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @StreamListener(MySink.DEMO01_INPUT) // 对应 DEMO-TOPIC-01.demo01-consumer-group-DEMO-TOPIC-01 public void onMessage(@Payload Demo01Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); // 注意,此处抛出一个 RuntimeException 异常,模拟消费失败 throw new RuntimeException("我就是故意抛出一个异常"); } @ServiceActivator(inputChannel = "DEMO-TOPIC-01.demo01-consumer-group-DEMO-TOPIC-01.errors") public void handleError(ErrorMessage errorMessage) { logger.error("[handleError][payload:{}]", ExceptionUtils.getRootCauseMessage(errorMessage.getPayload())); logger.error("[handleError][originalMessage:{}]", errorMessage.getOriginalMessage()); logger.error("[handleError][headers:{}]", errorMessage.getHeaders()); } @StreamListener(IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME) // errorChannel public void globalHandleError(ErrorMessage errorMessage) { logger.error("[globalHandleError][payload:{}]", ExceptionUtils.getRootCauseMessage(errorMessage.getPayload())); logger.error("[globalHandleError][originalMessage:{}]", errorMessage.getOriginalMessage()); logger.error("[globalHandleError][headers:{}]", errorMessage.getHeaders()); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener; import org.springframework.cloud.stream.annotation.Input; import org.springframework.messaging.SubscribableChannel; public interface MySink { String DEMO01_INPUT = "demo01-input"; @Input(DEMO01_INPUT) SubscribableChannel demo01Input(); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-error-handler/src/main/resources/application.yml ================================================ spring: application: name: demo-consumer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binding 配置项,对应 BindingProperties Map bindings: demo01-input: destination: DEMO-TOPIC-01 # 目的地。这里使用 RocketMQ Topic content-type: application/json # 内容格式。这里使用 JSON group: demo01-consumer-group-DEMO-TOPIC-01 # 消费者分组 # Consumer 配置项,对应 ConsumerProperties 类 consumer: max-attempts: 1 # Spring Cloud Stream RocketMQ 配置项 rocketmq: # RocketMQ Binder 配置项,对应 RocketMQBinderConfigurationProperties 类 binder: name-server: 127.0.0.1:9876 # RocketMQ Namesrv 地址 # RocketMQ 自定义 Binding 配置项,对应 RocketMQBindingProperties Map bindings: demo01-input: # RocketMQ Consumer 配置项,对应 RocketMQConsumerProperties 类 consumer: enabled: true # 是否开启消费,默认为 true broadcasting: false # 是否使用广播消费,默认为 false 使用集群消费 delay-level-when-next-consume: 0 # 异步消费消息模式下消费失败重试策略,默认为 0 server: port: ${random.int[10000,19999]} # 随机端口,方便启动多个消费者 ================================================ FILE: labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-filter/pom.xml ================================================ labx-06-spring-cloud-stream-rocketmq cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-06-sca-stream-rocketmq-consumer-filter 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-stream-rocketmq ================================================ FILE: labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-filter/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/consumerdemo/ConsumerApplication.java ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo; import cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener.MySink; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySink.class) public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener; import cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.message.Demo01Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.messaging.handler.annotation.Payload; import org.springframework.stereotype.Component; @Component public class Demo01Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @StreamListener(MySink.DEMO01_INPUT) public void onMessage(@Payload Demo01Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } // @StreamListener(value = MySink.DEMO01_INPUT, condition = "headers['rocketmq_TAGS'] == 'yunai'") // public void onMessage(@Payload Demo01Message message) { // logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); // } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener; import org.springframework.cloud.stream.annotation.Input; import org.springframework.messaging.SubscribableChannel; public interface MySink { String DEMO01_INPUT = "demo01-input"; @Input(DEMO01_INPUT) SubscribableChannel demo01Input(); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-filter/src/main/resources/application.yml ================================================ spring: application: name: demo-consumer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binding 配置项,对应 BindingProperties Map bindings: demo01-input: destination: DEMO-TOPIC-01 # 目的地。这里使用 RocketMQ Topic content-type: application/json # 内容格式。这里使用 JSON group: demo01-consumer-group-DEMO-TOPIC-01 # 消费者分组 # Spring Cloud Stream RocketMQ 配置项 rocketmq: # RocketMQ Binder 配置项,对应 RocketMQBinderConfigurationProperties 类 binder: name-server: 127.0.0.1:9876 # RocketMQ Namesrv 地址 # RocketMQ 自定义 Binding 配置项,对应 RocketMQBindingProperties Map bindings: demo01-input: # RocketMQ Consumer 配置项,对应 RocketMQConsumerProperties 类 consumer: enabled: true # 是否开启消费,默认为 true broadcasting: false # 是否使用广播消费,默认为 false 使用集群消费 tags: yunai || yutou # 基于 Tag 订阅,多个 Tag 使用 || 分隔,默认为空 sql: # 基于 SQL 订阅,默认为空 server: port: ${random.int[10000,19999]} # 随机端口,方便启动多个消费者 ================================================ FILE: labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-orderly/pom.xml ================================================ labx-06-spring-cloud-stream-rocketmq cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-06-sca-stream-rocketmq-consumer-orderly 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-stream-rocketmq ================================================ FILE: labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-orderly/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/consumerdemo/ConsumerApplication.java ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo; import cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener.MySink; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySink.class) public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.messaging.Message; import org.springframework.stereotype.Component; @Component public class Demo01Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @StreamListener(MySink.DEMO01_INPUT) public void onMessage(Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener; import org.springframework.cloud.stream.annotation.Input; import org.springframework.messaging.SubscribableChannel; public interface MySink { String DEMO01_INPUT = "demo01-input"; @Input(DEMO01_INPUT) SubscribableChannel demo01Input(); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-orderly/src/main/resources/application.yml ================================================ spring: application: name: demo-consumer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binding 配置项,对应 BindingProperties Map bindings: demo01-input: destination: DEMO-TOPIC-01 # 目的地。这里使用 RocketMQ Topic content-type: application/json # 内容格式。这里使用 JSON group: demo01-consumer-group-DEMO-TOPIC-01 # 消费者分组 # Spring Cloud Stream RocketMQ 配置项 rocketmq: # RocketMQ Binder 配置项,对应 RocketMQBinderConfigurationProperties 类 binder: name-server: 127.0.0.1:9876 # RocketMQ Namesrv 地址 # RocketMQ 自定义 Binding 配置项,对应 RocketMQBindingProperties Map bindings: demo01-input: # RocketMQ Consumer 配置项,对应 RocketMQConsumerProperties 类 consumer: enabled: true # 是否开启消费,默认为 true broadcasting: false # 是否使用广播消费,默认为 false 使用集群消费 orderly: true # 是否顺序消费,默认为 false 并发消费。 server: port: ${random.int[10000,19999]} # 随机端口,方便启动多个消费者 ================================================ FILE: labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-retry/pom.xml ================================================ labx-06-spring-cloud-stream-rocketmq cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-06-sca-stream-rocketmq-consumer-retry 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-stream-rocketmq ================================================ FILE: labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-retry/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/consumerdemo/ConsumerApplication.java ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo; import cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener.MySink; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySink.class) public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener; import cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.message.Demo01Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.messaging.handler.annotation.Payload; import org.springframework.stereotype.Component; @Component public class Demo01Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @StreamListener(MySink.DEMO01_INPUT) public void onMessage(@Payload Demo01Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); // 注意,此处抛出一个 RuntimeException 异常,模拟消费失败 throw new RuntimeException("我就是故意抛出一个异常"); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener; import org.springframework.cloud.stream.annotation.Input; import org.springframework.messaging.SubscribableChannel; public interface MySink { String DEMO01_INPUT = "demo01-input"; @Input(DEMO01_INPUT) SubscribableChannel demo01Input(); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-retry/src/main/resources/application.yml ================================================ spring: application: name: demo-consumer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binding 配置项,对应 BindingProperties Map bindings: demo01-input: destination: DEMO-TOPIC-01 # 目的地。这里使用 RocketMQ Topic content-type: application/json # 内容格式。这里使用 JSON group: demo01-consumer-group-DEMO-TOPIC-01 # 消费者分组 # Consumer 配置项,对应 ConsumerProperties 类 consumer: max-attempts: 1 # Spring Cloud Stream RocketMQ 配置项 rocketmq: # RocketMQ Binder 配置项,对应 RocketMQBinderConfigurationProperties 类 binder: name-server: 127.0.0.1:9876 # RocketMQ Namesrv 地址 # RocketMQ 自定义 Binding 配置项,对应 RocketMQBindingProperties Map bindings: demo01-input: # RocketMQ Consumer 配置项,对应 RocketMQConsumerProperties 类 consumer: enabled: true # 是否开启消费,默认为 true broadcasting: false # 是否使用广播消费,默认为 false 使用集群消费 delay-level-when-next-consume: 0 # 异步消费消息模式下消费失败重试策略,默认为 0 server: port: ${random.int[10000,19999]} # 随机端口,方便启动多个消费者 ================================================ FILE: labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-actuator/pom.xml ================================================ labx-06-spring-cloud-stream-rocketmq cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-06-sca-stream-rocketmq-producer-actuator 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-stream-rocketmq org.springframework.boot spring-boot-starter-actuator ================================================ FILE: labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-actuator/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/producerdemo/ProducerApplication.java ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo; import cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message.MySource; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySource.class) public class ProducerApplication { public static void main(String[] args) { SpringApplication.run(ProducerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.controller; import cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message.Demo01Message; import cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message.MySource; import org.apache.rocketmq.common.message.MessageConst; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.Message; import org.springframework.messaging.support.MessageBuilder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Random; @RestController @RequestMapping("/demo01") public class Demo01Controller { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private MySource mySource; @GetMapping("/send") public boolean send() { // 创建 Message Demo01Message message = new Demo01Message() .setId(new Random().nextInt()); // 创建 Spring Message 对象 Message springMessage = MessageBuilder.withPayload(message) .build(); // 发送消息 return mySource.demo01Output().send(springMessage); } @GetMapping("/send_delay") public boolean sendDelay() { // 创建 Message Demo01Message message = new Demo01Message() .setId(new Random().nextInt()); // 创建 Spring Message 对象 Message springMessage = MessageBuilder.withPayload(message) .setHeader(MessageConst.PROPERTY_DELAY_TIME_LEVEL, "3") // 设置延迟级别为 3,10 秒后消费。 .build(); // 发送消息 boolean sendResult = mySource.demo01Output().send(springMessage); logger.info("[sendDelay][发送消息完成, 结果 = {}]", sendResult); return sendResult; } @GetMapping("/send_tag") public boolean sendTag() { for (String tag : new String[]{"yunai", "yutou", "tudou"}) { // 创建 Message Demo01Message message = new Demo01Message() .setId(new Random().nextInt()); // 创建 Spring Message 对象 Message springMessage = MessageBuilder.withPayload(message) .setHeader(MessageConst.PROPERTY_TAGS, tag) // 设置 Tag .build(); // 发送消息 mySource.demo01Output().send(springMessage); } return true; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message; import org.springframework.cloud.stream.annotation.Output; import org.springframework.messaging.MessageChannel; public interface MySource { @Output("demo01-output") MessageChannel demo01Output(); } ================================================ FILE: labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-actuator/src/main/resources/application.yml ================================================ spring: application: name: demo-producer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binding 配置项,对应 BindingProperties Map bindings: demo01-output: destination: DEMO-TOPIC-01 # 目的地。这里使用 RocketMQ Topic content-type: application/json # 内容格式。这里使用 JSON # Spring Cloud Stream RocketMQ 配置项 rocketmq: # RocketMQ Binder 配置项,对应 RocketMQBinderConfigurationProperties 类 binder: name-server: 127.0.0.1:9876 # RocketMQ Namesrv 地址 # RocketMQ 自定义 Binding 配置项,对应 RocketMQBindingProperties Map bindings: demo01-output: # RocketMQ Producer 配置项,对应 RocketMQProducerProperties 类 producer: group: test # 生产者分组 sync: true # 是否同步发送消息,默认为 false 异步。 server: port: 18080 management: endpoints: web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 endpoint: # Health 端点配置项,对应 HealthProperties 配置类 health: enabled: true # 是否开启。默认为 true 开启。 show-details: ALWAYS # 何时显示完整的健康信息。默认为 NEVER 都不展示。可选 WHEN_AUTHORIZED 当经过授权的用户;可选 ALWAYS 总是展示。 ================================================ FILE: labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-aliyun/pom.xml ================================================ labx-06-spring-cloud-stream-rocketmq cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-06-sca-stream-rocketmq-producer-aliyun 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-stream-rocketmq ================================================ FILE: labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-aliyun/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/producerdemo/ProducerApplication.java ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo; import cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message.MySource; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySource.class) public class ProducerApplication { public static void main(String[] args) { SpringApplication.run(ProducerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.controller; import cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message.Demo01Message; import cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message.MySource; import org.apache.rocketmq.common.message.MessageConst; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.Message; import org.springframework.messaging.support.MessageBuilder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Random; @RestController @RequestMapping("/demo01") public class Demo01Controller { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private MySource mySource; @GetMapping("/send") public boolean send() { // 创建 Message Demo01Message message = new Demo01Message() .setId(new Random().nextInt()); // 创建 Spring Message 对象 Message springMessage = MessageBuilder.withPayload(message) .build(); // 发送消息 return mySource.demo01Output().send(springMessage); } @GetMapping("/send_delay") public boolean sendDelay() { // 创建 Message Demo01Message message = new Demo01Message() .setId(new Random().nextInt()); // 创建 Spring Message 对象 Message springMessage = MessageBuilder.withPayload(message) .setHeader(MessageConst.PROPERTY_DELAY_TIME_LEVEL, "3") // 设置延迟级别为 3,10 秒后消费。 .build(); // 发送消息 boolean sendResult = mySource.demo01Output().send(springMessage); logger.info("[sendDelay][发送消息完成, 结果 = {}]", sendResult); return sendResult; } @GetMapping("/send_tag") public boolean sendTag() { for (String tag : new String[]{"yunai", "yutou", "tudou"}) { // 创建 Message Demo01Message message = new Demo01Message() .setId(new Random().nextInt()); // 创建 Spring Message 对象 Message springMessage = MessageBuilder.withPayload(message) .setHeader(MessageConst.PROPERTY_TAGS, tag) // 设置 Tag .build(); // 发送消息 mySource.demo01Output().send(springMessage); } return true; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message; import org.springframework.cloud.stream.annotation.Output; import org.springframework.messaging.MessageChannel; public interface MySource { @Output("demo01-output") MessageChannel demo01Output(); } ================================================ FILE: labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-aliyun/src/main/resources/application.yml ================================================ spring: application: name: demo-producer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binding 配置项,对应 BindingProperties Map bindings: demo01-output: destination: TOPIC_YUNAI_TEST # 目的地。这里使用 RocketMQ Topic content-type: application/json # 内容格式。这里使用 JSON # Spring Cloud Stream RocketMQ 配置项 rocketmq: # RocketMQ Binder 配置项,对应 RocketMQBinderConfigurationProperties 类 binder: name-server: onsaddr.mq-internet-access.mq-internet.aliyuncs.com:80 # RocketMQ Namesrv 地址 access-key: ${ALIYUN_ACCESS_KEY} # 阿里云账号 AccessKey secret-key: ${ALIYUN_SECRET_KEY} # 阿里云账号 SecretKey # RocketMQ 自定义 Binding 配置项,对应 RocketMQBindingProperties Map bindings: demo01-output: # RocketMQ Producer 配置项,对应 RocketMQProducerProperties 类 producer: group: GID_PRODUCER_GROUP_YUNAI_TEST # 生产者分组 sync: true # 是否同步发送消息,默认为 false 异步。 server: port: 18080 ================================================ FILE: labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-demo/pom.xml ================================================ labx-06-spring-cloud-stream-rocketmq cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-06-sca-stream-rocketmq-producer-demo 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-stream-rocketmq ================================================ FILE: labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-demo/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/producerdemo/ProducerApplication.java ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo; import cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message.MySource; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySource.class) public class ProducerApplication { public static void main(String[] args) { SpringApplication.run(ProducerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.controller; import cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message.Demo01Message; import cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message.MySource; import org.apache.rocketmq.common.message.MessageConst; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.Message; import org.springframework.messaging.support.MessageBuilder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Random; @RestController @RequestMapping("/demo01") public class Demo01Controller { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private MySource mySource; @GetMapping("/send") public boolean send() { // 创建 Message Demo01Message message = new Demo01Message() .setId(new Random().nextInt()); // 创建 Spring Message 对象 Message springMessage = MessageBuilder.withPayload(message) .build(); // 发送消息 return mySource.demo01Output().send(springMessage); } @GetMapping("/send_delay") public boolean sendDelay() { // 创建 Message Demo01Message message = new Demo01Message() .setId(new Random().nextInt()); // 创建 Spring Message 对象 Message springMessage = MessageBuilder.withPayload(message) .setHeader(MessageConst.PROPERTY_DELAY_TIME_LEVEL, "3") // 设置延迟级别为 3,10 秒后消费。 .build(); // 发送消息 boolean sendResult = mySource.demo01Output().send(springMessage); logger.info("[sendDelay][发送消息完成, 结果 = {}]", sendResult); return sendResult; } @GetMapping("/send_tag") public boolean sendTag() { for (String tag : new String[]{"yunai", "yutou", "tudou"}) { // 创建 Message Demo01Message message = new Demo01Message() .setId(new Random().nextInt()); // 创建 Spring Message 对象 Message springMessage = MessageBuilder.withPayload(message) .setHeader(MessageConst.PROPERTY_TAGS, tag) // 设置 Tag .build(); // 发送消息 mySource.demo01Output().send(springMessage); } return true; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message; import org.springframework.cloud.stream.annotation.Output; import org.springframework.messaging.MessageChannel; public interface MySource { @Output("demo01-output") MessageChannel demo01Output(); } ================================================ FILE: labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-demo/src/main/resources/application.yml ================================================ spring: application: name: demo-producer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binding 配置项,对应 BindingProperties Map bindings: demo01-output: destination: DEMO-TOPIC-01 # 目的地。这里使用 RocketMQ Topic content-type: application/json # 内容格式。这里使用 JSON # Spring Cloud Stream RocketMQ 配置项 rocketmq: # RocketMQ Binder 配置项,对应 RocketMQBinderConfigurationProperties 类 binder: name-server: 127.0.0.1:9876 # RocketMQ Namesrv 地址 # RocketMQ 自定义 Binding 配置项,对应 RocketMQBindingProperties Map bindings: demo01-output: # RocketMQ Producer 配置项,对应 RocketMQProducerProperties 类 producer: group: test # 生产者分组 sync: true # 是否同步发送消息,默认为 false 异步。 server: port: 18080 ================================================ FILE: labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-orderly/pom.xml ================================================ labx-06-spring-cloud-stream-rocketmq cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-06-sca-stream-rocketmq-producer-orderly 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-stream-rocketmq ================================================ FILE: labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-orderly/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/producerdemo/ProducerApplication.java ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo; import cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message.MySource; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySource.class) public class ProducerApplication { public static void main(String[] args) { SpringApplication.run(ProducerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.controller; import cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message.Demo01Message; import cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message.MySource; import org.apache.rocketmq.common.message.MessageConst; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.Message; import org.springframework.messaging.support.MessageBuilder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Random; @RestController @RequestMapping("/demo01") public class Demo01Controller { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private MySource mySource; @GetMapping("/send") public boolean send() { // 创建 Message Demo01Message message = new Demo01Message() .setId(new Random().nextInt()); // 创建 Spring Message 对象 Message springMessage = MessageBuilder.withPayload(message) .build(); // 发送消息 return mySource.demo01Output().send(springMessage); } @GetMapping("/send_delay") public boolean sendDelay() { // 创建 Message Demo01Message message = new Demo01Message() .setId(new Random().nextInt()); // 创建 Spring Message 对象 Message springMessage = MessageBuilder.withPayload(message) .setHeader(MessageConst.PROPERTY_DELAY_TIME_LEVEL, "3") // 设置延迟级别为 3,10 秒后消费。 .build(); // 发送消息 boolean sendResult = mySource.demo01Output().send(springMessage); logger.info("[sendDelay][发送消息完成, 结果 = {}]", sendResult); return sendResult; } @GetMapping("/send_orderly") public boolean sendOrderly() { // 发送 3 条相同 id 的消息 int id = new Random().nextInt(); for (int i = 0; i < 3; i++) { // 创建 Message Demo01Message message = new Demo01Message().setId(id); // 创建 Spring Message 对象 Message springMessage = MessageBuilder.withPayload(message) .build(); // 发送消息 mySource.demo01Output().send(springMessage); } return true; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message; import org.springframework.cloud.stream.annotation.Output; import org.springframework.messaging.MessageChannel; public interface MySource { @Output("demo01-output") MessageChannel demo01Output(); } ================================================ FILE: labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-orderly/src/main/resources/application.yml ================================================ spring: application: name: demo-producer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binding 配置项,对应 BindingProperties Map bindings: demo01-output: destination: DEMO-TOPIC-01 # 目的地。这里使用 RocketMQ Topic content-type: application/json # 内容格式。这里使用 JSON # Producer 配置项,对应 ProducerProperties 类 producer: partition-key-expression: payload['id'] # 分区 key 表达式。该表达式基于 Spring EL,从消息中获得分区 key。 # Spring Cloud Stream RocketMQ 配置项 rocketmq: # RocketMQ Binder 配置项,对应 RocketMQBinderConfigurationProperties 类 binder: name-server: 127.0.0.1:9876 # RocketMQ Namesrv 地址 # RocketMQ 自定义 Binding 配置项,对应 RocketMQBindingProperties Map bindings: demo01-output: # RocketMQ Producer 配置项,对应 RocketMQProducerProperties 类 producer: group: test # 生产者分组 sync: true # 是否同步发送消息,默认为 false 异步。 server: port: 18080 ================================================ FILE: labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-transaction/pom.xml ================================================ labx-06-spring-cloud-stream-rocketmq cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-06-sca-stream-rocketmq-producer-transaction 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-stream-rocketmq ================================================ FILE: labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-transaction/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/producerdemo/ProducerApplication.java ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo; import cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message.MySource; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySource.class) public class ProducerApplication { public static void main(String[] args) { SpringApplication.run(ProducerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.controller; import cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message.Demo01Message; import cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message.MySource; import com.alibaba.fastjson.JSON; import org.apache.rocketmq.common.message.MessageConst; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.Message; import org.springframework.messaging.support.MessageBuilder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Random; @RestController @RequestMapping("/demo01") public class Demo01Controller { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private MySource mySource; @GetMapping("/send") public boolean send() { // 创建 Message Demo01Message message = new Demo01Message() .setId(new Random().nextInt()); // 创建 Spring Message 对象 Message springMessage = MessageBuilder.withPayload(message) .build(); // 发送消息 return mySource.demo01Output().send(springMessage); } @GetMapping("/send_delay") public boolean sendDelay() { // 创建 Message Demo01Message message = new Demo01Message() .setId(new Random().nextInt()); // 创建 Spring Message 对象 Message springMessage = MessageBuilder.withPayload(message) .setHeader(MessageConst.PROPERTY_DELAY_TIME_LEVEL, "3") // 设置延迟级别为 3,10 秒后消费。 .build(); // 发送消息 boolean sendResult = mySource.demo01Output().send(springMessage); logger.info("[sendDelay][发送消息完成, 结果 = {}]", sendResult); return sendResult; } @GetMapping("/send_tag") public boolean sendTag() { for (String tag : new String[]{"yunai", "yutou", "tudou"}) { // 创建 Message Demo01Message message = new Demo01Message() .setId(new Random().nextInt()); // 创建 Spring Message 对象 Message springMessage = MessageBuilder.withPayload(message) .setHeader(MessageConst.PROPERTY_TAGS, tag) // 设置 Tag .build(); // 发送消息 mySource.demo01Output().send(springMessage); } return true; } @GetMapping("/send_transaction") public boolean sendTransaction() { // 创建 Message Demo01Message message = new Demo01Message() .setId(new Random().nextInt()); // 创建 Spring Message 对象 Args args = new Args().setArgs1(1).setArgs2("2"); Message springMessage = MessageBuilder.withPayload(message) .setHeader("args", JSON.toJSONString(args)) .build(); // 发送消息 return mySource.demo01Output().send(springMessage); } public static class Args { private Integer args1; private String args2; public Integer getArgs1() { return args1; } public Args setArgs1(Integer args1) { this.args1 = args1; return this; } public String getArgs2() { return args2; } public Args setArgs2(String args2) { this.args2 = args2; return this; } @Override public String toString() { return "Args{" + "args1=" + args1 + ", args2='" + args2 + '\'' + '}'; } } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.listener; import cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.controller.Demo01Controller; import com.alibaba.fastjson.JSON; import org.apache.rocketmq.spring.annotation.RocketMQTransactionListener; import org.apache.rocketmq.spring.core.RocketMQLocalTransactionListener; import org.apache.rocketmq.spring.core.RocketMQLocalTransactionState; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.messaging.Message; @RocketMQTransactionListener(txProducerGroup = "test") public class TransactionListenerImpl implements RocketMQLocalTransactionListener { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) { // 从消息 Header 中解析到 args 参数,并使用 JSON 反序列化 Demo01Controller.Args args = JSON.parseObject(msg.getHeaders().get("args", String.class), Demo01Controller.Args.class); // ... local transaction process, return rollback, commit or unknown logger.info("[executeLocalTransaction][执行本地事务,消息:{} args:{}]", msg, args); return RocketMQLocalTransactionState.UNKNOWN; } @Override public RocketMQLocalTransactionState checkLocalTransaction(Message msg) { // ... check transaction status and return rollback, commit or unknown logger.info("[checkLocalTransaction][回查消息:{}]", msg); return RocketMQLocalTransactionState.COMMIT; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message; import org.springframework.cloud.stream.annotation.Output; import org.springframework.messaging.MessageChannel; public interface MySource { @Output("demo01-output") MessageChannel demo01Output(); } ================================================ FILE: labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-transaction/src/main/resources/application.yml ================================================ spring: application: name: demo-producer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binding 配置项,对应 BindingProperties Map bindings: demo01-output: destination: DEMO-TOPIC-01 # 目的地。这里使用 RocketMQ Topic content-type: application/json # 内容格式。这里使用 JSON # Spring Cloud Stream RocketMQ 配置项 rocketmq: # RocketMQ Binder 配置项,对应 RocketMQBinderConfigurationProperties 类 binder: name-server: 127.0.0.1:9876 # RocketMQ Namesrv 地址 # RocketMQ 自定义 Binding 配置项,对应 RocketMQBindingProperties Map bindings: demo01-output: # RocketMQ Producer 配置项,对应 RocketMQProducerProperties 类 producer: group: test # 生产者分组 sync: true # 是否同步发送消息,默认为 false 异步。 transactional: true # 是否发送事务消息,默认为 false。 server: port: 18080 ================================================ FILE: labx-06-spring-cloud-stream-rocketmq/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-06-spring-cloud-stream-rocketmq pom labx-06-sca-stream-rocketmq-consumer-demo labx-06-sca-stream-rocketmq-producer-demo labx-06-sca-stream-rocketmq-consumer-retry labx-06-sca-stream-rocketmq-consumer-error-handler labx-06-sca-stream-rocketmq-consumer-broadcasting labx-06-sca-stream-rocketmq-producer-orderly labx-06-sca-stream-rocketmq-consumer-orderly labx-06-sca-stream-rocketmq-consumer-filter labx-06-sca-stream-rocketmq-producer-transaction labx-06-sca-stream-rocketmq-producer-actuator labx-06-sca-stream-rocketmq-consumer-actuator labx-06-sca-stream-rocketmq-producer-aliyun labx-06-sca-stream-rocketmq-consumer-aliyun ================================================ FILE: labx-06-spring-cloud-stream-rocketmq/《芋道 Spring Cloud Alibaba 消息队列 RocketMQ 入门》.md ================================================ ================================================ FILE: labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo01/labx-07-sca-dubbo-demo01-api/pom.xml ================================================ labx-07-sca-dubbo-demo01 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-07-sca-dubbo-demo01-api ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.api; import cn.iocoder.springcloudalibaba.labx7.dto.UserAddDTO; import cn.iocoder.springcloudalibaba.labx7.dto.UserDTO; /** * 用户服务 RPC Service 接口 */ public interface UserService { /** * 根据指定用户编号,获得用户信息 * * @param id 用户编号 * @return 用户信息 */ UserDTO get(Integer id); /** * 添加新用户,返回新添加的用户编号 * * @param addDTO 添加的用户信息 * @return 用户编号 */ Integer add(UserAddDTO addDTO); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.dto; import java.io.Serializable; /** * 用户添加 DTO */ public class UserAddDTO implements Serializable { /** * 昵称 */ private String name; /** * 性别 */ private Integer gender; public String getName() { return name; } public UserAddDTO setName(String name) { this.name = name; return this; } public Integer getGender() { return gender; } public UserAddDTO setGender(Integer gender) { this.gender = gender; return this; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.dto; import java.io.Serializable; /** * 用户信息 DTO */ public class UserDTO implements Serializable { /** * 用户编号 */ private Integer id; /** * 昵称 */ private String name; /** * 性别 */ private Integer gender; public Integer getId() { return id; } public UserDTO setId(Integer id) { this.id = id; return this; } public String getName() { return name; } public UserDTO setName(String name) { this.name = name; return this; } public Integer getGender() { return gender; } public UserDTO setGender(Integer gender) { this.gender = gender; return this; } } ================================================ FILE: labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo01/labx-07-sca-dubbo-demo01-consumer/pom.xml ================================================ labx-07-sca-dubbo-demo01 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-07-sca-dubbo-demo01-consumer 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import cn.iocoder.springboot.labs labx-07-sca-dubbo-demo01-api 1.0-SNAPSHOT org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery com.alibaba.cloud spring-cloud-starter-dubbo ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.consumerdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.consumerdemo.controller; import cn.iocoder.springcloudalibaba.labx7.api.UserService; import cn.iocoder.springcloudalibaba.labx7.dto.UserAddDTO; import cn.iocoder.springcloudalibaba.labx7.dto.UserDTO; import org.apache.dubbo.config.annotation.Reference; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/user") public class UserController { @Reference(protocol = "dubbo", version = "1.0.0") private UserService userService; @GetMapping("/get") public UserDTO get(@RequestParam("id") Integer id) { return userService.get(id); } @PostMapping("/add") public Integer add(UserAddDTO addDTO) { return userService.add(addDTO); } } ================================================ FILE: labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo01/labx-07-sca-dubbo-demo01-consumer/src/main/resources/application.yaml ================================================ spring: application: name: demo-consumer cloud: # Nacos 作为注册中心的配置项 nacos: discovery: server-addr: 127.0.0.1:8848 # Dubbo 配置项,对应 DubboConfigurationProperties 类 dubbo: # Dubbo 服务注册中心配置,对应 RegistryConfig 类 registry: address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址 # Spring Cloud Alibaba Dubbo 专属配置项,对应 DubboCloudProperties 类 cloud: subscribed-services: demo-provider # 设置订阅的应用列表,默认为 * 订阅所有应用。 ================================================ FILE: labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo01/labx-07-sca-dubbo-demo01-provider/pom.xml ================================================ labx-07-sca-dubbo-demo01 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-07-sca-dubbo-demo01-provider 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import cn.iocoder.springboot.labs labx-07-sca-dubbo-demo01-api 1.0-SNAPSHOT com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery com.alibaba.cloud spring-cloud-starter-dubbo ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.providerdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ProviderApplication { public static void main(String[] args) { SpringApplication.run(ProviderApplication.class); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.providerdemo.service; import cn.iocoder.springcloudalibaba.labx7.api.UserService; import cn.iocoder.springcloudalibaba.labx7.dto.UserAddDTO; import cn.iocoder.springcloudalibaba.labx7.dto.UserDTO; @org.apache.dubbo.config.annotation.Service(protocol = "dubbo", version = "1.0.0") public class UserServiceImpl implements UserService { @Override public UserDTO get(Integer id) { return new UserDTO().setId(id) .setName("没有昵称:" + id) .setGender(id % 2 + 1); // 1 - 男;2 - 女 } @Override public Integer add(UserAddDTO addDTO) { return (int) (System.currentTimeMillis() / 1000); // 嘿嘿,随便返回一个 id } } ================================================ FILE: labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo01/labx-07-sca-dubbo-demo01-provider/src/main/resources/application.yaml ================================================ spring: application: name: demo-provider cloud: # Nacos 作为注册中心的配置项 nacos: discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 # Dubbo 配置项,对应 DubboConfigurationProperties 类 dubbo: scan: base-packages: cn.iocoder.springcloudalibaba.labx7.providerdemo.service # 指定 Dubbo 服务实现类的扫描基准包 # Dubbo 服务暴露的协议配置,对应 ProtocolConfig Map protocols: dubbo: name: dubbo # 协议名称 port: -1 # 协议端口,-1 表示自增端口,从 20880 开始 # Dubbo 服务注册中心配置,对应 RegistryConfig 类 registry: address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址 # Spring Cloud Alibaba Dubbo 专属配置项,对应 DubboCloudProperties 类 cloud: subscribed-services: '' # 设置订阅的应用列表,默认为 * 订阅所有应用。 ================================================ FILE: labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo01/pom.xml ================================================ labx-07-spring-cloud-alibaba-dubbo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-07-sca-dubbo-demo01 pom labx-07-sca-dubbo-demo01-api labx-07-sca-dubbo-demo01-provider labx-07-sca-dubbo-demo01-consumer ================================================ FILE: labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo02/labx-07-sca-dubbo-demo02-api/pom.xml ================================================ labx-07-sca-dubbo-demo02 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-07-sca-dubbo-demo02-api ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.api; import cn.iocoder.springcloudalibaba.labx7.dto.UserAddDTO; import cn.iocoder.springcloudalibaba.labx7.dto.UserDTO; /** * 用户服务 RPC Service 接口 */ public interface UserService { /** * 根据指定用户编号,获得用户信息 * * @param id 用户编号 * @return 用户信息 */ UserDTO get(Integer id); /** * 添加新用户,返回新添加的用户编号 * * @param addDTO 添加的用户信息 * @return 用户编号 */ Integer add(UserAddDTO addDTO); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.dto; import java.io.Serializable; /** * 用户添加 DTO */ public class UserAddDTO implements Serializable { /** * 昵称 */ private String name; /** * 性别 */ private Integer gender; public String getName() { return name; } public UserAddDTO setName(String name) { this.name = name; return this; } public Integer getGender() { return gender; } public UserAddDTO setGender(Integer gender) { this.gender = gender; return this; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.dto; import java.io.Serializable; /** * 用户信息 DTO */ public class UserDTO implements Serializable { /** * 用户编号 */ private Integer id; /** * 昵称 */ private String name; /** * 性别 */ private Integer gender; public Integer getId() { return id; } public UserDTO setId(Integer id) { this.id = id; return this; } public String getName() { return name; } public UserDTO setName(String name) { this.name = name; return this; } public Integer getGender() { return gender; } public UserDTO setGender(Integer gender) { this.gender = gender; return this; } } ================================================ FILE: labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo02/labx-07-sca-dubbo-demo02-consumer/pom.xml ================================================ labx-07-sca-dubbo-demo02 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-07-sca-dubbo-demo02-consumer 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import cn.iocoder.springboot.labs labx-07-sca-dubbo-demo02-api 1.0-SNAPSHOT org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery com.alibaba.cloud spring-cloud-starter-dubbo org.springframework.cloud spring-cloud-starter-openfeign ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.consumerdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication @EnableFeignClients public class FeignConsumerApplication { public static void main(String[] args) { SpringApplication.run(FeignConsumerApplication.class); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.consumerdemo.config; import com.alibaba.cloud.dubbo.annotation.DubboTransported; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; @Configuration public class RestTemplateConfig { @Bean @LoadBalanced @DubboTransported(protocol = "dubbo") public RestTemplate restTemplate() { return new RestTemplate(); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.consumerdemo.controller; import cn.iocoder.springcloudalibaba.labx7.consumerdemo.feign.UserFeignClient; import cn.iocoder.springcloudalibaba.labx7.dto.UserAddDTO; import cn.iocoder.springcloudalibaba.labx7.dto.UserDTO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/user01") public class User01Controller { @Autowired private UserFeignClient userFeignClient; @GetMapping("/get") public UserDTO get(@RequestParam("id") Integer id) { return userFeignClient.get(id); } @PostMapping("/add") public Integer add(UserAddDTO addDTO) { return userFeignClient.add(addDTO); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.consumerdemo.controller; import cn.iocoder.springcloudalibaba.labx7.consumerdemo.dto.UserAddDTO; import cn.iocoder.springcloudalibaba.labx7.consumerdemo.dto.UserDTO; import cn.iocoder.springcloudalibaba.labx7.consumerdemo.feign.UserFeignClient02; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/user02") public class User02Controller { @Autowired private UserFeignClient02 userFeignClient; @GetMapping("/get") public UserDTO get(@RequestParam("id") Integer id) { return userFeignClient.get(id); } @PostMapping("/add") public Integer add(UserAddDTO addDTO) { return userFeignClient.add(addDTO); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.consumerdemo.controller; import cn.iocoder.springcloudalibaba.labx7.dto.UserAddDTO; import cn.iocoder.springcloudalibaba.labx7.dto.UserDTO; import com.alibaba.fastjson.JSON; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; import org.springframework.web.client.RestTemplate; @RestController @RequestMapping("/user03") public class User03Controller { @Autowired private RestTemplate restTemplate; @GetMapping("/get") public UserDTO get(@RequestParam("id") Integer id) { String url = String.format("http://%s/user/get?id=%d", "demo-provider", id); return restTemplate.getForObject(url, UserDTO.class); } @PostMapping("/add") public Integer add(UserAddDTO addDTO) { // 请求头 HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); // 请求体 String body = JSON.toJSONString(addDTO); // 创建 HttpEntity 对象 HttpEntity entity = new HttpEntity<>(body, headers); // 执行请求 String url = String.format("http://%s/user/add", "demo-provider"); return restTemplate.postForObject(url, entity, Integer.class); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.consumerdemo.controller; import cn.iocoder.springcloudalibaba.labx7.api.UserService; import cn.iocoder.springcloudalibaba.labx7.dto.UserAddDTO; import cn.iocoder.springcloudalibaba.labx7.dto.UserDTO; import org.apache.dubbo.config.annotation.Reference; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/user04") public class User04Controller { @Reference(version = "1.0.0", protocol = "dubbo") private UserService userService; @GetMapping("/get") public UserDTO get(@RequestParam("id") Integer id) { return userService.get(id); } @PostMapping("/add") public Integer add(UserAddDTO addDTO) { return userService.add(addDTO); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.consumerdemo.dto; import java.io.Serializable; /** * 用户添加 DTO */ public class UserAddDTO implements Serializable { /** * 昵称 */ private String name; /** * 性别 */ private Integer gender; public String getName() { return name; } public UserAddDTO setName(String name) { this.name = name; return this; } public Integer getGender() { return gender; } public UserAddDTO setGender(Integer gender) { this.gender = gender; return this; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.consumerdemo.dto; import java.io.Serializable; /** * 用户信息 DTO */ public class UserDTO implements Serializable { /** * 用户编号 */ private Integer id; /** * 昵称 */ private String name; /** * 性别 */ private Integer gender; public Integer getId() { return id; } public UserDTO setId(Integer id) { this.id = id; return this; } public String getName() { return name; } public UserDTO setName(String name) { this.name = name; return this; } public Integer getGender() { return gender; } public UserDTO setGender(Integer gender) { this.gender = gender; return this; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.consumerdemo.feign; import cn.iocoder.springcloudalibaba.labx7.dto.UserAddDTO; import cn.iocoder.springcloudalibaba.labx7.dto.UserDTO; import com.alibaba.cloud.dubbo.annotation.DubboTransported; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; @FeignClient(name = "demo-provider") @DubboTransported(protocol = "dubbo") // @DubboTransported(protocol = "rest") public interface UserFeignClient { /** * 根据指定用户编号,获得用户信息 * * @param id 用户编号 * @return 用户信息 */ @GetMapping("/user/get") UserDTO get(@RequestParam("id") Integer id); /** * 添加新用户,返回新添加的用户编号 * * @param addDTO 添加的用户信息 * @return 用户编号 */ @PostMapping("/user/add") Integer add(@RequestBody UserAddDTO addDTO); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.consumerdemo.feign; import cn.iocoder.springcloudalibaba.labx7.consumerdemo.dto.UserAddDTO; import cn.iocoder.springcloudalibaba.labx7.consumerdemo.dto.UserDTO; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; @FeignClient(name = "demo-provider") public interface UserFeignClient02 { /** * 根据指定用户编号,获得用户信息 * * @param id 用户编号 * @return 用户信息 */ @GetMapping("/user/get") UserDTO get(@RequestParam("id") Integer id); /** * 添加新用户,返回新添加的用户编号 * * @param addDTO 添加的用户信息 * @return 用户编号 */ @PostMapping("/user/add") Integer add(@RequestBody UserAddDTO addDTO); } ================================================ FILE: labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo02/labx-07-sca-dubbo-demo02-consumer/src/main/resources/application.yaml ================================================ spring: application: name: demo-consumer cloud: # Nacos 作为注册中心的配置项 nacos: discovery: server-addr: 127.0.0.1:8848 # Dubbo 配置项,对应 DubboConfigurationProperties 类 dubbo: # Dubbo 服务注册中心配置,对应 RegistryConfig 类 registry: address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址 # Spring Cloud Alibaba Dubbo 专属配置项,对应 DubboCloudProperties 类 cloud: subscribed-services: demo-provider # 设置订阅的应用列表,默认为 * 订阅所有应用。 ================================================ FILE: labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo02/labx-07-sca-dubbo-demo02-provider-rest/pom.xml ================================================ labx-07-sca-dubbo-demo02 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-07-sca-dubbo-demo02-provider-rest 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE 2.7.4.1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import cn.iocoder.springboot.labs labx-07-sca-dubbo-demo02-api 1.0-SNAPSHOT com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery jsr311-api javax.ws.rs com.alibaba.cloud spring-cloud-starter-dubbo org.jboss.resteasy resteasy-netty4 3.0.19.Final org.hibernate.validator hibernate-validator javax.servlet javax.servlet-api 3.1.0 ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.providerdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class RestProviderApplication { public static void main(String[] args) { SpringApplication.run(RestProviderApplication.class); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.providerdemo.service; import cn.iocoder.springcloudalibaba.labx7.api.UserService; import cn.iocoder.springcloudalibaba.labx7.dto.UserAddDTO; import cn.iocoder.springcloudalibaba.labx7.dto.UserDTO; import javax.ws.rs.*; import javax.ws.rs.core.MediaType; import static org.springframework.util.MimeTypeUtils.APPLICATION_JSON_VALUE; @org.apache.dubbo.config.annotation.Service(version = "1.0.0", protocol = {"dubbo", "rest"}) @Path("/user") public class UserServiceImpl implements UserService { @Override @GET @Path("/get") @Produces(APPLICATION_JSON_VALUE) public UserDTO get(@QueryParam("id") Integer id) { return new UserDTO().setId(id) .setName("没有昵称:" + id) .setGender(id % 2 + 1); // 1 - 男;2 - 女 } @Override @POST @Path("/add") @Consumes(MediaType.APPLICATION_JSON) public Integer add(UserAddDTO addDTO) { return (int) (System.currentTimeMillis() / 1000); // 嘿嘿,随便返回一个 id } } ================================================ FILE: labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo02/labx-07-sca-dubbo-demo02-provider-rest/src/main/resources/application.yaml ================================================ spring: application: name: demo-provider cloud: # Nacos 作为注册中心的配置项 nacos: discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 main: web-application-type: NONE # Web 应用类型,这里设置为 NONE # Dubbo 配置项,对应 DubboConfigurationProperties 类 dubbo: scan: base-packages: cn.iocoder.springcloudalibaba.labx7.providerdemo.service # 指定 Dubbo 服务实现类的扫描基准包 # Dubbo 服务暴露的协议配置,对应 ProtocolConfig Map protocols: dubbo: name: dubbo # 协议名称 port: -1 # 协议端口,-1 表示自增端口,从 20880 开始 rest: name: rest port: 9090 # 协议端口 server: netty # 使用的服务器 # Dubbo 服务注册中心配置,对应 RegistryConfig 类 registry: address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址 # Spring Cloud Alibaba Dubbo 专属配置项,对应 DubboCloudProperties 类 cloud: subscribed-services: '' # 设置订阅的应用列表,默认为 * 订阅所有应用。 ================================================ FILE: labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo02/labx-07-sca-dubbo-demo02-provider-springmvc/pom.xml ================================================ labx-07-sca-dubbo-demo02 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-07-sca-dubbo-demo02-provider-springmvc 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import cn.iocoder.springboot.labs labx-07-sca-dubbo-demo01-api 1.0-SNAPSHOT com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery com.alibaba.cloud spring-cloud-starter-dubbo org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator org.apache.dubbo dubbo-spring-boot-actuator 2.7.4.1 ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.providerdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SpringMVCProviderApplication { public static void main(String[] args) { SpringApplication.run(SpringMVCProviderApplication.class); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.providerdemo.service; import cn.iocoder.springcloudalibaba.labx7.api.UserService; import cn.iocoder.springcloudalibaba.labx7.dto.UserAddDTO; import cn.iocoder.springcloudalibaba.labx7.dto.UserDTO; import org.springframework.web.bind.annotation.*; @org.apache.dubbo.config.annotation.Service(version = "1.0.0", protocol = {"dubbo"}) @RestController @RequestMapping("/user") public class UserServiceImpl implements UserService { @Override @GetMapping("/get") public UserDTO get(@RequestParam("id") Integer id) { return new UserDTO().setId(id) .setName("没有昵称:" + id) .setGender(id % 2 + 1); // 1 - 男;2 - 女 } @Override @PostMapping("/add") public Integer add(@RequestBody UserAddDTO addDTO) { return (int) (System.currentTimeMillis() / 1000); // 嘿嘿,随便返回一个 id } } ================================================ FILE: labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo02/labx-07-sca-dubbo-demo02-provider-springmvc/src/main/resources/application.yaml ================================================ spring: application: name: demo-provider cloud: # Nacos 作为注册中心的配置项 nacos: discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 server: port: 18080 # 服务器端口,默认为 8080 # Dubbo 配置项,对应 DubboConfigurationProperties 类 dubbo: scan: base-packages: cn.iocoder.springcloudalibaba.labx7.providerdemo.service # 指定 Dubbo 服务实现类的扫描基准包 # Dubbo 服务暴露的协议配置,对应 ProtocolConfig Map protocols: dubbo: name: dubbo # 协议名称 port: -1 # 协议端口,-1 表示自增端口,从 20880 开始 # Dubbo 服务注册中心配置,对应 RegistryConfig 类 registry: address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址 # Spring Cloud Alibaba Dubbo 专属配置项,对应 DubboCloudProperties 类 cloud: subscribed-services: '' # 设置订阅的应用列表,默认为 * 订阅所有应用。 management: endpoints: web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 endpoint: # Health 端点配置项,对应 HealthProperties 配置类 health: enabled: true # 是否开启。默认为 true 开启。 show-details: ALWAYS # 何时显示完整的健康信息。默认为 NEVER 都不展示。可选 WHEN_AUTHORIZED 当经过授权的用户;可选 ALWAYS 总是展示。 ================================================ FILE: labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo02/pom.xml ================================================ labx-07-spring-cloud-alibaba-dubbo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-07-sca-dubbo-demo02 pom labx-07-sca-dubbo-demo02-api labx-07-sca-dubbo-demo02-provider-rest labx-07-sca-dubbo-demo02-provider-springmvc labx-07-sca-dubbo-demo02-consumer ================================================ FILE: labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo03-validation/labx-07-sca-dubbo-demo03-api/pom.xml ================================================ labx-07-sca-dubbo-demo03-validation cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-07-sca-dubbo-demo03-api javax.validation validation-api 2.0.1.Final org.hibernate.validator hibernate-validator 6.0.18.Final org.glassfish javax.el 3.0.1-b11 ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.api; import cn.iocoder.springcloudalibaba.labx7.dto.UserAddDTO; import cn.iocoder.springcloudalibaba.labx7.dto.UserDTO; import javax.validation.ConstraintViolationException; import javax.validation.constraints.NotNull; /** * 用户服务 RPC Service 接口 */ public interface UserService { /** * 根据指定用户编号,获得用户信息 * * @param id 用户编号 * @return 用户信息 */ UserDTO get(@NotNull(message = "用户编号不能为空") Integer id) throws ConstraintViolationException;; /** * 添加新用户,返回新添加的用户编号 * * @param addDTO 添加的用户信息 * @return 用户编号 */ Integer add(UserAddDTO addDTO) throws ConstraintViolationException;; } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.dto; import org.hibernate.validator.constraints.Length; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; import java.io.Serializable; /** * 用户添加 DTO */ public class UserAddDTO implements Serializable { /** * 昵称 */ @NotEmpty(message = "昵称不能为空") @Length(min = 5, max = 16, message = "账号长度为 5-16 位") private String name; /** * 性别 */ @NotNull(message = "性别不能为空") private Integer gender; public String getName() { return name; } public UserAddDTO setName(String name) { this.name = name; return this; } public Integer getGender() { return gender; } public UserAddDTO setGender(Integer gender) { this.gender = gender; return this; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.dto; import java.io.Serializable; /** * 用户信息 DTO */ public class UserDTO implements Serializable { /** * 用户编号 */ private Integer id; /** * 昵称 */ private String name; /** * 性别 */ private Integer gender; public Integer getId() { return id; } public UserDTO setId(Integer id) { this.id = id; return this; } public String getName() { return name; } public UserDTO setName(String name) { this.name = name; return this; } public Integer getGender() { return gender; } public UserDTO setGender(Integer gender) { this.gender = gender; return this; } } ================================================ FILE: labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo03-validation/labx-07-sca-dubbo-demo03-consumer/pom.xml ================================================ labx-07-sca-dubbo-demo03-validation cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-07-sca-dubbo-demo03-consumer 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import cn.iocoder.springboot.labs labx-07-sca-dubbo-demo03-api 1.0-SNAPSHOT org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery com.alibaba.cloud spring-cloud-starter-dubbo ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.consumerdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.consumerdemo.controller; import cn.iocoder.springcloudalibaba.labx7.api.UserService; import cn.iocoder.springcloudalibaba.labx7.dto.UserAddDTO; import cn.iocoder.springcloudalibaba.labx7.dto.UserDTO; import org.apache.dubbo.config.annotation.Reference; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/user") public class UserController { @Reference(protocol = "dubbo", version = "1.0.0", validation = "true") private UserService userService; @GetMapping("/get") public UserDTO get(@RequestParam("id") Integer id) { return userService.get(id); } @PostMapping("/add") public Integer add(UserAddDTO addDTO) { return userService.add(addDTO); } } ================================================ FILE: labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo03-validation/labx-07-sca-dubbo-demo03-consumer/src/main/resources/application.yaml ================================================ spring: application: name: demo-consumer cloud: # Nacos 作为注册中心的配置项 nacos: discovery: server-addr: 127.0.0.1:8848 # Dubbo 配置项,对应 DubboConfigurationProperties 类 dubbo: # Dubbo 服务注册中心配置,对应 RegistryConfig 类 registry: address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址 # Spring Cloud Alibaba Dubbo 专属配置项,对应 DubboCloudProperties 类 cloud: subscribed-services: demo-provider # 设置订阅的应用列表,默认为 * 订阅所有应用。 ================================================ FILE: labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo03-validation/labx-07-sca-dubbo-demo03-provider/pom.xml ================================================ labx-07-sca-dubbo-demo03-validation cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-07-sca-dubbo-demo03-provider 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import cn.iocoder.springboot.labs labx-07-sca-dubbo-demo03-api 1.0-SNAPSHOT com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery com.alibaba.cloud spring-cloud-starter-dubbo ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.providerdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ProviderApplication { public static void main(String[] args) { SpringApplication.run(ProviderApplication.class); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.providerdemo.service; import cn.iocoder.springcloudalibaba.labx7.api.UserService; import cn.iocoder.springcloudalibaba.labx7.dto.UserAddDTO; import cn.iocoder.springcloudalibaba.labx7.dto.UserDTO; @org.apache.dubbo.config.annotation.Service(protocol = "dubbo", version = "1.0.0", validation = "true") public class UserServiceImpl implements UserService { @Override public UserDTO get(Integer id) { return new UserDTO().setId(id) .setName("没有昵称:" + id) .setGender(id % 2 + 1); // 1 - 男;2 - 女 } @Override public Integer add(UserAddDTO addDTO) { return (int) (System.currentTimeMillis() / 1000); // 嘿嘿,随便返回一个 id } } ================================================ FILE: labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo03-validation/labx-07-sca-dubbo-demo03-provider/src/main/resources/application.yaml ================================================ spring: application: name: demo-provider cloud: # Nacos 作为注册中心的配置项 nacos: discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 # Dubbo 配置项,对应 DubboConfigurationProperties 类 dubbo: scan: base-packages: cn.iocoder.springcloudalibaba.labx7.providerdemo.service # 指定 Dubbo 服务实现类的扫描基准包 # Dubbo 服务暴露的协议配置,对应 ProtocolConfig Map protocols: dubbo: name: dubbo # 协议名称 port: -1 # 协议端口,-1 表示自增端口,从 20880 开始 # Dubbo 服务注册中心配置,对应 RegistryConfig 类 registry: address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址 # Spring Cloud Alibaba Dubbo 专属配置项,对应 DubboCloudProperties 类 cloud: subscribed-services: '' # 设置订阅的应用列表,默认为 * 订阅所有应用。 ================================================ FILE: labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo03-validation/pom.xml ================================================ labx-07-spring-cloud-alibaba-dubbo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-07-sca-dubbo-demo03-validation pom labx-07-sca-dubbo-demo03-api labx-07-sca-dubbo-demo03-provider labx-07-sca-dubbo-demo03-consumer ================================================ FILE: labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo04-filter/labx-07-sca-dubbo-demo04-api/pom.xml ================================================ labx-07-sca-dubbo-demo04-filter cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-07-sca-dubbo-demo04-api javax.validation validation-api 2.0.1.Final org.hibernate.validator hibernate-validator 6.0.18.Final org.glassfish javax.el 3.0.1-b11 ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.api; import cn.iocoder.springcloudalibaba.labx7.dto.UserAddDTO; import cn.iocoder.springcloudalibaba.labx7.dto.UserDTO; import javax.validation.ConstraintViolationException; import javax.validation.constraints.NotNull; /** * 用户服务 RPC Service 接口 */ public interface UserService { /** * 根据指定用户编号,获得用户信息 * * @param id 用户编号 * @return 用户信息 */ UserDTO get(@NotNull(message = "用户编号不能为空") Integer id) throws ConstraintViolationException;; /** * 添加新用户,返回新添加的用户编号 * * @param addDTO 添加的用户信息 * @return 用户编号 */ Integer add(UserAddDTO addDTO) throws ConstraintViolationException;; } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.core; /** * 服务异常 * * 参考 https://www.kancloud.cn/onebase/ob/484204 文章 * * 一共 10 位,分成四段 * * 第一段,1 位,类型 * 1 - 业务级别异常 * 2 - 系统级别异常 * 第二段,3 位,系统类型 * 001 - 用户系统 * 002 - 商品系统 * 003 - 订单系统 * 004 - 支付系统 * 005 - 优惠劵系统 * ... - ... * 第三段,3 位,模块 * 不限制规则。 * 一般建议,每个系统里面,可能有多个模块,可以再去做分段。以用户系统为例子: * 001 - OAuth2 模块 * 002 - User 模块 * 003 - MobileCode 模块 * 第四段,3 位,错误码 * 不限制规则。 * 一般建议,每个模块自增。 */ public final class ServiceException extends RuntimeException { /** * 错误码 */ private Integer code; public ServiceException() { // 创建默认构造方法,用于反序列化的场景。 } public ServiceException(ServiceExceptionEnum serviceExceptionEnum) { // 使用父类的 message 字段 super(serviceExceptionEnum.getMessage()); // 设置错误码 this.code = serviceExceptionEnum.getCode(); } public ServiceException(ServiceExceptionEnum serviceExceptionEnum, String message) { // 使用父类的 message 字段 super(message); // 设置错误码 this.code = serviceExceptionEnum.getCode(); } public Integer getCode() { return code; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.core; /** * 业务异常枚举 */ public enum ServiceExceptionEnum { // ========== 系统级别 ========== SUCCESS(0, "成功"), SYS_ERROR(2001001000, "服务端发生异常"), MISSING_REQUEST_PARAM_ERROR(2001001001, "参数缺失"), INVALID_REQUEST_PARAM_ERROR(2001001002, "请求参数不合法"), // ========== 用户模块 ========== USER_NOT_FOUND(1001002000, "用户不存在"), USER_EXISTS(1001002001, "用户已存在"), // ========== 订单模块 ========== // ========== 商品模块 ========== ; /** * 错误码 */ private final int code; /** * 错误提示 */ private final String message; ServiceExceptionEnum(int code, String message) { this.code = code; this.message = message; } public int getCode() { return code; } public String getMessage() { return message; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.dto; import org.hibernate.validator.constraints.Length; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; import java.io.Serializable; /** * 用户添加 DTO */ public class UserAddDTO implements Serializable { /** * 昵称 */ @NotEmpty(message = "昵称不能为空") @Length(min = 5, max = 16, message = "账号长度为 5-16 位") private String name; /** * 性别 */ @NotNull(message = "性别不能为空") private Integer gender; public String getName() { return name; } public UserAddDTO setName(String name) { this.name = name; return this; } public Integer getGender() { return gender; } public UserAddDTO setGender(Integer gender) { this.gender = gender; return this; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.dto; import java.io.Serializable; /** * 用户信息 DTO */ public class UserDTO implements Serializable { /** * 用户编号 */ private Integer id; /** * 昵称 */ private String name; /** * 性别 */ private Integer gender; public Integer getId() { return id; } public UserDTO setId(Integer id) { this.id = id; return this; } public String getName() { return name; } public UserDTO setName(String name) { this.name = name; return this; } public Integer getGender() { return gender; } public UserDTO setGender(Integer gender) { this.gender = gender; return this; } } ================================================ FILE: labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo04-filter/labx-07-sca-dubbo-demo04-consumer/pom.xml ================================================ labx-07-sca-dubbo-demo04-filter cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-07-sca-dubbo-demo04-consumer 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import cn.iocoder.springboot.labs labx-07-sca-dubbo-demo04-api 1.0-SNAPSHOT org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery com.alibaba.cloud spring-cloud-starter-dubbo ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.consumerdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.consumerdemo.controller; import cn.iocoder.springcloudalibaba.labx7.api.UserService; import cn.iocoder.springcloudalibaba.labx7.dto.UserAddDTO; import cn.iocoder.springcloudalibaba.labx7.dto.UserDTO; import org.apache.dubbo.config.annotation.Reference; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/user") public class UserController { @Reference(protocol = "dubbo", version = "1.0.0", validation = "true") private UserService userService; @GetMapping("/get") public UserDTO get(@RequestParam("id") Integer id) { return userService.get(id); } @PostMapping("/add") public Integer add(UserAddDTO addDTO) { return userService.add(addDTO); } } ================================================ FILE: labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo04-filter/labx-07-sca-dubbo-demo04-consumer/src/main/resources/application.yaml ================================================ spring: application: name: demo-consumer cloud: # Nacos 作为注册中心的配置项 nacos: discovery: server-addr: 127.0.0.1:8848 # Dubbo 配置项,对应 DubboConfigurationProperties 类 dubbo: # Dubbo 服务注册中心配置,对应 RegistryConfig 类 registry: address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址 # Spring Cloud Alibaba Dubbo 专属配置项,对应 DubboCloudProperties 类 cloud: subscribed-services: demo-provider # 设置订阅的应用列表,默认为 * 订阅所有应用。 ================================================ FILE: labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo04-filter/labx-07-sca-dubbo-demo04-provider/pom.xml ================================================ labx-07-sca-dubbo-demo04-filter cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-07-sca-dubbo-demo04-provider 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import cn.iocoder.springboot.labs labx-07-sca-dubbo-demo04-api 1.0-SNAPSHOT com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery com.alibaba.cloud spring-cloud-starter-dubbo ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.providerdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ProviderApplication { public static void main(String[] args) { SpringApplication.run(ProviderApplication.class); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.providerdemo.filter; import cn.iocoder.springcloudalibaba.labx7.core.ServiceException; import cn.iocoder.springcloudalibaba.labx7.core.ServiceExceptionEnum; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.ReflectUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.*; import org.apache.dubbo.rpc.service.GenericService; import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolationException; import java.lang.reflect.Method; @Activate(group = CommonConstants.PROVIDER) public class DubboExceptionFilter extends ListenableFilter { public DubboExceptionFilter() { super.listener = new ExceptionListenerX(); } @Override public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { return invoker.invoke(invocation); } static class ExceptionListenerX extends ExceptionListener { @Override public void onResponse(Result appResponse, Invoker invoker, Invocation invocation) { // 发生异常,并且非泛化调用 if (appResponse.hasException() && GenericService.class != invoker.getInterface()) { Throwable exception = appResponse.getException(); // <1> 如果是 ServiceException 异常,直接返回 if (exception instanceof ServiceException) { return; } // <2> 如果是参数校验的 ConstraintViolationException 异常,则封装返回 if (exception instanceof ConstraintViolationException) { appResponse.setException(this.handleConstraintViolationException((ConstraintViolationException) exception)); return; } } // <3> 其它情况,继续使用父类处理 super.onResponse(appResponse, invoker, invocation); } // 将 ConstraintViolationException 转换成 ServiceException private ServiceException handleConstraintViolationException(ConstraintViolationException ex) { // 拼接错误 StringBuilder detailMessage = new StringBuilder(); for (ConstraintViolation constraintViolation : ex.getConstraintViolations()) { // 使用 ; 分隔多个错误 if (detailMessage.length() > 0) { detailMessage.append(";"); } // 拼接内容到其中 detailMessage.append(constraintViolation.getMessage()); } // 返回异常 return new ServiceException(ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR, detailMessage.toString()); } } static class ExceptionListener implements Listener { private Logger logger = LoggerFactory.getLogger(ExceptionListener.class); @Override public void onResponse(Result appResponse, Invoker invoker, Invocation invocation) { if (appResponse.hasException() && GenericService.class != invoker.getInterface()) { try { Throwable exception = appResponse.getException(); // directly throw if it's checked exception if (!(exception instanceof RuntimeException) && (exception instanceof Exception)) { return; } // directly throw if the exception appears in the signature try { Method method = invoker.getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes()); Class[] exceptionClassses = method.getExceptionTypes(); for (Class exceptionClass : exceptionClassses) { if (exception.getClass().equals(exceptionClass)) { return; } } } catch (NoSuchMethodException e) { return; } // for the exception not found in method's signature, print ERROR message in server's log. 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); // directly throw if exception class and interface class are in the same jar file. String serviceFile = ReflectUtils.getCodeBase(invoker.getInterface()); String exceptionFile = ReflectUtils.getCodeBase(exception.getClass()); if (serviceFile == null || exceptionFile == null || serviceFile.equals(exceptionFile)) { return; } // directly throw if it's JDK exception String className = exception.getClass().getName(); if (className.startsWith("java.") || className.startsWith("javax.")) { return; } // directly throw if it's dubbo exception if (exception instanceof RpcException) { return; } // otherwise, wrap with RuntimeException and throw back to the client appResponse.setException(new RuntimeException(StringUtils.toString(exception))); return; } catch (Throwable e) { 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); return; } } } @Override public void onError(Throwable e, Invoker invoker, Invocation invocation) { 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); } // For test purpose public void setLogger(Logger logger) { this.logger = logger; } } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.providerdemo.service; import cn.iocoder.springcloudalibaba.labx7.api.UserService; import cn.iocoder.springcloudalibaba.labx7.core.ServiceException; import cn.iocoder.springcloudalibaba.labx7.core.ServiceExceptionEnum; import cn.iocoder.springcloudalibaba.labx7.dto.UserAddDTO; import cn.iocoder.springcloudalibaba.labx7.dto.UserDTO; @org.apache.dubbo.config.annotation.Service(protocol = "dubbo", version = "1.0.0", validation = "true", filter = "-exception") public class UserServiceImpl implements UserService { @Override public UserDTO get(Integer id) { return new UserDTO().setId(id) .setName("没有昵称:" + id) .setGender(id % 2 + 1); // 1 - 男;2 - 女 } @Override public Integer add(UserAddDTO addDTO) { // 【额外添加】这里,模拟用户已经存在的情况 if ("yudaoyuanma".equals(addDTO.getName())) { throw new ServiceException(ServiceExceptionEnum.USER_EXISTS); } return (int) (System.currentTimeMillis() / 1000); // 嘿嘿,随便返回一个 id } } ================================================ FILE: 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 ================================================ dubboExceptionFilter=cn.iocoder.springcloudalibaba.labx7.providerdemo.filter.DubboExceptionFilter ================================================ FILE: labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo04-filter/labx-07-sca-dubbo-demo04-provider/src/main/resources/application.yaml ================================================ spring: application: name: demo-provider cloud: # Nacos 作为注册中心的配置项 nacos: discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 # Dubbo 配置项,对应 DubboConfigurationProperties 类 dubbo: scan: base-packages: cn.iocoder.springcloudalibaba.labx7.providerdemo.service # 指定 Dubbo 服务实现类的扫描基准包 # Dubbo 服务暴露的协议配置,对应 ProtocolConfig Map protocols: dubbo: name: dubbo # 协议名称 port: -1 # 协议端口,-1 表示自增端口,从 20880 开始 # Dubbo 服务注册中心配置,对应 RegistryConfig 类 registry: address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址 # Spring Cloud Alibaba Dubbo 专属配置项,对应 DubboCloudProperties 类 cloud: subscribed-services: '' # 设置订阅的应用列表,默认为 * 订阅所有应用。 ================================================ FILE: labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo04-filter/pom.xml ================================================ labx-07-spring-cloud-alibaba-dubbo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-07-sca-dubbo-demo04-filter pom labx-07-sca-dubbo-demo04-api labx-07-sca-dubbo-demo04-provider labx-07-sca-dubbo-demo04-consumer ================================================ FILE: labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo05-sentinel/labx-07-sca-dubbo-demo05-api/pom.xml ================================================ labx-07-sca-dubbo-demo05-sentinel cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-07-sca-dubbo-demo05-api ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.api; import cn.iocoder.springcloudalibaba.labx7.dto.UserAddDTO; import cn.iocoder.springcloudalibaba.labx7.dto.UserDTO; /** * 用户服务 RPC Service 接口 */ public interface UserService { /** * 根据指定用户编号,获得用户信息 * * @param id 用户编号 * @return 用户信息 */ UserDTO get(Integer id); /** * 添加新用户,返回新添加的用户编号 * * @param addDTO 添加的用户信息 * @return 用户编号 */ Integer add(UserAddDTO addDTO); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.dto; import java.io.Serializable; /** * 用户添加 DTO */ public class UserAddDTO implements Serializable { /** * 昵称 */ private String name; /** * 性别 */ private Integer gender; public String getName() { return name; } public UserAddDTO setName(String name) { this.name = name; return this; } public Integer getGender() { return gender; } public UserAddDTO setGender(Integer gender) { this.gender = gender; return this; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.dto; import java.io.Serializable; /** * 用户信息 DTO */ public class UserDTO implements Serializable { /** * 用户编号 */ private Integer id; /** * 昵称 */ private String name; /** * 性别 */ private Integer gender; public Integer getId() { return id; } public UserDTO setId(Integer id) { this.id = id; return this; } public String getName() { return name; } public UserDTO setName(String name) { this.name = name; return this; } public Integer getGender() { return gender; } public UserDTO setGender(Integer gender) { this.gender = gender; return this; } } ================================================ FILE: labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo05-sentinel/labx-07-sca-dubbo-demo05-consumer/pom.xml ================================================ labx-07-sca-dubbo-demo05-sentinel cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-07-sca-dubbo-demo05-consumer 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import cn.iocoder.springboot.labs labx-07-sca-dubbo-demo01-api 1.0-SNAPSHOT org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery com.alibaba.cloud spring-cloud-starter-dubbo com.alibaba.cloud spring-cloud-starter-alibaba-sentinel com.alibaba.csp sentinel-apache-dubbo-adapter ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.consumerdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.consumerdemo.controller; import cn.iocoder.springcloudalibaba.labx7.api.UserService; import cn.iocoder.springcloudalibaba.labx7.dto.UserAddDTO; import cn.iocoder.springcloudalibaba.labx7.dto.UserDTO; import org.apache.dubbo.config.annotation.Reference; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/user") public class UserController { @Reference(protocol = "dubbo", version = "1.0.0") private UserService userService; @GetMapping("/get") public UserDTO get(@RequestParam("id") Integer id) { return userService.get(id); } @PostMapping("/add") public Integer add(UserAddDTO addDTO) { return userService.add(addDTO); } } ================================================ FILE: labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo05-sentinel/labx-07-sca-dubbo-demo05-consumer/src/main/resources/application.yaml ================================================ spring: application: name: demo-consumer cloud: # Nacos 作为注册中心的配置项 nacos: discovery: server-addr: 127.0.0.1:8848 # Sentinel 配置项 sentinel: eager: true # 是否饥饿加载。默认为 false 关闭 transport: dashboard: 127.0.0.1:7070 # Sentinel 控制台地址 # Dubbo 配置项,对应 DubboConfigurationProperties 类 dubbo: # Dubbo 服务注册中心配置,对应 RegistryConfig 类 registry: address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址 # Spring Cloud Alibaba Dubbo 专属配置项,对应 DubboCloudProperties 类 cloud: subscribed-services: demo-provider # 设置订阅的应用列表,默认为 * 订阅所有应用。 ================================================ FILE: labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo05-sentinel/labx-07-sca-dubbo-demo05-provider/pom.xml ================================================ labx-07-sca-dubbo-demo05-sentinel cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-07-sca-dubbo-demo05-provider 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import cn.iocoder.springboot.labs labx-07-sca-dubbo-demo01-api 1.0-SNAPSHOT com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery com.alibaba.cloud spring-cloud-starter-dubbo com.alibaba.cloud spring-cloud-starter-alibaba-sentinel com.alibaba.csp sentinel-apache-dubbo-adapter ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.providerdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ProviderApplication { public static void main(String[] args) { SpringApplication.run(ProviderApplication.class); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx7.providerdemo.service; import cn.iocoder.springcloudalibaba.labx7.api.UserService; import cn.iocoder.springcloudalibaba.labx7.dto.UserAddDTO; import cn.iocoder.springcloudalibaba.labx7.dto.UserDTO; @org.apache.dubbo.config.annotation.Service(protocol = "dubbo", version = "1.0.0") public class UserServiceImpl implements UserService { @Override public UserDTO get(Integer id) { return new UserDTO().setId(id) .setName("没有昵称:" + id) .setGender(id % 2 + 1); // 1 - 男;2 - 女 } @Override public Integer add(UserAddDTO addDTO) { return (int) (System.currentTimeMillis() / 1000); // 嘿嘿,随便返回一个 id } } ================================================ FILE: labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo05-sentinel/labx-07-sca-dubbo-demo05-provider/src/main/resources/application.yaml ================================================ spring: application: name: demo-provider cloud: # Nacos 作为注册中心的配置项 nacos: discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 # Sentinel 配置项 sentinel: eager: true # 是否饥饿加载。默认为 false 关闭 transport: dashboard: 127.0.0.1:7070 # Sentinel 控制台地址 # Dubbo 配置项,对应 DubboConfigurationProperties 类 dubbo: scan: base-packages: cn.iocoder.springcloudalibaba.labx7.providerdemo.service # 指定 Dubbo 服务实现类的扫描基准包 # Dubbo 服务暴露的协议配置,对应 ProtocolConfig Map protocols: dubbo: name: dubbo # 协议名称 port: -1 # 协议端口,-1 表示自增端口,从 20880 开始 # Dubbo 服务注册中心配置,对应 RegistryConfig 类 registry: address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址 # Spring Cloud Alibaba Dubbo 专属配置项,对应 DubboCloudProperties 类 cloud: subscribed-services: '' # 设置订阅的应用列表,默认为 * 订阅所有应用。 ================================================ FILE: labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo05-sentinel/pom.xml ================================================ labx-07-spring-cloud-alibaba-dubbo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-07-sca-dubbo-demo05-sentinel pom labx-07-sca-dubbo-demo05-api labx-07-sca-dubbo-demo05-provider labx-07-sca-dubbo-demo05-consumer ================================================ FILE: labx-07-spring-cloud-alibaba-dubbo/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-07-spring-cloud-alibaba-dubbo pom labx-07-sca-dubbo-demo01 labx-07-sca-dubbo-demo02 labx-07-sca-dubbo-demo03-validation labx-07-sca-dubbo-demo04-filter labx-07-sca-dubbo-demo05-sentinel ================================================ FILE: labx-07-spring-cloud-alibaba-dubbo/《芋道 Spring Cloud Alibaba 服务调用 Dubbo 入门》.md ================================================ ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo01/pom.xml ================================================ labx-08-spring-cloud-gateway cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-08-sc-gateway-demo01 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.cloud spring-cloud-starter-gateway ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo01/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/GatewayApplication.java ================================================ package cn.iocoder.springcloud.labx08.gatewaydemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } } ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo01/src/main/resources/application.yaml ================================================ server: port: 8888 spring: application: name: gateway-application cloud: # Spring Cloud Gateway 配置项,对应 GatewayProperties 类 gateway: # 路由配置项,对应 RouteDefinition 数组 routes: - id: yudaoyuanma # 路由的编号 uri: http://www.iocoder.cn # 路由到的目标地址 predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组 - Path=/blog filters: - StripPrefix=1 - id: oschina # 路由的编号 uri: https://www.oschina.net # 路由的目标地址 predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组 - Path=/oschina filters: # 过滤器,对请求进行拦截,实现自定义的功能,对应 FilterDefinition 数组 - StripPrefix=1 ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo01-test/pom.xml ================================================ labx-08-spring-cloud-gateway cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-08-sc-gateway-demo01-test 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.cloud spring-cloud-starter-gateway ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo01-test/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/GatewayApplication.java ================================================ package cn.iocoder.springcloud.labx08.gatewaydemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } } ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo01-test/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/config/GatewayConfig.java ================================================ package cn.iocoder.springcloud.labx08.gatewaydemo.config; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.context.annotation.Bean; import org.springframework.core.annotation.Order; import reactor.core.publisher.Mono; //@Configuration public class GatewayConfig { private Logger logger = LoggerFactory.getLogger(GatewayConfig.class); @Bean @Order(1) public GlobalFilter firstGlobalFilter() { return (exchange, chain) -> { logger.info("[first][pre]"); return chain.filter(exchange) .then(Mono.fromRunnable(() -> logger.info("[first][post]"))); }; } @Bean @Order(2) public GlobalFilter secondGatewayFilter() { return (exchange, chain) -> { logger.info("[second][pre]"); return chain.filter(exchange) .then(Mono.fromRunnable(() -> logger.info("[second][post]"))); }; } @Bean @Order(3) public GlobalFilter thirdGlobalFilter() { return (exchange, chain) -> { logger.info("[third][pre]"); return chain.filter(exchange) .then(Mono.fromRunnable(() -> logger.info("[third][post]"))); }; } } ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo01-test/src/main/resources/application.yaml ================================================ server: port: 8888 spring: application: name: gateway-application cloud: # Spring Cloud Gateway 配置项,对应 GatewayProperties 类 gateway: # 路由配置项,对应 RouteDefinition 数组 routes: - id: yudaoyuanma # 路由的编号 uri: http://www.iocoder.cn # 路由到的目标地址 predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组 - Path=/blog filters: - StripPrefix=1 - id: oschina # 路由的编号 uri: https://www.oschina.net # 路由的目标地址 predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组 - Path=/oschina filters: # 过滤器,对请求进行拦截,实现自定义的功能,对应 FilterDefinition 数组 - StripPrefix=100 # - StripPrefix=200 # default-filters: # - StripPrefix=1 # - StripPrefix=2 # - StripPrefix=3 # httpserver: # wiretap: true # httpclient: # wiretap: true logging: level: reactor.netty: DEBUG org.springframework.cloud.gateway: TRACE # org.springframework.web.reactive: TRACE ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo02-registry/pom.xml ================================================ labx-08-spring-cloud-gateway cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-08-sc-gateway-demo02-registry 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.cloud spring-cloud-starter-gateway com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo02-registry/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/GatewayApplication.java ================================================ package cn.iocoder.springcloud.labx08.gatewaydemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } } ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo02-registry/src/main/resources/application.yaml ================================================ server: port: 8888 spring: application: name: gateway-application cloud: # Spring Cloud Gateway 配置项,对应 GatewayProperties 类 gateway: # 路由配置项,对应 RouteDefinition 数组 routes: - id: yudaoyuanma # 路由的编号 uri: http://www.iocoder.cn # 路由到的目标地址 predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组 - Path=/blog filters: - StripPrefix=1 - id: oschina # 路由的编号 uri: https://www.oschina.net # 路由的目标地址 predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组 - Path=/oschina filters: # 过滤器,对请求进行拦截,实现自定义的功能,对应 FilterDefinition 数组 - StripPrefix=1 # 与 Spring Cloud 注册中心的集成,对应 DiscoveryLocatorProperties 类 discovery: locator: enabled: true # 是否开启,默认为 false 关闭 url-expression: "'lb://' + serviceId" # 路由的目标地址的表达式,默认为 "'lb://' + serviceId" # Nacos 作为注册中心的配置项 nacos: discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo03-config-apollo/pom.xml ================================================ labx-08-spring-cloud-gateway cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-08-sc-gateway-demo03-config-apollo 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.cloud spring-cloud-starter-gateway com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery com.ctrip.framework.apollo apollo-client 1.5.1 ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo03-config-apollo/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/GatewayApplication.java ================================================ package cn.iocoder.springcloud.labx08.gatewaydemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } } ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo03-config-apollo/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/GatewayPropertiesRefresher.java ================================================ package cn.iocoder.springcloud.labx08.gatewaydemo; import com.ctrip.framework.apollo.enums.PropertyChangeType; import com.ctrip.framework.apollo.model.ConfigChange; import com.ctrip.framework.apollo.model.ConfigChangeEvent; import com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.context.environment.EnvironmentChangeEvent; import org.springframework.cloud.gateway.config.GatewayProperties; import org.springframework.cloud.gateway.event.RefreshRoutesEvent; import org.springframework.cloud.gateway.route.RouteDefinitionWriter; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.stereotype.Component; import java.util.ArrayList; /** * 由 https://github.com/ctripcorp/apollo-use-cases/tree/master/spring-cloud-gateway 提供代码,感谢~ */ @Component public class GatewayPropertiesRefresher implements ApplicationContextAware, ApplicationEventPublisherAware { private static final Logger logger = LoggerFactory.getLogger(GatewayPropertiesRefresher.class); private static final String ID_PATTERN = "spring\\.cloud\\.gateway\\.routes\\[\\d+\\]\\.id"; private static final String DEFAULT_FILTER_PATTERN = "spring\\.cloud\\.gateway\\.default-filters\\[\\d+\\]\\.name"; private ApplicationContext applicationContext; private ApplicationEventPublisher publisher; @Autowired private GatewayProperties gatewayProperties; @Autowired private RouteDefinitionWriter routeDefinitionWriter; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.publisher = applicationEventPublisher; } @ApolloConfigChangeListener(interestedKeyPrefixes = "spring.cloud.gateway.") public void onChange(ConfigChangeEvent changeEvent) { refreshGatewayProperties(changeEvent); } /*** * 刷新org.springframework.cloud.gateway.config.PropertiesRouteDefinitionLocator中定义的routes * * @param changeEvent * @return void * @author ksewen * @date 2019/5/21 2:13 PM */ private void refreshGatewayProperties(ConfigChangeEvent changeEvent) { logger.info("Refreshing GatewayProperties!"); // <1> preDestroyGatewayProperties(changeEvent); // <2> this.applicationContext.publishEvent(new EnvironmentChangeEvent(changeEvent.changedKeys())); // <3> refreshGatewayRouteDefinition(); logger.info("GatewayProperties refreshed!"); } /*** * GatewayProperties没有@PreDestroy和destroy方法 * org.springframework.cloud.context.properties.ConfigurationPropertiesRebinder#rebind(java.lang.String)中destroyBean时不会销毁当前对象 * 如果把spring.cloud.gateway.前缀的配置项全部删除(例如需要动态删除最后一个路由的场景),initializeBean时也无法创建新的bean,则return当前bean * 若仍保留有spring.cloud.gateway.routes[n]或spring.cloud.gateway.default-filters[n]等配置,initializeBean时会注入新的属性替换已有的bean * 这个方法提供了类似@PreDestroy的操作,根据配置文件的实际情况把org.springframework.cloud.gateway.config.GatewayProperties#routes * 和org.springframework.cloud.gateway.config.GatewayProperties#defaultFilters两个集合清空 * * @param * @return void * @author ksewen * @date 2019/5/21 2:13 PM */ private synchronized void preDestroyGatewayProperties(ConfigChangeEvent changeEvent) { logger.info("Pre Destroy GatewayProperties!"); // 判断 `spring.cloud.gateway.routes` 配置项,是否被全部删除。如果是,则置空 GatewayProperties 的 `routes` 属性 final boolean needClearRoutes = this.checkNeedClear(changeEvent, ID_PATTERN, this.gatewayProperties.getRoutes().size()); if (needClearRoutes) { this.gatewayProperties.setRoutes(new ArrayList<>()); } // 判断 `spring.cloud.gateway.default-filters` 配置项,是否被全部删除。如果是,则置空 GatewayProperties 的 `defaultFilters` 属性 final boolean needClearDefaultFilters = this.checkNeedClear(changeEvent, DEFAULT_FILTER_PATTERN, this.gatewayProperties.getDefaultFilters().size()); if (needClearDefaultFilters) { this.gatewayProperties.setRoutes(new ArrayList<>()); } logger.info("Pre Destroy GatewayProperties finished!"); } private void refreshGatewayRouteDefinition() { logger.info("Refreshing Gateway RouteDefinition!"); this.publisher.publishEvent(new RefreshRoutesEvent(this)); logger.info("Gateway RouteDefinition refreshed!"); } /*** * 根据changeEvent和定义的pattern匹配key,如果所有对应PropertyChangeType为DELETED则需要清空GatewayProperties里相关集合 * * @param changeEvent * @param pattern * @param existSize * @return boolean * @author ksewen * @date 2019/5/23 2:18 PM */ // 判断是否清除的标准,是通过指定配置项被删除的数量,是否和内存中的该配置项的数量一样。如果一样,说明被清空了 private boolean checkNeedClear(ConfigChangeEvent changeEvent, String pattern, int existSize) { return changeEvent.changedKeys().stream().filter(key -> key.matches(pattern)) .filter(key -> { ConfigChange change = changeEvent.getChange(key); return PropertyChangeType.DELETED.equals(change.getChangeType()); }).count() == existSize; } } ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo03-config-apollo/src/main/resources/application.yaml ================================================ server: port: 8888 spring: application: name: gateway-application cloud: # Spring Cloud Gateway 配置项,全部配置在 Apollo 中 # gateway: # Nacos 作为注册中心的配置项 nacos: discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 # Apollo 相关配置项 app: id: ${spring.application.name} # 使用的 Apollo 的项目(应用)编号 apollo: meta: http://127.0.0.1:8080 # Apollo Meta Server 地址 bootstrap: enabled: true # 是否开启 Apollo 配置预加载功能。默认为 false。 eagerLoad: enable: true # 是否开启 Apollo 支持日志级别的加载时机。默认为 false。 namespaces: application # 使用的 Apollo 的命名空间,默认为 application。 ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo03-config-nacos/pom.xml ================================================ labx-08-spring-cloud-gateway cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-08-sc-gateway-demo03-config-nacos 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.cloud spring-cloud-starter-gateway com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery com.alibaba.cloud spring-cloud-starter-alibaba-nacos-config ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo03-config-nacos/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/GatewayApplication.java ================================================ package cn.iocoder.springcloud.labx08.gatewaydemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } } ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo03-config-nacos/src/main/resources/application.yaml ================================================ server: port: 8888 spring: cloud: # Spring Cloud Gateway 配置项,全部配置在 Nacos 中 # gateway: # Nacos 作为注册中心的配置项 nacos: discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo03-config-nacos/src/main/resources/bootstrap.yaml ================================================ spring: application: name: gateway-application cloud: nacos: # Nacos Config 配置项,对应 NacosConfigProperties 配置属性类 config: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 namespace: # 使用的 Nacos 的命名空间,默认为 null group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP name: # 使用的 Nacos 配置集的 dataId,默认为 spring.application.name file-extension: yaml # 使用的 Nacos 配置集的 dataId 的文件拓展名,同时也是 Nacos 配置集的配置格式,默认为 properties ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo04/pom.xml ================================================ labx-08-spring-cloud-gateway cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-08-sc-gateway-demo04 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.cloud spring-cloud-starter-gateway ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo04/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/GatewayApplication.java ================================================ package cn.iocoder.springcloud.labx08.gatewaydemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } } ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo04/src/main/resources/application.yaml ================================================ server: port: 8888 spring: application: name: gateway-application cloud: # Spring Cloud Gateway 配置项,对应 GatewayProperties 类 gateway: # 路由配置项,对应 RouteDefinition 数组 routes: - id: user-service-prod uri: http://www.iocoder.cn predicates: - Path=/** - Weight=user-service, 90 - id: user-service-canary uri: https://www.oschina.net predicates: - Path=/** - Weight=user-service, 10 ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo05-custom-gateway-filter/pom.xml ================================================ labx-08-spring-cloud-gateway cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-08-sc-gateway-demo05-custom-gateway-filter 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.cloud spring-cloud-starter-gateway ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo05-custom-gateway-filter/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/GatewayApplication.java ================================================ package cn.iocoder.springcloud.labx08.gatewaydemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } } ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo05-custom-gateway-filter/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/filter/AuthGatewayFilterFactory.java ================================================ package cn.iocoder.springcloud.labx08.gatewaydemo.filter; import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.util.HashMap; import java.util.Map; @Component public class AuthGatewayFilterFactory extends AbstractGatewayFilterFactory { public AuthGatewayFilterFactory() { super(AuthGatewayFilterFactory.Config.class); } @Override public GatewayFilter apply(Config config) { // token 和 userId 的映射 Map tokenMap = new HashMap<>(); tokenMap.put("yunai", 1); // 创建 GatewayFilter 对象 return new GatewayFilter() { @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 获得 token ServerHttpRequest request = exchange.getRequest(); HttpHeaders headers = request.getHeaders(); String token = headers.getFirst(config.getTokenHeaderName()); // 如果没有 token,则不进行认证。因为可能是无需认证的 API 接口 if (!StringUtils.hasText(token)) { return chain.filter(exchange); } // 进行认证 ServerHttpResponse response = exchange.getResponse(); Integer userId = tokenMap.get(token); // 通过 token 获取不到 userId,说明认证不通过 if (userId == null) { // 响应 401 状态码 response.setStatusCode(HttpStatus.UNAUTHORIZED); // 响应提示 DataBuffer buffer = exchange.getResponse().bufferFactory().wrap("认证不通过".getBytes()); return response.writeWith(Flux.just(buffer)); } // 认证通过,将 userId 添加到 Header 中 request = request.mutate().header(config.getUserIdHeaderName(), String.valueOf(userId)) .build(); return chain.filter(exchange.mutate().request(request).build()); } }; } public static class Config { private static final String DEFAULT_TOKEN_HEADER_NAME = "token"; private static final String DEFAULT_HEADER_NAME = "user-id"; private String tokenHeaderName = DEFAULT_TOKEN_HEADER_NAME; private String userIdHeaderName = DEFAULT_HEADER_NAME; public String getTokenHeaderName() { return tokenHeaderName; } public String getUserIdHeaderName() { return userIdHeaderName; } public Config setTokenHeaderName(String tokenHeaderName) { this.tokenHeaderName = tokenHeaderName; return this; } public Config setUserIdHeaderName(String userIdHeaderName) { this.userIdHeaderName = userIdHeaderName; return this; } } } ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo05-custom-gateway-filter/src/main/resources/application.yaml ================================================ server: port: 8888 spring: application: name: gateway-application cloud: # Spring Cloud Gateway 配置项,对应 GatewayProperties 类 gateway: # 路由配置项,对应 RouteDefinition 数组 routes: - id: yudaoyuanma # 路由的编号 uri: http://www.iocoder.cn # 路由到的目标地址 predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组 - Path=/blog filters: - StripPrefix=1 - id: oschina # 路由的编号 uri: https://www.oschina.net # 路由的目标地址 predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组 - Path=/oschina filters: # 过滤器,对请求进行拦截,实现自定义的功能,对应 FilterDefinition 数组 - StripPrefix=1 # 默认过滤器,对应 FilterDefinition 数组 default-filters: - name: Auth args: token-header-name: access-token ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo06-rate-limiter/pom.xml ================================================ labx-08-spring-cloud-gateway cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-08-sc-gateway-demo06-rate-limiter 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.cloud spring-cloud-starter-gateway org.springframework.boot spring-boot-starter-data-redis ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo06-rate-limiter/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/GatewayApplication.java ================================================ package cn.iocoder.springcloud.labx08.gatewaydemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } } ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo06-rate-limiter/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/config/GatewayConfig.java ================================================ package cn.iocoder.springcloud.labx08.gatewaydemo.config; import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; @Configuration public class GatewayConfig { @Bean public KeyResolver ipKeyResolver() { return new KeyResolver() { @Override public Mono resolve(ServerWebExchange exchange) { // 获取请求的 IP return Mono.just(exchange.getRequest().getRemoteAddress().getHostName()); } }; } } ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo06-rate-limiter/src/main/resources/application.yaml ================================================ server: port: 8888 spring: application: name: gateway-application cloud: # Spring Cloud Gateway 配置项,对应 GatewayProperties 类 gateway: # 路由配置项,对应 RouteDefinition 数组 routes: - id: yudaoyuanma # 路由的编号 uri: http://www.iocoder.cn # 路由到的目标地址 predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组 - Path=/blog filters: - StripPrefix=1 - name: RequestRateLimiter args: redis-rate-limiter.replenishRate: 1 # 令牌桶的每秒放的数量 redis-rate-limiter.burstCapacity: 2 # 令牌桶的最大令牌数 key-resolver: "#{@ipKeyResolver}" # 获取限流 KEY 的 Bean 的名字 - id: oschina # 路由的编号 uri: https://www.oschina.net # 路由的目标地址 predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组 - Path=/oschina filters: # 过滤器,对请求进行拦截,实现自定义的功能,对应 FilterDefinition 数组 - StripPrefix=1 # Redis 配置项 redis: host: 127.0.0.1 port: 6379 ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo07-hystrix/pom.xml ================================================ labx-08-spring-cloud-gateway cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-08-sc-gateway-demo07-hystrix 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.cloud spring-cloud-starter-gateway org.springframework.cloud spring-cloud-starter-netflix-hystrix ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo07-hystrix/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/GatewayApplication.java ================================================ package cn.iocoder.springcloud.labx08.gatewaydemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } } ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo07-hystrix/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/controller/FallbackController.java ================================================ package cn.iocoder.springcloud.labx08.gatewaydemo.controller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.gateway.support.ServerWebExchangeUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.server.ServerWebExchange; @RestController public class FallbackController { private Logger logger = LoggerFactory.getLogger(FallbackController.class); @GetMapping("/fallback") public String fallback(ServerWebExchange exchange) { // URI requestUrl = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR); Throwable executionException = exchange.getAttribute(ServerWebExchangeUtils.HYSTRIX_EXECUTION_EXCEPTION_ATTR); logger.error("[fallback][发生异常]", executionException); return "服务降级..." + executionException.getMessage(); } } ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo07-hystrix/src/main/resources/application.yaml ================================================ server: port: 8888 spring: application: name: gateway-application cloud: # Spring Cloud Gateway 配置项,对应 GatewayProperties 类 gateway: # 路由配置项,对应 RouteDefinition 数组 routes: - id: hystrix_test uri: http://127.0.0.1:18181 predicates: - Path=/** filters: - name: Hystrix args: name: fallbackcmd # 对应的 Hystrix Command 名字 fallbackUri: forward:/fallback # 处理 Hystrix fallback 的情况,重定向到指定地址 ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo07-sentinel/pom.xml ================================================ labx-08-spring-cloud-gateway cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-08-sc-gateway-demo07-sentinel 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.cloud spring-cloud-starter-gateway com.alibaba.cloud spring-cloud-starter-alibaba-sentinel com.alibaba.cloud spring-cloud-alibaba-sentinel-gateway ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo07-sentinel/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/CustomBlockRequestHandler.java ================================================ package cn.iocoder.springcloud.labx08.gatewaydemo; import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.stereotype.Component; import org.springframework.web.reactive.function.server.ServerResponse; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; @Component public class CustomBlockRequestHandler implements BlockRequestHandler { private static final String DEFAULT_BLOCK_MSG_PREFIX = "HAHAHA ~ Blocked by Sentinel: "; @Override public Mono handleRequest(ServerWebExchange exchange, Throwable ex) { return ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS) // 状态码 .contentType(MediaType.TEXT_PLAIN) // 内容类型为 text/plain 纯文本 .bodyValue(DEFAULT_BLOCK_MSG_PREFIX + ex.getClass().getSimpleName()); // 错误提示 } } ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo07-sentinel/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/GatewayApplication.java ================================================ package cn.iocoder.springcloud.labx08.gatewaydemo; import com.alibaba.cloud.sentinel.gateway.ConfigConstants; import com.alibaba.csp.sentinel.config.SentinelConfig; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class GatewayApplication { public static void main(String[] args) { System.setProperty(SentinelConfig.APP_TYPE, ConfigConstants.APP_TYPE_SCG_GATEWAY); // 【重点】设置应用类型为 Spring Cloud Gateway SpringApplication.run(GatewayApplication.class, args); } } ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo07-sentinel/src/main/resources/application.yaml ================================================ server: port: 8888 spring: application: name: gateway-application cloud: # Spring Cloud Gateway 配置项,对应 GatewayProperties 类 gateway: # 路由配置项,对应 RouteDefinition 数组 routes: - id: yudaoyuanma # 路由的编号 uri: http://www.iocoder.cn # 路由到的目标地址 predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组 - Path=/** sentinel: eager: true # 是否饥饿加载。默认为 false 关闭 transport: dashboard: localhost:7070 # 是否饥饿加载。默认为 false 关闭 # # 数据源的配置项 # datasource: # ds1.file: # file: "classpath: sentinel-gw-flow.json" # ruleType: gw-flow # ds2.file: # file: "classpath: sentinel-gw-api-group.json" # ruleType: gw-api-group # Sentinel 对 Spring Cloud Gateway 的专属配置项,对应 SentinelGatewayProperties 类 scg: order: -2147483648 # 过滤器顺序,默认为 -2147483648 最高优先级 fallback: mode: # fallback 模式,目前有三种:response、redirect、空 # 专属 response 模式 response-status: 429 # 响应状态码,默认为 429 response-body: 你被 block 了... # 响应内容,默认为空 content-type: application/json # 内容类型,默认为 application/json # 专属 redirect 模式 redirect: http://www.baidu.com ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo07-sentinel/src/main/resources/sentinel-gw-api-group.json ================================================ [ { "apiName": "yudaoyuanma_customized_api", "predicateItems": [ { "pattern": "/categories/**", "matchStrategy": 1 }, { "items": [ { "pattern": "/Dubbo/good-collection/", "matchStrategy": 0 }, { "pattern": "/SkyWalking/**", "matchStrategy": 1 } ] } ] } ] ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo07-sentinel/src/main/resources/sentinel-gw-flow.json ================================================ [ { "resource": "yudaoyuanma", "count": 3 }, { "resource": "yudaoyuanma_customized_api", "count": 1 } ] ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo08-custom-global-filter/pom.xml ================================================ labx-08-spring-cloud-gateway cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-08-sc-gateway-demo08-custom-global-filter 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.cloud spring-cloud-starter-gateway com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery com.alibaba.cloud spring-cloud-starter-dubbo ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo08-custom-global-filter/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/GatewayApplication.java ================================================ package cn.iocoder.springcloud.labx08.gatewaydemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } } ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo08-custom-global-filter/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/User03Controller.java ================================================ package cn.iocoder.springcloud.labx08.gatewaydemo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @RestController @RequestMapping("/user03") public class User03Controller { @Autowired private RestTemplate restTemplate; // @GetMapping("/get") // public UserDTO get(@RequestParam("id") Integer id) { // String url = String.format("http://%s/user/get?id=%d", "demo-provider", id); // return restTemplate.getForObject(url, UserDTO.class); // } // // @PostMapping("/add") // public Integer add(UserAddDTO addDTO) { // // 请求头 // HttpHeaders headers = new HttpHeaders(); // headers.setContentType(MediaType.APPLICATION_JSON); // // 请求体 // String body = JSON.toJSONString(addDTO); // // 创建 HttpEntity 对象 // HttpEntity entity = new HttpEntity<>(body, headers); // // 执行请求 // String url = String.format("http://%s/user/add", "demo-provider"); // return restTemplate.postForObject(url, entity, Integer.class); // } } ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo08-custom-global-filter/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/config/RestTemplateConfig.java ================================================ package cn.iocoder.springcloud.labx08.gatewaydemo.config; import com.alibaba.cloud.dubbo.annotation.DubboTransported; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; @Configuration public class RestTemplateConfig { @Bean @LoadBalanced @DubboTransported(protocol = "dubbo") public RestTemplate restTemplate() { return new RestTemplate(); } } ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo08-custom-global-filter/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/filter/DubboFilter.java ================================================ package cn.iocoder.springcloud.labx08.gatewaydemo.filter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; import java.net.URI; import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR; import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR; @Component public class DubboFilter implements GlobalFilter, Ordered { @Autowired private RestTemplate restTemplate; public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR); String schemePrefix = exchange.getAttribute(GATEWAY_SCHEME_PREFIX_ATTR); if (url == null || (!"dubbo".equals(url.getScheme()) && !"dubbo".equals(schemePrefix))) { return chain.filter(exchange); } String urlx = String.format("http://%s/user/get?id=%d", "demo-provider", 1); // UserDTO result = restTemplate.getForObject(url, UserDTO.class); String result = restTemplate.getForObject(url, String.class); return null; } public int getOrder() { return 10100; } } ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo08-custom-global-filter/src/main/resources/application.yaml ================================================ server: port: 8888 spring: application: name: gateway-application cloud: # Spring Cloud Gateway 配置项,对应 GatewayProperties 类 gateway: # 路由配置项,对应 RouteDefinition 数组 routes: # - id: yudaoyuanma # 路由的编号 # uri: http://www.iocoder.cn # 路由到的目标地址 # predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组 # - Path=/blog # filters: # - StripPrefix=1 # - id: oschina # 路由的编号 # uri: https://www.oschina.net # 路由的目标地址 # predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组 # - Path=/oschina # filters: # 过滤器,对请求进行拦截,实现自定义的功能,对应 FilterDefinition 数组 # - StripPrefix=1 - id: yudaoyuanma # 路由的编号 uri: dubbo://demo-provider # 路由到的目标地址 predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组 - Path=/user/** # filters: # - StripPrefix=1 # Dubbo 配置项,对应 DubboConfigurationProperties 类 dubbo: # Dubbo 服务注册中心配置,对应 RegistryConfig 类 registry: address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址 # Spring Cloud Alibaba Dubbo 专属配置项,对应 DubboCloudProperties 类 cloud: subscribed-services: demo-provider # 设置订阅的应用列表,默认为 * 订阅所有应用。 ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo09-actuator/pom.xml ================================================ labx-08-spring-cloud-gateway cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-08-sc-gateway-demo09-actuator 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.cloud spring-cloud-starter-gateway org.springframework.boot spring-boot-starter-actuator ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo09-actuator/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/GatewayApplication.java ================================================ package cn.iocoder.springcloud.labx08.gatewaydemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } } ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo09-actuator/src/main/resources/application.yaml ================================================ server: port: 8888 spring: application: name: gateway-application # cloud: # # Spring Cloud Gateway 配置项,对应 GatewayProperties 类 # gateway: # # 路由配置项,对应 RouteDefinition 数组 # routes: # - id: yudaoyuanma # 路由的编号 # uri: http://www.iocoder.cn # 路由到的目标地址 # predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组 # - Path=/blog # filters: # - StripPrefix=1 # - id: oschina # 路由的编号 # uri: https://www.oschina.net # 路由的目标地址 # predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组 # - Path=/oschina # filters: # 过滤器,对请求进行拦截,实现自定义的功能,对应 FilterDefinition 数组 # - StripPrefix=1 management: endpoints: web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 endpoint: # Health 端点配置项,对应 HealthProperties 配置类 health: enabled: true # 是否开启。默认为 true 开启。 show-details: ALWAYS # 何时显示完整的健康信息。默认为 NEVER 都不展示。可选 WHEN_AUTHORIZED 当经过授权的用户;可选 ALWAYS 总是展示。 ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo10-troubleshooting/pom.xml ================================================ labx-08-spring-cloud-gateway cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-08-sc-gateway-demo10-troubleshooting 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.cloud spring-cloud-starter-gateway ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo10-troubleshooting/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/GatewayApplication.java ================================================ package cn.iocoder.springcloud.labx08.gatewaydemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } } ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo10-troubleshooting/src/main/resources/application.yaml ================================================ server: port: 8888 spring: application: name: gateway-application cloud: # Spring Cloud Gateway 配置项,对应 GatewayProperties 类 gateway: # 路由配置项,对应 RouteDefinition 数组 routes: - id: yudaoyuanma # 路由的编号 uri: http://www.iocoder.cn # 路由到的目标地址 predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组 - Path=/blog filters: - StripPrefix=1 - id: oschina # 路由的编号 uri: https://www.oschina.net # 路由的目标地址 predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组 - Path=/oschina filters: # 过滤器,对请求进行拦截,实现自定义的功能,对应 FilterDefinition 数组 - StripPrefix=1 # Reactor Netty 相关配置 httpserver: wiretap: true httpclient: wiretap: true logging: level: reactor.netty: DEBUG org.springframework.cloud.gateway: TRACE ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-user-service/pom.xml ================================================ labx-08-spring-cloud-gateway cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-08-sc-user-service 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-user-service/src/main/java/cn/iocoder/springcloud/labx08/userservice/UserServiceApplication.java ================================================ package cn.iocoder.springcloud.labx08.userservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class UserServiceApplication { public static void main(String[] args) { SpringApplication.run(UserServiceApplication.class, args); } } ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-user-service/src/main/java/cn/iocoder/springcloud/labx08/userservice/controller/UserController.java ================================================ package cn.iocoder.springcloud.labx08.userservice.controller; import cn.iocoder.springcloud.labx08.userservice.dto.UserAddDTO; import cn.iocoder.springcloud.labx08.userservice.dto.UserDTO; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/user") public class UserController { @GetMapping("/get") public UserDTO get(@RequestParam("id") Integer id) { return new UserDTO().setId(id) .setName("没有昵称:" + id) .setGender(id % 2 + 1); // 1 - 男;2 - 女 } @PostMapping("/add") public Integer add(UserAddDTO addDTO) { return (int) (System.currentTimeMillis() / 1000); // 嘿嘿,随便返回一个 id } } ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-user-service/src/main/java/cn/iocoder/springcloud/labx08/userservice/dto/UserAddDTO.java ================================================ package cn.iocoder.springcloud.labx08.userservice.dto; import java.io.Serializable; /** * 用户添加 DTO */ public class UserAddDTO implements Serializable { /** * 昵称 */ private String name; /** * 性别 */ private Integer gender; public String getName() { return name; } public UserAddDTO setName(String name) { this.name = name; return this; } public Integer getGender() { return gender; } public UserAddDTO setGender(Integer gender) { this.gender = gender; return this; } } ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-user-service/src/main/java/cn/iocoder/springcloud/labx08/userservice/dto/UserDTO.java ================================================ package cn.iocoder.springcloud.labx08.userservice.dto; import java.io.Serializable; /** * 用户信息 DTO */ public class UserDTO implements Serializable { /** * 用户编号 */ private Integer id; /** * 昵称 */ private String name; /** * 性别 */ private Integer gender; public Integer getId() { return id; } public UserDTO setId(Integer id) { this.id = id; return this; } public String getName() { return name; } public UserDTO setName(String name) { this.name = name; return this; } public Integer getGender() { return gender; } public UserDTO setGender(Integer gender) { this.gender = gender; return this; } } ================================================ FILE: labx-08-spring-cloud-gateway/labx-08-sc-user-service/src/main/resources/application.yaml ================================================ spring: application: name: user-service # Spring 应用名 cloud: nacos: # Nacos 作为注册中心的配置项 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 "'lb://'+serviceId" server: port: ${random.int[10000,19999]} # 服务器端口。默认为 8080 # port: 18080 # 服务器端口。默认为 8080 ================================================ FILE: labx-08-spring-cloud-gateway/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-08-spring-cloud-gateway pom labx-08-sc-gateway-demo01 labx-08-sc-gateway-demo01-test labx-08-sc-gateway-demo02-registry labx-08-sc-gateway-demo03-config-apollo labx-08-sc-gateway-demo03-config-nacos labx-08-sc-gateway-demo04 labx-08-sc-gateway-demo05-custom-gateway-filter labx-08-sc-gateway-demo06-rate-limiter labx-08-sc-gateway-demo07-hystrix labx-08-sc-gateway-demo07-sentinel labx-08-sc-gateway-demo08-custom-global-filter labx-08-sc-gateway-demo09-actuator labx-08-sc-gateway-demo10-troubleshooting labx-08-sc-user-service ================================================ FILE: labx-08-spring-cloud-gateway/《芋道 Spring Cloud 网关 Spring Cloud Gateway 入门》.md ================================================ ================================================ FILE: labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo/pom.xml ================================================ labx-09-spring-cloud-apollo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-09-sc-apollo-demo 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.cloud spring-cloud-commons org.springframework.cloud spring-cloud-context org.springframework.boot spring-boot-starter-web com.ctrip.framework.apollo apollo-client 1.5.1 ================================================ FILE: labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo/src/main/java/cn/iocoder/springcloud/labx09/apollodemo/DemoApplication.java ================================================ package cn.iocoder.springcloud.labx09.apollodemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class); } } ================================================ FILE: labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo/src/main/java/cn/iocoder/springcloud/labx09/apollodemo/config/OrderProperties.java ================================================ package cn.iocoder.springcloud.labx09.apollodemo.config; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component @ConfigurationProperties(prefix = "order") public class OrderProperties { /** * 订单支付超时时长,单位:秒。 */ private Integer payTimeoutSeconds; /** * 订单创建频率,单位:秒 */ private Integer createFrequencySeconds; // /** // * 配置描述 // */ // private String desc; public Integer getPayTimeoutSeconds() { return payTimeoutSeconds; } public OrderProperties setPayTimeoutSeconds(Integer payTimeoutSeconds) { this.payTimeoutSeconds = payTimeoutSeconds; return this; } public Integer getCreateFrequencySeconds() { return createFrequencySeconds; } public OrderProperties setCreateFrequencySeconds(Integer createFrequencySeconds) { this.createFrequencySeconds = createFrequencySeconds; return this; } // public String getDesc() { // return desc; // } // // public OrderProperties setDesc(String desc) { // this.desc = desc; // return this; // } } ================================================ FILE: labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo/src/main/java/cn/iocoder/springcloud/labx09/apollodemo/controller/DemoController.java ================================================ package cn.iocoder.springcloud.labx09.apollodemo.controller; import cn.iocoder.springcloud.labx09.apollodemo.config.OrderProperties; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.Map; @RestController @RequestMapping("/demo") public class DemoController { @Autowired private OrderProperties orderProperties; /** * 测试 @ConfigurationProperties 注解的配置属性类 */ @GetMapping("/test01") public OrderProperties test01() { return orderProperties; } @Value(value = "${order.pay-timeout-seconds}") private Integer payTimeoutSeconds; @Value(value = "${order.create-frequency-seconds}") private Integer createFrequencySeconds; /** * 测试 @Value 注解的属性 */ @GetMapping("/test02") public Map test02() { Map result = new HashMap<>(); result.put("payTimeoutSeconds", payTimeoutSeconds); result.put("createFrequencySeconds", createFrequencySeconds); return result; } } ================================================ FILE: labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo/src/main/resources/application.yaml ================================================ server: port: 7070 # 避免和本地的 Apollo Portal 端口冲突 app: id: demo-application # 使用的 Apollo 的项目(应用)编号 apollo: meta: http://127.0.0.1:8080 # Apollo Meta Server 地址 bootstrap: enabled: true # 是否开启 Apollo 配置预加载功能。默认为 false。 eagerLoad: enable: true # 是否开启 Apollo 支持日志级别的加载时机。默认为 false。 namespaces: application # 使用的 Apollo 的命名空间,默认为 application。 ================================================ FILE: labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo-auto-refresh/pom.xml ================================================ labx-09-spring-cloud-apollo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-09-sc-apollo-demo-auto-refresh 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.cloud spring-cloud-commons org.springframework.cloud spring-cloud-context org.springframework.boot spring-boot-starter-web com.ctrip.framework.apollo apollo-client 1.5.1 ================================================ FILE: labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo-auto-refresh/src/main/java/cn/iocoder/springcloud/labx09/apollodemo/DemoApplication.java ================================================ package cn.iocoder.springcloud.labx09.apollodemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class); } } ================================================ FILE: labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo-auto-refresh/src/main/java/cn/iocoder/springcloud/labx09/apollodemo/config/OrderProperties.java ================================================ package cn.iocoder.springcloud.labx09.apollodemo.config; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component @ConfigurationProperties(prefix = "order") public class OrderProperties { /** * 订单支付超时时长,单位:秒。 */ private Integer payTimeoutSeconds; /** * 订单创建频率,单位:秒 */ private Integer createFrequencySeconds; // /** // * 配置描述 // */ // private String desc; public Integer getPayTimeoutSeconds() { return payTimeoutSeconds; } public OrderProperties setPayTimeoutSeconds(Integer payTimeoutSeconds) { this.payTimeoutSeconds = payTimeoutSeconds; return this; } public Integer getCreateFrequencySeconds() { return createFrequencySeconds; } public OrderProperties setCreateFrequencySeconds(Integer createFrequencySeconds) { this.createFrequencySeconds = createFrequencySeconds; return this; } // public String getDesc() { // return desc; // } // // public OrderProperties setDesc(String desc) { // this.desc = desc; // return this; // } } ================================================ FILE: labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo-auto-refresh/src/main/java/cn/iocoder/springcloud/labx09/apollodemo/controller/DemoController.java ================================================ package cn.iocoder.springcloud.labx09.apollodemo.controller; import cn.iocoder.springcloud.labx09.apollodemo.config.OrderProperties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.Map; @RestController @RequestMapping("/demo") public class DemoController { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private OrderProperties orderProperties; /** * 测试 @ConfigurationProperties 注解的配置属性类 */ @GetMapping("/test01") public OrderProperties test01() { return orderProperties; } @Value(value = "${order.pay-timeout-seconds}") private Integer payTimeoutSeconds; @Value(value = "${order.create-frequency-seconds}") private Integer createFrequencySeconds; /** * 测试 @Value 注解的属性 */ @GetMapping("/test02") public Map test02() { Map result = new HashMap<>(); result.put("payTimeoutSeconds", payTimeoutSeconds); result.put("createFrequencySeconds", createFrequencySeconds); return result; } @GetMapping("/logger") public void logger() { logger.debug("[logger][测试一下]"); } } ================================================ FILE: labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo-auto-refresh/src/main/java/cn/iocoder/springcloud/labx09/apollodemo/listener/ApolloPropertiesRefresher.java ================================================ package cn.iocoder.springcloud.labx09.apollodemo.listener; import com.ctrip.framework.apollo.core.ConfigConsts; import com.ctrip.framework.apollo.model.ConfigChangeEvent; import com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener; import org.springframework.beans.BeansException; import org.springframework.cloud.context.environment.EnvironmentChangeEvent; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; @Component public class ApolloPropertiesRefresher implements ApplicationContextAware { private ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } @ApolloConfigChangeListener(value = ConfigConsts.NAMESPACE_APPLICATION) public void onChange(ConfigChangeEvent changeEvent) { this.applicationContext.publishEvent(new EnvironmentChangeEvent(changeEvent.changedKeys())); } } ================================================ FILE: labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo-auto-refresh/src/main/resources/application.yaml ================================================ server: port: 7070 # 避免和本地的 Apollo Portal 端口冲突 app: id: demo-application # 使用的 Apollo 的项目(应用)编号 apollo: meta: http://127.0.0.1:8080 # Apollo Meta Server 地址 bootstrap: enabled: true # 是否开启 Apollo 配置预加载功能。默认为 false。 eagerLoad: enable: true # 是否开启 Apollo 支持日志级别的加载时机。默认为 false。 namespaces: application # 使用的 Apollo 的命名空间,默认为 application。 ================================================ FILE: labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo-jasypt/pom.xml ================================================ labx-09-spring-cloud-apollo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-09-sc-apollo-demo-jasypt 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.cloud spring-cloud-commons org.springframework.cloud spring-cloud-context org.springframework.boot spring-boot-starter-web com.ctrip.framework.apollo apollo-client 1.5.1 com.github.ulisesbocchio jasypt-spring-boot-starter 3.0.2 org.springframework.boot spring-boot-starter-test test ================================================ FILE: labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo-jasypt/src/main/java/cn/iocoder/springcloud/labx09/apollodemo/DemoApplication.java ================================================ package cn.iocoder.springcloud.labx09.apollodemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class); } } ================================================ FILE: labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo-jasypt/src/main/java/cn/iocoder/springcloud/labx09/apollodemo/controller/DemoController.java ================================================ package cn.iocoder.springcloud.labx09.apollodemo.controller; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @Value("${xxx-password:}") private String xxxPassword; @GetMapping("/test") public String test() { return xxxPassword; } } ================================================ FILE: labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo-jasypt/src/main/resources/application.yaml ================================================ server: port: 7070 # 避免和本地的 Apollo Portal 端口冲突 app: id: demo-application-jasypt # 使用的 Apollo 的项目(应用)编号 apollo: meta: http://127.0.0.1:8080 # Apollo Meta Server 地址 bootstrap: enabled: true # 是否开启 Apollo 配置预加载功能。默认为 false。 eagerLoad: enable: true # 是否开启 Apollo 支持日志级别的加载时机。默认为 false。 namespaces: application # 使用的 Apollo 的命名空间,默认为 application。 ================================================ FILE: labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo-jasypt/src/test/java/cn/iocoder/springcloud/labx09/apollodemo/JasyptTest.java ================================================ package cn.iocoder.springcloud.labx09.apollodemo; import org.jasypt.encryption.StringEncryptor; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest public class JasyptTest { @Autowired private StringEncryptor encryptor; @Test public void encode() { // 第一个加密 String password = "woshimima"; System.out.println(encryptor.encrypt(password)); // 第二个加密 password = "bushimima"; System.out.println(encryptor.encrypt(password)); } @Value("${xxx-password:}") private String xxxPassword; @Test public void print() { System.out.println(xxxPassword); } // @Value("${jasypt.encryptor.password}") // private String password; } ================================================ FILE: labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo-multi/pom.xml ================================================ labx-09-spring-cloud-apollo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-09-sc-apollo-demo-multi 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.cloud spring-cloud-commons org.springframework.cloud spring-cloud-context org.springframework.boot spring-boot-starter-web com.ctrip.framework.apollo apollo-client 1.5.1 ================================================ FILE: labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo-multi/src/main/java/cn/iocoder/springcloud/labx09/apollodemo/DemoApplication.java ================================================ package cn.iocoder.springcloud.labx09.apollodemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.env.Environment; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { // 启动 Spring Boot 应用 ConfigurableApplicationContext context = SpringApplication.run(DemoApplication.class); // 查看 Environment Environment environment = context.getEnvironment(); System.out.println(environment); } } ================================================ FILE: labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo-multi/src/main/resources/application.yaml ================================================ server: port: 7070 # 避免和本地的 Apollo Portal 端口冲突 app: id: demo-application-multi # 使用的 Apollo 的项目(应用)编号 apollo: meta: http://127.0.0.1:8080 # Apollo Meta Server 地址 bootstrap: enabled: true # 是否开启 Apollo 配置预加载功能。默认为 false。 eagerLoad: enable: true # 是否开启 Apollo 支持日志级别的加载时机。默认为 false。 namespaces: application, db # 使用的 Apollo 的命名空间,默认为 application。 ================================================ FILE: labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo-profiles/pom.xml ================================================ labx-09-spring-cloud-apollo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-09-sc-apollo-demo-profiles 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.cloud spring-cloud-commons org.springframework.cloud spring-cloud-context org.springframework.boot spring-boot-starter-web com.ctrip.framework.apollo apollo-client 1.5.1 ================================================ FILE: labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo-profiles/src/main/java/cn/iocoder/springcloud/labx09/apollodemo/DemoApplication.java ================================================ package cn.iocoder.springcloud.labx09.apollodemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class); } } ================================================ FILE: labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo-profiles/src/main/resources/application-dev.yaml ================================================ app: id: demo-application-profiles # 使用的 Apollo 的项目(应用)编号 apollo: meta: http://127.0.0.1:8080 # Apollo Meta Server 地址 bootstrap: enabled: true # 是否开启 Apollo 配置预加载功能。默认为 false。 eagerLoad: enable: true # 是否开启 Apollo 支持日志级别的加载时机。默认为 false。 namespaces: application # 使用的 Apollo 的命名空间,默认为 application。 ================================================ FILE: labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo-profiles/src/main/resources/application-prod.yaml ================================================ app: id: demo-application-profiles # 使用的 Apollo 的项目(应用)编号 apollo: meta: http://127.0.0.1:18080 # Apollo Meta Server 地址 bootstrap: enabled: true # 是否开启 Apollo 配置预加载功能。默认为 false。 eagerLoad: enable: true # 是否开启 Apollo 支持日志级别的加载时机。默认为 false。 namespaces: application # 使用的 Apollo 的命名空间,默认为 application。 ================================================ FILE: labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo-profiles/src/main/resources/application.yaml ================================================ #server: # port: 7070 spring: application: name: demo-application ================================================ FILE: labx-09-spring-cloud-apollo/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-09-spring-cloud-apollo pom labx-09-sc-apollo-demo labx-09-sc-apollo-demo-profiles labx-09-sc-apollo-demo-auto-refresh labx-09-sc-apollo-demo-jasypt labx-09-sc-apollo-demo-multi ================================================ FILE: labx-09-spring-cloud-apollo/《芋道 Spring Cloud 配置中心 Apollo 入门》.md ================================================ ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-ack/pom.xml ================================================ labx-10-spring-cloud-stream-rabbitmq cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-10-sc-stream-rabbitmq-consumer-ack 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-rabbit ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-ack/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/ConsumerApplication.java ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo; import cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener.MySink; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySink.class) public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener; import cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message.Demo01Message; import com.rabbitmq.client.Channel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.support.AmqpHeaders; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.messaging.handler.annotation.Header; import org.springframework.messaging.handler.annotation.Payload; import org.springframework.stereotype.Component; import java.io.IOException; import java.util.concurrent.atomic.AtomicInteger; @Component public class Demo01Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); private AtomicInteger index = new AtomicInteger(); @StreamListener(MySink.DEMO01_INPUT) public void onMessage(@Payload Demo01Message message, @Header(AmqpHeaders.CHANNEL) Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) throws IOException { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); // 提交消费进度 // if (message.getId() % 2 == 1) { if (index.incrementAndGet() == 1) { // ack 确认消息 // 第二个参数 multiple ,用于批量确认消息,为了减少网络流量,手动确认可以被批处。 // 1. 当 multiple 为 true 时,则可以一次性确认 deliveryTag 小于等于传入值的所有消息 // 2. 当 multiple 为 false 时,则只确认当前 deliveryTag 对应的消息 channel.basicAck(deliveryTag, false); } } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener; import org.springframework.cloud.stream.annotation.Input; import org.springframework.messaging.SubscribableChannel; public interface MySink { String DEMO01_INPUT = "demo01-input"; @Input(DEMO01_INPUT) SubscribableChannel demo01Input(); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-ack/src/main/resources/application.yml ================================================ spring: application: name: demo-consumer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binder 配置项,对应 BinderProperties Map binders: rabbit001: type: rabbit # 设置 Binder 的类型 environment: # 设置 Binder 的环境配置 # 如果是 RabbitMQ 类型的时候,则对应的是 RabbitProperties 类 spring: rabbitmq: host: 127.0.0.1 # RabbitMQ 服务的地址 port: 5672 # RabbitMQ 服务的端口 username: guest # RabbitMQ 服务的账号 password: guest # RabbitMQ 服务的密码 # Binding 配置项,对应 BindingProperties Map bindings: demo01-input: destination: DEMO-TOPIC-01 # 目的地。这里使用 RabbitMQ Exchange content-type: application/json # 内容格式。这里使用 JSON group: demo01-consumer-group-DEMO-TOPIC-01 # 消费者分组 binder: rabbit001 # 设置使用的 Binder 名字 # RabbitMQ 自定义 Binding 配置项,对应 RabbitBindingProperties Map rabbit: bindings: demo01-input: # RabbitMQ Consumer 配置项,对应 RabbitConsumerProperties 类 consumer: acknowledge-mode: MANUAL # 消费消息的确认模式,默认为 AUTO 自动确认 server: port: ${random.int[10000,19999]} # 随机端口,方便启动多个消费者 ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-actuator/pom.xml ================================================ labx-10-spring-cloud-stream-rabbitmq cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-10-sc-stream-rabbitmq-consumer-actuator 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-rabbit org.springframework.boot spring-boot-starter-actuator ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-actuator/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/ConsumerApplication.java ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo; import cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener.MySink; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySink.class) public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener; import cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message.Demo01Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.messaging.handler.annotation.Payload; import org.springframework.stereotype.Component; @Component public class Demo01Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @StreamListener(MySink.DEMO01_INPUT) public void onMessage(@Payload Demo01Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener; import org.springframework.cloud.stream.annotation.Input; import org.springframework.messaging.SubscribableChannel; public interface MySink { String DEMO01_INPUT = "demo01-input"; @Input(DEMO01_INPUT) SubscribableChannel demo01Input(); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-actuator/src/main/resources/application.yml ================================================ spring: application: name: demo-consumer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binder 配置项,对应 BinderProperties Map binders: rabbit001: type: rabbit # 设置 Binder 的类型 environment: # 设置 Binder 的环境配置 # 如果是 RabbitMQ 类型的时候,则对应的是 RabbitProperties 类 spring: rabbitmq: host: 127.0.0.1 # RabbitMQ 服务的地址 port: 5672 # RabbitMQ 服务的端口 username: guest # RabbitMQ 服务的账号 password: guest # RabbitMQ 服务的密码 # Binding 配置项,对应 BindingProperties Map bindings: demo01-input: destination: DEMO-TOPIC-01 # 目的地。这里使用 RabbitMQ Exchange content-type: application/json # 内容格式。这里使用 JSON group: demo01-consumer-group-DEMO-TOPIC-01 # 消费者分组 binder: rabbit001 # 设置使用的 Binder 名字 server: port: ${random.int[10000,19999]} # 随机端口,方便启动多个消费者 management: endpoints: web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 endpoint: # Health 端点配置项,对应 HealthProperties 配置类 health: enabled: true # 是否开启。默认为 true 开启。 show-details: ALWAYS # 何时显示完整的健康信息。默认为 NEVER 都不展示。可选 WHEN_AUTHORIZED 当经过授权的用户;可选 ALWAYS 总是展示。 ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-batch/pom.xml ================================================ labx-10-spring-cloud-stream-rabbitmq cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-10-sc-stream-rabbitmq-consumer-batch 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-rabbit ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-batch/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/ConsumerApplication.java ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo; import cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener.MySink; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySink.class) public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener; import cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message.Demo01Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.messaging.handler.annotation.Payload; import org.springframework.stereotype.Component; import java.util.List; @Component public class Demo01Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @StreamListener(MySink.DEMO01_INPUT) public void onMessage(@Payload List message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener; import org.springframework.cloud.stream.annotation.Input; import org.springframework.messaging.SubscribableChannel; public interface MySink { String DEMO01_INPUT = "demo01-input"; @Input(DEMO01_INPUT) SubscribableChannel demo01Input(); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-batch/src/main/resources/application.yml ================================================ spring: application: name: demo-consumer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binder 配置项,对应 BinderProperties Map binders: rabbit001: type: rabbit # 设置 Binder 的类型 environment: # 设置 Binder 的环境配置 # 如果是 RabbitMQ 类型的时候,则对应的是 RabbitProperties 类 spring: rabbitmq: host: 127.0.0.1 # RabbitMQ 服务的地址 port: 5672 # RabbitMQ 服务的端口 username: guest # RabbitMQ 服务的账号 password: guest # RabbitMQ 服务的密码 # Binding 配置项,对应 BindingProperties Map bindings: demo01-input: destination: DEMO-TOPIC-01 # 目的地。这里使用 RabbitMQ Exchange content-type: application/json # 内容格式。这里使用 JSON group: demo01-consumer-group-DEMO-TOPIC-01 # 消费者分组 binder: rabbit001 # 设置使用的 Binder 名字 # Consumer 配置项,对应 ConsumerProperties 类 consumer: batch-mode: true # 是否批量消费默认,默认为 false server: port: ${random.int[10000,19999]} # 随机端口,方便启动多个消费者 ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-broadcasting/pom.xml ================================================ labx-10-spring-cloud-stream-rabbitmq cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-10-sc-stream-rabbitmq-consumer-broadcasting 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-rabbit ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-broadcasting/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/ConsumerApplication.java ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo; import cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener.MySink; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySink.class) public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener; import cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message.Demo01Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.messaging.handler.annotation.Payload; import org.springframework.stereotype.Component; @Component public class Demo01Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @StreamListener(MySink.DEMO01_INPUT) public void onMessage(@Payload Demo01Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener; import org.springframework.cloud.stream.annotation.Input; import org.springframework.messaging.SubscribableChannel; public interface MySink { String DEMO01_INPUT = "demo01-input"; @Input(DEMO01_INPUT) SubscribableChannel demo01Input(); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-broadcasting/src/main/resources/application.yml ================================================ spring: application: name: demo-consumer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binder 配置项,对应 BinderProperties Map binders: rabbit001: type: rabbit # 设置 Binder 的类型 environment: # 设置 Binder 的环境配置 # 如果是 RabbitMQ 类型的时候,则对应的是 RabbitProperties 类 spring: rabbitmq: host: 127.0.0.1 # RabbitMQ 服务的地址 port: 5672 # RabbitMQ 服务的端口 username: guest # RabbitMQ 服务的账号 password: guest # RabbitMQ 服务的密码 # Binding 配置项,对应 BindingProperties Map bindings: demo01-input: destination: DEMO-TOPIC-01 # 目的地。这里使用 RabbitMQ Exchange content-type: application/json # 内容格式。这里使用 JSON # group: demo01-consumer-group-DEMO-TOPIC-01 # 消费者分组 binder: rabbit001 # 设置使用的 Binder 名字 server: port: ${random.int[10000,19999]} # 随机端口,方便启动多个消费者 ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-concurrency/pom.xml ================================================ labx-10-spring-cloud-stream-rabbitmq cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-10-sc-stream-rabbitmq-consumer-concurrency 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-rabbit ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-concurrency/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/ConsumerApplication.java ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo; import cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener.MySink; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySink.class) public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener; import cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message.Demo01Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.messaging.handler.annotation.Payload; import org.springframework.stereotype.Component; @Component public class Demo01Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @StreamListener(MySink.DEMO01_INPUT) public void onMessage(@Payload Demo01Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); // // try { // Thread.sleep(10 * 1000L); // } catch (InterruptedException ignored) { // } } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener; import org.springframework.cloud.stream.annotation.Input; import org.springframework.messaging.SubscribableChannel; public interface MySink { String DEMO01_INPUT = "demo01-input"; @Input(DEMO01_INPUT) SubscribableChannel demo01Input(); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-concurrency/src/main/resources/application.yml ================================================ spring: application: name: demo-consumer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binder 配置项,对应 BinderProperties Map binders: rabbit001: type: rabbit # 设置 Binder 的类型 environment: # 设置 Binder 的环境配置 # 如果是 RabbitMQ 类型的时候,则对应的是 RabbitProperties 类 spring: rabbitmq: host: 127.0.0.1 # RabbitMQ 服务的地址 port: 5672 # RabbitMQ 服务的端口 username: guest # RabbitMQ 服务的账号 password: guest # RabbitMQ 服务的密码 # Binding 配置项,对应 BindingProperties Map bindings: demo01-input: destination: DEMO-TOPIC-01 # 目的地。这里使用 RabbitMQ Exchange content-type: application/json # 内容格式。这里使用 JSON group: demo01-consumer-group-DEMO-TOPIC-01 # 消费者分组 binder: rabbit001 # 设置使用的 Binder 名字 # Consumer 配置项,对应 ConsumerProperties 类 consumer: concurrency: 2 # 每个 Consumer 消费线程数的初始大小,默认为 1 # RabbitMQ 自定义 Binding 配置项,对应 RabbitBindingProperties Map rabbit: bindings: demo01-input: # RabbitMQ Consumer 配置项,对应 RabbitConsumerProperties 类 consumer: max-concurrency: 10 # 每个 Consumer 消费线程数的最大大小,默认为 1 server: port: ${random.int[10000,19999]} # 随机端口,方便启动多个消费者 ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-delay/pom.xml ================================================ labx-10-spring-cloud-stream-rabbitmq cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-10-sc-stream-rabbitmq-consumer-delay 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-rabbit ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-delay/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/ConsumerApplication.java ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo; import cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener.MySink; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySink.class) public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener; import cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message.Demo01Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.messaging.handler.annotation.Payload; import org.springframework.stereotype.Component; @Component public class Demo01Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @StreamListener(MySink.DEMO01_INPUT) public void onMessage(@Payload Demo01Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener; import org.springframework.cloud.stream.annotation.Input; import org.springframework.messaging.SubscribableChannel; public interface MySink { String DEMO01_INPUT = "demo01-input"; @Input(DEMO01_INPUT) SubscribableChannel demo01Input(); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-delay/src/main/resources/application.yml ================================================ spring: application: name: demo-consumer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binder 配置项,对应 BinderProperties Map binders: rabbit001: type: rabbit # 设置 Binder 的类型 environment: # 设置 Binder 的环境配置 # 如果是 RabbitMQ 类型的时候,则对应的是 RabbitProperties 类 spring: rabbitmq: host: 127.0.0.1 # RabbitMQ 服务的地址 port: 5672 # RabbitMQ 服务的端口 username: guest # RabbitMQ 服务的账号 password: guest # RabbitMQ 服务的密码 # Binding 配置项,对应 BindingProperties Map bindings: demo01-input: destination: DEMO-TOPIC-02 # 目的地。这里使用 RabbitMQ Exchange content-type: application/json # 内容格式。这里使用 JSON group: demo01-consumer-group-DEMO-TOPIC-01 # 消费者分组 binder: rabbit001 # 设置使用的 Binder 名字 # RabbitMQ 自定义 Binding 配置项,对应 RabbitBindingProperties Map rabbit: bindings: demo01-input: # RabbitMQ Consumer 配置项,对应 RabbitConsumerProperties 类 consumer: delayed-exchange: true # 是否使用 x-delayed-message 类型的 Exchange,即延迟消息,默认为 false server: port: ${random.int[10000,19999]} # 随机端口,方便启动多个消费者 ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-demo/pom.xml ================================================ labx-10-spring-cloud-stream-rabbitmq cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-10-sc-stream-rabbitmq-consumer-demo 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-rabbit ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-demo/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/ConsumerApplication.java ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo; import cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener.MySink; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySink.class) public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener; import cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message.Demo01Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.messaging.handler.annotation.Payload; import org.springframework.stereotype.Component; @Component public class Demo01Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @StreamListener(MySink.DEMO01_INPUT) public void onMessage(@Payload Demo01Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener; import org.springframework.cloud.stream.annotation.Input; import org.springframework.messaging.SubscribableChannel; public interface MySink { String DEMO01_INPUT = "demo01-input"; @Input(DEMO01_INPUT) SubscribableChannel demo01Input(); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-demo/src/main/resources/application.yml ================================================ spring: application: name: demo-consumer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binder 配置项,对应 BinderProperties Map binders: rabbit001: type: rabbit # 设置 Binder 的类型 environment: # 设置 Binder 的环境配置 # 如果是 RabbitMQ 类型的时候,则对应的是 RabbitProperties 类 spring: rabbitmq: host: 127.0.0.1 # RabbitMQ 服务的地址 port: 5672 # RabbitMQ 服务的端口 username: guest # RabbitMQ 服务的账号 password: guest # RabbitMQ 服务的密码 # Binding 配置项,对应 BindingProperties Map bindings: demo01-input: destination: DEMO-TOPIC-01 # 目的地。这里使用 RabbitMQ Exchange content-type: application/json # 内容格式。这里使用 JSON group: demo01-consumer-group-DEMO-TOPIC-01 # 消费者分组 binder: rabbit001 # 设置使用的 Binder 名字 server: port: ${random.int[10000,19999]} # 随机端口,方便启动多个消费者 ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-error-handler/pom.xml ================================================ labx-10-spring-cloud-stream-rabbitmq cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-10-sc-stream-rabbitmq-consumer-error-handler 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-rabbit ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo; import cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener.MySink; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySink.class) public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener; import cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message.Demo01Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.integration.annotation.ServiceActivator; import org.springframework.integration.context.IntegrationContextUtils; import org.springframework.messaging.handler.annotation.Payload; import org.springframework.messaging.support.ErrorMessage; import org.springframework.stereotype.Component; @Component public class Demo01Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @StreamListener(MySink.DEMO01_INPUT) // 对应 DEMO-TOPIC-01.demo01-consumer-group-DEMO-TOPIC-01 public void onMessage(@Payload Demo01Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); // 注意,此处抛出一个 RuntimeException 异常,模拟消费失败 throw new RuntimeException("我就是故意抛出一个异常"); } @ServiceActivator(inputChannel = "DEMO-TOPIC-01.demo01-consumer-group-DEMO-TOPIC-01.errors") public void handleError(ErrorMessage errorMessage) { logger.error("[handleError][payload:{}]", errorMessage.getPayload().getMessage()); logger.error("[handleError][originalMessage:{}]", errorMessage.getOriginalMessage()); logger.error("[handleError][headers:{}]", errorMessage.getHeaders()); } @StreamListener(IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME) // errorChannel public void globalHandleError(ErrorMessage errorMessage) { logger.error("[globalHandleError][payload:{}]", errorMessage.getPayload().getMessage()); logger.error("[globalHandleError][originalMessage:{}]", errorMessage.getOriginalMessage()); logger.error("[globalHandleError][headers:{}]", errorMessage.getHeaders()); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener; import org.springframework.cloud.stream.annotation.Input; import org.springframework.messaging.SubscribableChannel; public interface MySink { String DEMO01_INPUT = "demo01-input"; @Input(DEMO01_INPUT) SubscribableChannel demo01Input(); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-error-handler/src/main/resources/application.yml ================================================ spring: application: name: demo-consumer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binder 配置项,对应 BinderProperties Map binders: rabbit001: type: rabbit # 设置 Binder 的类型 environment: # 设置 Binder 的环境配置 # 如果是 RabbitMQ 类型的时候,则对应的是 RabbitProperties 类 spring: rabbitmq: host: 127.0.0.1 # RabbitMQ 服务的地址 port: 5672 # RabbitMQ 服务的端口 username: guest # RabbitMQ 服务的账号 password: guest # RabbitMQ 服务的密码 # Binding 配置项,对应 BindingProperties Map bindings: demo01-input: destination: DEMO-TOPIC-01 # 目的地。这里使用 RabbitMQ Exchange content-type: application/json # 内容格式。这里使用 JSON group: demo01-consumer-group-DEMO-TOPIC-01 # 消费者分组 binder: rabbit001 # 设置使用的 Binder 名字 # Consumer 配置项,对应 ConsumerProperties 类 consumer: max-attempts: 3 # 重试次数,默认为 3 次。 back-off-initial-interval: 3000 # 重试间隔的初始值,单位毫秒,默认为 1000 back-off-multiplier: 2.0 # 重试间隔的递乘系数,默认为 2.0 back-off-max-interval: 10000 # 重试间隔的最大值,单位毫秒,默认为 10000 # RabbitMQ 自定义 Binding 配置项,对应 RabbitBindingProperties Map rabbit: bindings: demo01-input: # RabbitMQ Consumer 配置项,对应 RabbitConsumerProperties 类 consumer: auto-bind-dlq: true # 是否创建对应的死信队列,并进行绑定,默认为 false。 republish-to-dlq: true # 消费失败的消息发布到对应的死信队列时,是否添加异常异常的信息到消息头 server: port: ${random.int[10000,19999]} # 随机端口,方便启动多个消费者 ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-filter/pom.xml ================================================ labx-10-spring-cloud-stream-rabbitmq cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-10-sc-stream-rabbitmq-consumer-filter 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-rabbit ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-filter/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/ConsumerApplication.java ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo; import cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener.MySink; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySink.class) public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener; import cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message.Demo01Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.messaging.handler.annotation.Payload; import org.springframework.stereotype.Component; @Component public class Demo01Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @StreamListener(value = MySink.DEMO01_INPUT, condition = "headers['tag'] == 'yunai'") public void onMessage(@Payload Demo01Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener; import org.springframework.cloud.stream.annotation.Input; import org.springframework.messaging.SubscribableChannel; public interface MySink { String DEMO01_INPUT = "demo01-input"; @Input(DEMO01_INPUT) SubscribableChannel demo01Input(); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-filter/src/main/resources/application.yml ================================================ spring: application: name: demo-consumer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binder 配置项,对应 BinderProperties Map binders: rabbit001: type: rabbit # 设置 Binder 的类型 environment: # 设置 Binder 的环境配置 # 如果是 RabbitMQ 类型的时候,则对应的是 RabbitProperties 类 spring: rabbitmq: host: 127.0.0.1 # RabbitMQ 服务的地址 port: 5672 # RabbitMQ 服务的端口 username: guest # RabbitMQ 服务的账号 password: guest # RabbitMQ 服务的密码 # Binding 配置项,对应 BindingProperties Map bindings: demo01-input: destination: DEMO-TOPIC-01 # 目的地。这里使用 RabbitMQ Exchange content-type: application/json # 内容格式。这里使用 JSON group: demo01-consumer-group-DEMO-TOPIC-01 # 消费者分组 binder: rabbit001 # 设置使用的 Binder 名字 server: port: ${random.int[10000,19999]} # 随机端口,方便启动多个消费者 ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-partitioning/pom.xml ================================================ labx-10-spring-cloud-stream-rabbitmq cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-10-sc-stream-rabbitmq-consumer-partitioning 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-rabbit ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-partitioning/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/ConsumerApplication.java ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo; import cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener.MySink; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySink.class) public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener; import cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message.Demo01Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.messaging.handler.annotation.Payload; import org.springframework.stereotype.Component; @Component public class Demo01Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @StreamListener(MySink.DEMO01_INPUT) public void onMessage(@Payload Demo01Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener; import org.springframework.cloud.stream.annotation.Input; import org.springframework.messaging.SubscribableChannel; public interface MySink { String DEMO01_INPUT = "demo01-input"; @Input(DEMO01_INPUT) SubscribableChannel demo01Input(); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-partitioning/src/main/resources/application.yml ================================================ spring: application: name: demo-consumer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binder 配置项,对应 BinderProperties Map binders: rabbit001: type: rabbit # 设置 Binder 的类型 environment: # 设置 Binder 的环境配置 # 如果是 RabbitMQ 类型的时候,则对应的是 RabbitProperties 类 spring: rabbitmq: host: 127.0.0.1 # RabbitMQ 服务的地址 port: 5672 # RabbitMQ 服务的端口 username: guest # RabbitMQ 服务的账号 password: guest # RabbitMQ 服务的密码 # Binding 配置项,对应 BindingProperties Map bindings: demo01-input: destination: DEMO-TOPIC-03 # 目的地。这里使用 RabbitMQ Exchange content-type: application/json # 内容格式。这里使用 JSON group: demo01-consumer-group-DEMO-TOPIC-01 # 消费者分组 binder: rabbit001 # 设置使用的 Binder 名字 consumer: partitioned: true instance-index: 1 server: port: ${random.int[10000,19999]} # 随机端口,方便启动多个消费者 ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-retry/pom.xml ================================================ labx-10-spring-cloud-stream-rabbitmq cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-10-sc-stream-rabbitmq-consumer-retry 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-rabbit ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-retry/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/ConsumerApplication.java ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo; import cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener.MySink; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySink.class) public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener; import cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message.Demo01Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.messaging.handler.annotation.Payload; import org.springframework.stereotype.Component; @Component public class Demo01Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @StreamListener(MySink.DEMO01_INPUT) public void onMessage(@Payload Demo01Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); // 注意,此处抛出一个 RuntimeException 异常,模拟消费失败 throw new RuntimeException("我就是故意抛出一个异常"); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener; import org.springframework.cloud.stream.annotation.Input; import org.springframework.messaging.SubscribableChannel; public interface MySink { String DEMO01_INPUT = "demo01-input"; @Input(DEMO01_INPUT) SubscribableChannel demo01Input(); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-retry/src/main/resources/application.yml ================================================ spring: application: name: demo-consumer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binder 配置项,对应 BinderProperties Map binders: rabbit001: type: rabbit # 设置 Binder 的类型 environment: # 设置 Binder 的环境配置 # 如果是 RabbitMQ 类型的时候,则对应的是 RabbitProperties 类 spring: rabbitmq: host: 127.0.0.1 # RabbitMQ 服务的地址 port: 5672 # RabbitMQ 服务的端口 username: guest # RabbitMQ 服务的账号 password: guest # RabbitMQ 服务的密码 # Binding 配置项,对应 BindingProperties Map bindings: demo01-input: destination: DEMO-TOPIC-01 # 目的地。这里使用 RabbitMQ Exchange content-type: application/json # 内容格式。这里使用 JSON group: demo01-consumer-group-DEMO-TOPIC-01 # 消费者分组 binder: rabbit001 # 设置使用的 Binder 名字 # Consumer 配置项,对应 ConsumerProperties 类 consumer: max-attempts: 3 # 重试次数,默认为 3 次。 back-off-initial-interval: 3000 # 重试间隔的初始值,单位毫秒,默认为 1000 back-off-multiplier: 2.0 # 重试间隔的递乘系数,默认为 2.0 back-off-max-interval: 10000 # 重试间隔的最大值,单位毫秒,默认为 10000 # RabbitMQ 自定义 Binding 配置项,对应 RabbitBindingProperties Map rabbit: bindings: demo01-input: # RabbitMQ Consumer 配置项,对应 RabbitConsumerProperties 类 consumer: auto-bind-dlq: true # 是否创建对应的死信队列,并进行绑定,默认为 false。 republish-to-dlq: true # 消费失败的消息发布到对应的死信队列时,是否添加异常异常的信息到消息头 server: port: ${random.int[10000,19999]} # 随机端口,方便启动多个消费者 ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-actuator/pom.xml ================================================ labx-10-spring-cloud-stream-rabbitmq cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-10-sc-stream-rabbitmq-producer-actuator 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-rabbit org.springframework.boot spring-boot-starter-actuator ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-actuator/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/producerdemo/ProducerApplication.java ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo; import cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message.MySource; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySource.class) public class ProducerApplication { public static void main(String[] args) { SpringApplication.run(ProducerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.controller; import cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message.Demo01Message; import cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message.MySource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.Message; import org.springframework.messaging.support.MessageBuilder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Random; @RestController @RequestMapping("/demo01") public class Demo01Controller { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private MySource mySource; @GetMapping("/send") public boolean send() { // 创建 Message Demo01Message message = new Demo01Message() .setId(new Random().nextInt()); // 创建 Spring Message 对象 Message springMessage = MessageBuilder.withPayload(message) .build(); // 发送消息 return mySource.demo01Output().send(springMessage); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message; import org.springframework.cloud.stream.annotation.Output; import org.springframework.messaging.MessageChannel; public interface MySource { @Output("demo01-output") MessageChannel demo01Output(); } ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-actuator/src/main/resources/application.yml ================================================ spring: application: name: demo-producer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binder 配置项,对应 BinderProperties Map binders: rabbit001: type: rabbit # 设置 Binder 的类型 environment: # 设置 Binder 的环境配置 # 如果是 RabbitMQ 类型的时候,则对应的是 RabbitProperties 类 spring: rabbitmq: host: 127.0.0.1 # RabbitMQ 服务的地址 port: 5672 # RabbitMQ 服务的端口 username: guest # RabbitMQ 服务的账号 password: guest # RabbitMQ 服务的密码 # Binding 配置项,对应 BindingProperties Map bindings: demo01-output: destination: DEMO-TOPIC-01 # 目的地。这里使用 RabbitMQ Exchange content-type: application/json # 内容格式。这里使用 JSON binder: rabbit001 # 设置使用的 Binder 名字 server: port: 18080 management: endpoints: web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 endpoint: # Health 端点配置项,对应 HealthProperties 配置类 health: enabled: true # 是否开启。默认为 true 开启。 show-details: ALWAYS # 何时显示完整的健康信息。默认为 NEVER 都不展示。可选 WHEN_AUTHORIZED 当经过授权的用户;可选 ALWAYS 总是展示。 ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-batch/pom.xml ================================================ labx-10-spring-cloud-stream-rabbitmq cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-10-sc-stream-rabbitmq-producer-batch 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-rabbit ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-batch/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/producerdemo/ProducerApplication.java ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo; import cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message.MySource; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySource.class) public class ProducerApplication { public static void main(String[] args) { SpringApplication.run(ProducerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.controller; import cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message.Demo01Message; import cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message.MySource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.Message; import org.springframework.messaging.support.MessageBuilder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Random; @RestController @RequestMapping("/demo01") public class Demo01Controller { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private MySource mySource; @GetMapping("/send_batch") public boolean sendBatch() throws InterruptedException { // 发送 3 条消息,每条中间间隔 10 秒 for (int i = 0; i < 3; i++) { // 创建 Message Demo01Message message = new Demo01Message() .setId(new Random().nextInt()); // 创建 Spring Message 对象 Message springMessage = MessageBuilder.withPayload(message) .build(); // 发送消息 mySource.demo01Output().send(springMessage); // 故意每条消息之间,隔离 10 秒 logger.info("[sendBatch][发送编号:[{}] 发送成功]", message.getId()); Thread.sleep(10 * 1000L); } return true; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message; import org.springframework.cloud.stream.annotation.Output; import org.springframework.messaging.MessageChannel; public interface MySource { @Output("demo01-output") MessageChannel demo01Output(); } ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-batch/src/main/resources/application.yml ================================================ spring: application: name: demo-producer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binder 配置项,对应 BinderProperties Map binders: rabbit001: type: rabbit # 设置 Binder 的类型 environment: # 设置 Binder 的环境配置 # 如果是 RabbitMQ 类型的时候,则对应的是 RabbitProperties 类 spring: rabbitmq: host: 127.0.0.1 # RabbitMQ 服务的地址 port: 5672 # RabbitMQ 服务的端口 username: guest # RabbitMQ 服务的账号 password: guest # RabbitMQ 服务的密码 # Binding 配置项,对应 BindingProperties Map bindings: demo01-output: destination: DEMO-TOPIC-01 # 目的地。这里使用 RabbitMQ Exchange content-type: application/json # 内容格式。这里使用 JSON binder: rabbit001 # 设置使用的 Binder 名字 # RabbitMQ 自定义 Binding 配置项,对应 RabbitBindingProperties Map rabbit: bindings: demo01-output: # RabbitMQ Producer 配置项,对应 RabbitProducerProperties 类 producer: batching-enabled: true # 是否开启批量发送功能,默认为 false batch-size: 100 # 超过收集的消息数量的最大条数,默认为 100 batch-buffer-limit: 10000 # 每次批量发送消息的最大内存,默认为 10000 batch-timeout: 30000 # 超过收集的时间的最大等待时长,单位:毫秒,默认为 5000 server: port: 18080 ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-confirm/pom.xml ================================================ labx-10-spring-cloud-stream-rabbitmq cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-10-sc-stream-rabbitmq-producer-confirm 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-rabbit ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-confirm/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/producerdemo/ProducerApplication.java ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo; import cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message.MySource; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySource.class) public class ProducerApplication { public static void main(String[] args) { SpringApplication.run(ProducerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.controller; import cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message.Demo01Message; import cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message.MySource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.Message; import org.springframework.messaging.support.MessageBuilder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Random; @RestController @RequestMapping("/demo01") public class Demo01Controller { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private MySource mySource; @GetMapping("/send") public boolean send() { // 创建 Message Demo01Message message = new Demo01Message() .setId(new Random().nextInt()); // 创建 Spring Message 对象 Message springMessage = MessageBuilder.withPayload(message) .build(); // 发送消息 return mySource.demo01Output().send(springMessage); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.integration.annotation.ServiceActivator; import org.springframework.messaging.Message; import org.springframework.stereotype.Component; @Component public class Demo01ProducerConfirmCallback { private Logger logger = LoggerFactory.getLogger(getClass()); @ServiceActivator(inputChannel = "demo01-producer-confirm") public void onPublisherConfirm(Message message) { logger.info("[onPublisherConfirm][headers:{}]", message.getHeaders()); logger.info("[onPublisherConfirm][payload:{}]", message.getPayload()); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.integration.annotation.ServiceActivator; import org.springframework.integration.context.IntegrationContextUtils; import org.springframework.messaging.support.ErrorMessage; import org.springframework.stereotype.Component; @Component public class Demo01ProducerReturnCallback { private Logger logger = LoggerFactory.getLogger(getClass()); @ServiceActivator(inputChannel = "DEMO-TOPIC-01.errors") public void handleError(ErrorMessage errorMessage) { logger.error("[handleError][headers:{}]", errorMessage.getHeaders()); logger.error("[handleError][payload:{}]", errorMessage.getPayload().getMessage()); logger.error("[handleError][originalMessage:{}]", errorMessage.getOriginalMessage()); } @StreamListener(IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME) // errorChannel public void globalHandleError(ErrorMessage errorMessage) { logger.error("[globalHandleError][payload:{}]", errorMessage.getPayload().getMessage()); logger.error("[globalHandleError][originalMessage:{}]", errorMessage.getOriginalMessage()); logger.error("[globalHandleError][headers:{}]", errorMessage.getHeaders()); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message; import org.springframework.cloud.stream.annotation.Output; import org.springframework.messaging.MessageChannel; public interface MySource { @Output("demo01-output") MessageChannel demo01Output(); } ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-confirm/src/main/resources/application.yml ================================================ spring: application: name: demo-producer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binder 配置项,对应 BinderProperties Map binders: rabbit001: type: rabbit # 设置 Binder 的类型 environment: # 设置 Binder 的环境配置 # 如果是 RabbitMQ 类型的时候,则对应的是 RabbitProperties 类 spring: rabbitmq: host: 127.0.0.1 # RabbitMQ 服务的地址 port: 5672 # RabbitMQ 服务的端口 username: guest # RabbitMQ 服务的账号 password: guest # RabbitMQ 服务的密码 publisher-returns: true # 设置消息是否回退,默认为 false publisher-confirm-type: simple # 设置开启消息确认模型,默认为 null 不进行确认 # Binding 配置项,对应 BindingProperties Map bindings: demo01-output: destination: DEMO-TOPIC-01 # 目的地。这里使用 RabbitMQ Exchange content-type: application/json # 内容格式。这里使用 JSON binder: rabbit001 # 设置使用的 Binder 名字 producer: error-channel-enabled: true # 是否开启异常 Channel,默认为 false 关闭 # RabbitMQ 自定义 Binding 配置项,对应 RabbitBindingProperties Map rabbit: bindings: demo01-output: # RabbitMQ Producer 配置项,对应 RabbitProducerProperties 类 producer: confirm-ack-channel: demo01-producer-confirm # 设置发送确认的 Channel,默认为 null server: port: 18080 ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-delay/pom.xml ================================================ labx-10-spring-cloud-stream-rabbitmq cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-10-sc-stream-rabbitmq-producer-delay 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-rabbit ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-delay/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/producerdemo/ProducerApplication.java ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo; import cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message.MySource; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySource.class) public class ProducerApplication { public static void main(String[] args) { SpringApplication.run(ProducerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.controller; import cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message.Demo01Message; import cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message.MySource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.Message; import org.springframework.messaging.support.MessageBuilder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Random; @RestController @RequestMapping("/demo01") public class Demo01Controller { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private MySource mySource; @GetMapping("/send_delay") public boolean sendDelay() { // 创建 Message Demo01Message message = new Demo01Message() .setId(new Random().nextInt()); // 创建 Spring Message 对象 Message springMessage = MessageBuilder.withPayload(message) .setHeader("x-delay", 5000) // 设置延迟时间,单位:毫秒 .build(); // 发送消息 boolean sendResult = mySource.demo01Output().send(springMessage); logger.info("[sendDelay][发送消息完成, 结果 = {}]", sendResult); return sendResult; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message; import org.springframework.cloud.stream.annotation.Output; import org.springframework.messaging.MessageChannel; public interface MySource { @Output("demo01-output") MessageChannel demo01Output(); } ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-delay/src/main/resources/application.yml ================================================ spring: application: name: demo-producer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binder 配置项,对应 BinderProperties Map binders: rabbit001: type: rabbit # 设置 Binder 的类型 environment: # 设置 Binder 的环境配置 # 如果是 RabbitMQ 类型的时候,则对应的是 RabbitProperties 类 spring: rabbitmq: host: 127.0.0.1 # RabbitMQ 服务的地址 port: 5672 # RabbitMQ 服务的端口 username: guest # RabbitMQ 服务的账号 password: guest # RabbitMQ 服务的密码 # Binding 配置项,对应 BindingProperties Map bindings: demo01-output: destination: DEMO-TOPIC-02 # 目的地。这里使用 RabbitMQ Exchange content-type: application/json # 内容格式。这里使用 JSON binder: rabbit001 # 设置使用的 Binder 名字 # RabbitMQ 自定义 Binding 配置项,对应 RabbitBindingProperties Map rabbit: bindings: demo01-output: # RabbitMQ Producer 配置项,对应 RabbitProducerProperties 类 producer: delayed-exchange: true # 是否使用 x-delayed-message 类型的 Exchange,即延迟消息,默认为 false server: port: 18080 ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-demo/pom.xml ================================================ labx-10-spring-cloud-stream-rabbitmq cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-10-sc-stream-rabbitmq-producer-demo 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-rabbit ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-demo/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/producerdemo/ProducerApplication.java ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo; import cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message.MySource; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySource.class) public class ProducerApplication { public static void main(String[] args) { SpringApplication.run(ProducerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.controller; import cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message.Demo01Message; import cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message.MySource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.Message; import org.springframework.messaging.support.MessageBuilder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Random; @RestController @RequestMapping("/demo01") public class Demo01Controller { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private MySource mySource; @GetMapping("/send") public boolean send() { // 创建 Message Demo01Message message = new Demo01Message() .setId(new Random().nextInt()); // 创建 Spring Message 对象 Message springMessage = MessageBuilder.withPayload(message) .build(); // 发送消息 return mySource.demo01Output().send(springMessage); } @GetMapping("/send_tag") public boolean sendTag() { for (String tag : new String[]{"yunai", "yutou", "tudou"}) { // 创建 Message Demo01Message message = new Demo01Message() .setId(new Random().nextInt()); // 创建 Spring Message 对象 Message springMessage = MessageBuilder.withPayload(message) .setHeader("tag", tag) // 设置 Tag .build(); // 发送消息 mySource.demo01Output().send(springMessage); } return true; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message; import org.springframework.cloud.stream.annotation.Output; import org.springframework.messaging.MessageChannel; public interface MySource { @Output("demo01-output") MessageChannel demo01Output(); } ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-demo/src/main/resources/application.yml ================================================ spring: application: name: demo-producer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binder 配置项,对应 BinderProperties Map binders: rabbit001: type: rabbit # 设置 Binder 的类型 environment: # 设置 Binder 的环境配置 # 如果是 RabbitMQ 类型的时候,则对应的是 RabbitProperties 类 spring: rabbitmq: host: 127.0.0.1 # RabbitMQ 服务的地址 port: 5672 # RabbitMQ 服务的端口 username: guest # RabbitMQ 服务的账号 password: guest # RabbitMQ 服务的密码 # Binding 配置项,对应 BindingProperties Map bindings: demo01-output: destination: DEMO-TOPIC-01 # 目的地。这里使用 RabbitMQ Exchange content-type: application/json # 内容格式。这里使用 JSON binder: rabbit001 # 设置使用的 Binder 名字 server: port: 18080 ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-partitioning/pom.xml ================================================ labx-10-spring-cloud-stream-rabbitmq cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-10-sc-stream-rabbitmq-producer-partitioning 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-rabbit ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-partitioning/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/producerdemo/ProducerApplication.java ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo; import cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message.MySource; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySource.class) public class ProducerApplication { public static void main(String[] args) { SpringApplication.run(ProducerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.controller; import cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message.Demo01Message; import cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message.MySource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.Message; import org.springframework.messaging.support.MessageBuilder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Random; @RestController @RequestMapping("/demo01") public class Demo01Controller { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private MySource mySource; @GetMapping("/send_orderly") public boolean send() { // 创建 Message Demo01Message message = new Demo01Message() .setId(new Random().nextInt()); // 创建 Spring Message 对象 Message springMessage = MessageBuilder.withPayload(message) .build(); // 发送消息 return mySource.demo01Output().send(springMessage); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message; import org.springframework.cloud.stream.annotation.Output; import org.springframework.messaging.MessageChannel; public interface MySource { @Output("demo01-output") MessageChannel demo01Output(); } ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-partitioning/src/main/resources/application.yml ================================================ spring: application: name: demo-producer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binder 配置项,对应 BinderProperties Map binders: rabbit001: type: rabbit # 设置 Binder 的类型 environment: # 设置 Binder 的环境配置 # 如果是 RabbitMQ 类型的时候,则对应的是 RabbitProperties 类 spring: rabbitmq: host: 127.0.0.1 # RabbitMQ 服务的地址 port: 5672 # RabbitMQ 服务的端口 username: guest # RabbitMQ 服务的账号 password: guest # RabbitMQ 服务的密码 # Binding 配置项,对应 BindingProperties Map bindings: demo01-output: destination: DEMO-TOPIC-03 # 目的地。这里使用 RabbitMQ Exchange content-type: application/json # 内容格式。这里使用 JSON binder: rabbit001 # 设置使用的 Binder 名字 # Producer 配置项,对应 ProducerProperties 类 producer: partition-key-expression: payload['id'] # 分区 key 表达式。该表达式基于 Spring EL,从消息中获得分区 key。 partition-count: 2 # 分区大小,默认为 1 分区 # required-groups: demo01-consumer-group-DEMO-TOPIC-01 #only applicable for rabbit server: port: 18080 ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-transaction/pom.xml ================================================ labx-10-spring-cloud-stream-rabbitmq cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-10-sc-stream-rabbitmq-producer-transaction 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-rabbit ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-transaction/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/producerdemo/ProducerApplication.java ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo; import cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message.MySource; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySource.class) public class ProducerApplication { public static void main(String[] args) { SpringApplication.run(ProducerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.config; import org.springframework.amqp.rabbit.connection.ConnectionFactory; import org.springframework.amqp.rabbit.transaction.RabbitTransactionManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.annotation.EnableTransactionManagement; @Configuration @EnableTransactionManagement public class TransactionConfig { @Bean public RabbitTransactionManager rabbitTransactionManager(ConnectionFactory connectionFactory) { return new RabbitTransactionManager(connectionFactory); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.controller; import cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message.Demo01Message; import cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message.MySource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.Message; import org.springframework.messaging.support.MessageBuilder; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Random; @RestController @RequestMapping("/demo01") public class Demo01Controller { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private MySource mySource; @Transactional @GetMapping("/send_transaction") public void sendTransaction() throws InterruptedException { // 创建 Message int id = new Random().nextInt(); Demo01Message message = new Demo01Message() .setId(id); // 创建 Spring Message 对象 Message springMessage = MessageBuilder.withPayload(message) .build(); // 发送消息 mySource.demo01Output().send(springMessage); logger.info("[syncSend][发送编号:[{}] 发送成功]", id); // 等待 Thread.sleep(10 * 1000L); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message; import org.springframework.cloud.stream.annotation.Output; import org.springframework.messaging.MessageChannel; public interface MySource { @Output("demo01-output") MessageChannel demo01Output(); } ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-transaction/src/main/resources/application.yml ================================================ spring: application: name: demo-producer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binder 配置项,对应 BinderProperties Map binders: rabbit001: type: rabbit # 设置 Binder 的类型 environment: # 设置 Binder 的环境配置 # 如果是 RabbitMQ 类型的时候,则对应的是 RabbitProperties 类 spring: rabbitmq: host: 127.0.0.1 # RabbitMQ 服务的地址 port: 5672 # RabbitMQ 服务的端口 username: guest # RabbitMQ 服务的账号 password: guest # RabbitMQ 服务的密码 # Binding 配置项,对应 BindingProperties Map bindings: demo01-output: destination: DEMO-TOPIC-01 # 目的地。这里使用 RabbitMQ Exchange content-type: application/json # 内容格式。这里使用 JSON binder: rabbit001 # 设置使用的 Binder 名字 # RabbitMQ 自定义 Binding 配置项,对应 RabbitBindingProperties Map rabbit: bindings: demo01-output: # RabbitMQ Producer 配置项,对应 RabbitProducerProperties 类 producer: transacted: true # 是否开启事务功能,默认为 false server: port: 18080 ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-10-spring-cloud-stream-rabbitmq pom labx-10-sc-stream-rabbitmq-producer-demo labx-10-sc-stream-rabbitmq-consumer-demo labx-10-sc-stream-rabbitmq-producer-delay labx-10-sc-stream-rabbitmq-consumer-delay labx-10-sc-stream-rabbitmq-consumer-retry labx-10-sc-stream-rabbitmq-consumer-error-handler labx-10-sc-stream-rabbitmq-consumer-broadcasting labx-10-sc-stream-rabbitmq-producer-partitioning labx-10-sc-stream-rabbitmq-consumer-partitioning labx-10-sc-stream-rabbitmq-consumer-concurrency labx-10-sc-stream-rabbitmq-consumer-filter labx-10-sc-stream-rabbitmq-producer-transaction labx-10-sc-stream-rabbitmq-consumer-ack labx-10-sc-stream-rabbitmq-producer-confirm labx-10-sc-stream-rabbitmq-producer-batch labx-10-sc-stream-rabbitmq-consumer-batch labx-10-sc-stream-rabbitmq-producer-actuator labx-10-sc-stream-rabbitmq-consumer-actuator ================================================ FILE: labx-10-spring-cloud-stream-rabbitmq/《芋道 Spring Cloud 消息队列 RabbitMQ 入门》.md ================================================ ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-ack/pom.xml ================================================ labx-11-spring-cloud-stream-kafka cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-11-sc-stream-kafka-consumer-ack 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-kafka ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-ack/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/ConsumerApplication.java ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo; import cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener.MySink; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySink.class) public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener; import cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.message.Demo01Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.kafka.support.Acknowledgment; import org.springframework.kafka.support.KafkaHeaders; import org.springframework.messaging.handler.annotation.Header; import org.springframework.messaging.handler.annotation.Payload; import org.springframework.stereotype.Component; import java.util.concurrent.atomic.AtomicInteger; @Component public class Demo01Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); private AtomicInteger index = new AtomicInteger(); @StreamListener(MySink.DEMO01_INPUT) public void onMessage(@Payload Demo01Message message, @Header(KafkaHeaders.ACKNOWLEDGMENT) Acknowledgment acknowledgment) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); // 提交消费进度 // if (message.getId() % 2 == 1) { if (index.incrementAndGet() == 1) { acknowledgment.acknowledge(); } } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener; import org.springframework.cloud.stream.annotation.Input; import org.springframework.messaging.SubscribableChannel; public interface MySink { String DEMO01_INPUT = "demo01-input"; @Input(DEMO01_INPUT) SubscribableChannel demo01Input(); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-ack/src/main/resources/application.yml ================================================ spring: application: name: demo-consumer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binder 配置项,对应 BinderProperties Map # binders: # Binding 配置项,对应 BindingProperties Map bindings: demo01-input: destination: DEMO-TOPIC-01 # 目的地。这里使用 Kafka Topic content-type: application/json # 内容格式。这里使用 JSON group: demo01-consumer-group # 消费者分组 # Spring Cloud Stream Kafka 配置项 kafka: # Kafka Binder 配置项,对应 KafkaBinderConfigurationProperties 类 binder: brokers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 # Kafka Binding 配置项,对应 KafkaBindingProperties 类 bindings: demo01-input: # Kafka Consumer 配置项,对应 KafkaConsumerProperties 类 consumer: auto-commit-offset: false # 是否自动提交消费进度,默认为 true 自动提交。 ack-each-record: true # 是否每一条消息都进行提交消费进度,默认为 false 在每一批消费完成后一起提交。 server: port: ${random.int[10000,19999]} # 随机端口,方便启动多个消费者 ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-actuator/pom.xml ================================================ labx-11-spring-cloud-stream-kafka cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-11-sc-stream-kafka-consumer-actuator 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-kafka org.springframework.boot spring-boot-starter-actuator ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-actuator/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/ConsumerApplication.java ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo; import cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener.MySink; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySink.class) public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener; import cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.message.Demo01Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.messaging.handler.annotation.Payload; import org.springframework.stereotype.Component; @Component public class Demo01Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @StreamListener(MySink.DEMO01_INPUT) public void onMessage(@Payload Demo01Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener; import org.springframework.cloud.stream.annotation.Input; import org.springframework.messaging.SubscribableChannel; public interface MySink { String DEMO01_INPUT = "demo01-input"; @Input(DEMO01_INPUT) SubscribableChannel demo01Input(); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-actuator/src/main/resources/application.yml ================================================ spring: application: name: demo-consumer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binder 配置项,对应 BinderProperties Map # binders: # Binding 配置项,对应 BindingProperties Map bindings: demo01-input: destination: DEMO-TOPIC-01 # 目的地。这里使用 Kafka Topic content-type: application/json # 内容格式。这里使用 JSON group: demo01-consumer-group # 消费者分组 # Spring Cloud Stream Kafka 配置项 kafka: # Kafka Binder 配置项,对应 KafkaBinderConfigurationProperties 类 binder: brokers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 server: port: ${random.int[10000,19999]} # 随机端口,方便启动多个消费者 management: endpoints: web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 endpoint: # Health 端点配置项,对应 HealthProperties 配置类 health: enabled: true # 是否开启。默认为 true 开启。 show-details: ALWAYS # 何时显示完整的健康信息。默认为 NEVER 都不展示。可选 WHEN_AUTHORIZED 当经过授权的用户;可选 ALWAYS 总是展示。 ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-batch/pom.xml ================================================ labx-11-spring-cloud-stream-kafka cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-11-sc-stream-kafka-consumer-batch 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-kafka ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-batch/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/ConsumerApplication.java ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo; import cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener.MySink; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySink.class) public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener; import cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.message.Demo01Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.messaging.handler.annotation.Payload; import org.springframework.stereotype.Component; import java.util.List; @Component public class Demo01Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @StreamListener(MySink.DEMO01_INPUT) public void onMessage(@Payload List messages) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), messages); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener; import org.springframework.cloud.stream.annotation.Input; import org.springframework.messaging.SubscribableChannel; public interface MySink { String DEMO01_INPUT = "demo01-input"; @Input(DEMO01_INPUT) SubscribableChannel demo01Input(); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-batch/src/main/resources/application.yml ================================================ spring: application: name: demo-consumer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binder 配置项,对应 BinderProperties Map # binders: # Binding 配置项,对应 BindingProperties Map bindings: demo01-input: destination: DEMO-TOPIC-01 # 目的地。这里使用 Kafka Topic content-type: application/json # 内容格式。这里使用 JSON group: demo01-consumer-group # 消费者分组 # Consumer 配置项,对应 ConsumerProperties 类 consumer: batch-mode: true # 是否批量消费默认,默认为 false # Spring Cloud Stream Kafka 配置项 kafka: # Kafka Binder 配置项,对应 KafkaBinderConfigurationProperties 类 binder: brokers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 configuration: fetch.max.wait.ms: 10000 # poll 一次拉取的阻塞的最大时长,单位:毫秒。这里指的是阻塞拉取需要满足至少 fetch-min-size 大小的消息 fetch.min.bytes: 1024 # poll 一次消息拉取的最小数据量,单位:字节 max.poll.records: 100 # poll 一次消息拉取的最大数量 server: port: ${random.int[10000,19999]} # 随机端口,方便启动多个消费者 ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-broadcasting/pom.xml ================================================ labx-11-spring-cloud-stream-kafka cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-11-sc-stream-kafka-consumer-broadcasting 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-kafka ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-broadcasting/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/ConsumerApplication.java ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo; import cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener.MySink; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySink.class) public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener; import cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.message.Demo01Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.messaging.handler.annotation.Payload; import org.springframework.stereotype.Component; @Component public class Demo01Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @StreamListener(MySink.DEMO01_INPUT) public void onMessage(@Payload Demo01Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener; import org.springframework.cloud.stream.annotation.Input; import org.springframework.messaging.SubscribableChannel; public interface MySink { String DEMO01_INPUT = "demo01-input"; @Input(DEMO01_INPUT) SubscribableChannel demo01Input(); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-broadcasting/src/main/resources/application.yml ================================================ spring: application: name: demo-consumer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binder 配置项,对应 BinderProperties Map # binders: # Binding 配置项,对应 BindingProperties Map bindings: demo01-input: destination: DEMO-TOPIC-01 # 目的地。这里使用 Kafka Topic content-type: application/json # 内容格式。这里使用 JSON # group: demo01-consumer-group # 消费者分组 # Spring Cloud Stream Kafka 配置项 kafka: # Kafka Binder 配置项,对应 KafkaBinderConfigurationProperties 类 binder: brokers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 server: port: ${random.int[10000,19999]} # 随机端口,方便启动多个消费者 ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-concurrency/pom.xml ================================================ labx-11-spring-cloud-stream-kafka cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-11-sc-stream-kafka-consumer-concurrency 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-kafka ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-concurrency/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/ConsumerApplication.java ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo; import cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener.MySink; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySink.class) public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener; import cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.message.Demo01Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.messaging.handler.annotation.Payload; import org.springframework.stereotype.Component; @Component public class Demo01Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @StreamListener(MySink.DEMO01_INPUT) public void onMessage(@Payload Demo01Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener; import org.springframework.cloud.stream.annotation.Input; import org.springframework.messaging.SubscribableChannel; public interface MySink { String DEMO01_INPUT = "demo01-input"; @Input(DEMO01_INPUT) SubscribableChannel demo01Input(); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-concurrency/src/main/resources/application.yml ================================================ spring: application: name: demo-consumer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binder 配置项,对应 BinderProperties Map # binders: # Binding 配置项,对应 BindingProperties Map bindings: demo01-input: destination: DEMO-TOPIC-01 # 目的地。这里使用 Kafka Topic content-type: application/json # 内容格式。这里使用 JSON group: demo01-consumer-group # 消费者分组 # Consumer 配置项,对应 ConsumerProperties 类 consumer: concurrency: 2 # 每个 Consumer 消费线程数的初始大小,默认为 1 # Spring Cloud Stream Kafka 配置项 kafka: # Kafka Binder 配置项,对应 KafkaBinderConfigurationProperties 类 binder: brokers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 server: port: ${random.int[10000,19999]} # 随机端口,方便启动多个消费者 ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-demo/pom.xml ================================================ labx-11-spring-cloud-stream-kafka cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-11-sc-stream-kafka-consumer-demo 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-kafka ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-demo/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/ConsumerApplication.java ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo; import cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener.MySink; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySink.class) public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener; import cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.message.Demo01Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.messaging.handler.annotation.Payload; import org.springframework.stereotype.Component; @Component public class Demo01Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @StreamListener(MySink.DEMO01_INPUT) public void onMessage(@Payload Demo01Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener; import org.springframework.cloud.stream.annotation.Input; import org.springframework.messaging.SubscribableChannel; public interface MySink { String DEMO01_INPUT = "demo01-input"; @Input(DEMO01_INPUT) SubscribableChannel demo01Input(); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-demo/src/main/resources/application.yml ================================================ spring: application: name: demo-consumer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binder 配置项,对应 BinderProperties Map # binders: # Binding 配置项,对应 BindingProperties Map bindings: demo01-input: destination: DEMO-TOPIC-01 # 目的地。这里使用 Kafka Topic content-type: application/json # 内容格式。这里使用 JSON group: demo01-consumer-group # 消费者分组 # Spring Cloud Stream Kafka 配置项 kafka: # Kafka Binder 配置项,对应 KafkaBinderConfigurationProperties 类 binder: brokers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 server: port: ${random.int[10000,19999]} # 随机端口,方便启动多个消费者 ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-error-handler/pom.xml ================================================ labx-11-spring-cloud-stream-kafka cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-11-sc-stream-kafka-consumer-error-handler 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-kafka ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo; import cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener.MySink; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySink.class) public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener; import cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.message.Demo01Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.integration.annotation.ServiceActivator; import org.springframework.integration.context.IntegrationContextUtils; import org.springframework.messaging.handler.annotation.Payload; import org.springframework.messaging.support.ErrorMessage; import org.springframework.stereotype.Component; @Component public class Demo01Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @StreamListener(MySink.DEMO01_INPUT) // 对应 DEMO-TOPIC-01.demo01-consumer-group public void onMessage(@Payload Demo01Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); // 注意,此处抛出一个 RuntimeException 异常,模拟消费失败 throw new RuntimeException("我就是故意抛出一个异常"); } @ServiceActivator(inputChannel = "DEMO-TOPIC-01.demo01-consumer-group.errors") public void handleError(ErrorMessage errorMessage) { logger.error("[handleError][payload:{}]", errorMessage.getPayload().getMessage()); logger.error("[handleError][originalMessage:{}]", errorMessage.getOriginalMessage()); logger.error("[handleError][headers:{}]", errorMessage.getHeaders()); } @StreamListener(IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME) // errorChannel public void globalHandleError(ErrorMessage errorMessage) { logger.error("[globalHandleError][payload:{}]", errorMessage.getPayload().getMessage()); logger.error("[globalHandleError][originalMessage:{}]", errorMessage.getOriginalMessage()); logger.error("[globalHandleError][headers:{}]", errorMessage.getHeaders()); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener; import org.springframework.cloud.stream.annotation.Input; import org.springframework.messaging.SubscribableChannel; public interface MySink { String DEMO01_INPUT = "demo01-input"; @Input(DEMO01_INPUT) SubscribableChannel demo01Input(); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-error-handler/src/main/resources/application.yml ================================================ spring: application: name: demo-consumer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binder 配置项,对应 BinderProperties Map # binders: # Binding 配置项,对应 BindingProperties Map bindings: demo01-input: destination: DEMO-TOPIC-01 # 目的地。这里使用 Kafka Topic content-type: application/json # 内容格式。这里使用 JSON group: demo01-consumer-group # 消费者分组 # Consumer 配置项,对应 ConsumerProperties 类 consumer: max-attempts: 3 # 重试次数,默认为 3 次。 back-off-initial-interval: 3000 # 重试间隔的初始值,单位毫秒,默认为 1000 back-off-multiplier: 2.0 # 重试间隔的递乘系数,默认为 2.0 back-off-max-interval: 10000 # 重试间隔的最大值,单位毫秒,默认为 10000 # Spring Cloud Stream Kafka 配置项 kafka: # Kafka Binder 配置项,对应 KafkaBinderConfigurationProperties 类 binder: brokers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 # Kafka Binding 配置项,对应 KafkaBindingProperties 类 bindings: demo01-input: # Kafka Consumer 配置项,对应 KafkaConsumerProperties 类 consumer: enable-dlq: true # 是否开启死信队列,默认为 false 关闭 dlq-name: # 死信队列名,默认为 `errors.{topicName}.{consumerGroup}` server: port: ${random.int[10000,19999]} # 随机端口,方便启动多个消费者 ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-filter/pom.xml ================================================ labx-11-spring-cloud-stream-kafka cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-11-sc-stream-kafka-consumer-filter 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-kafka ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-filter/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/ConsumerApplication.java ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo; import cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener.MySink; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySink.class) public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener; import cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.message.Demo01Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.messaging.handler.annotation.Payload; import org.springframework.stereotype.Component; @Component public class Demo01Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @StreamListener(value = MySink.DEMO01_INPUT, condition = "headers['tag'] == 'yunai'") public void onMessage(@Payload Demo01Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener; import org.springframework.cloud.stream.annotation.Input; import org.springframework.messaging.SubscribableChannel; public interface MySink { String DEMO01_INPUT = "demo01-input"; @Input(DEMO01_INPUT) SubscribableChannel demo01Input(); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-filter/src/main/resources/application.yml ================================================ spring: application: name: demo-consumer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binder 配置项,对应 BinderProperties Map # binders: # Binding 配置项,对应 BindingProperties Map bindings: demo01-input: destination: DEMO-TOPIC-01 # 目的地。这里使用 Kafka Topic content-type: application/json # 内容格式。这里使用 JSON group: demo01-consumer-group # 消费者分组 # Spring Cloud Stream Kafka 配置项 kafka: # Kafka Binder 配置项,对应 KafkaBinderConfigurationProperties 类 binder: brokers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 server: port: ${random.int[10000,19999]} # 随机端口,方便启动多个消费者 ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-partitioning/pom.xml ================================================ labx-11-spring-cloud-stream-kafka cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-11-sc-stream-kafka-consumer-partitioning 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-kafka ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-partitioning/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/ConsumerApplication.java ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo; import cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener.MySink; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySink.class) public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.messaging.Message; import org.springframework.stereotype.Component; @Component public class Demo01Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @StreamListener(MySink.DEMO01_INPUT) public void onMessage(Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener; import org.springframework.cloud.stream.annotation.Input; import org.springframework.messaging.SubscribableChannel; public interface MySink { String DEMO01_INPUT = "demo01-input"; @Input(DEMO01_INPUT) SubscribableChannel demo01Input(); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-partitioning/src/main/resources/application.yml ================================================ spring: application: name: demo-consumer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binder 配置项,对应 BinderProperties Map # binders: # Binding 配置项,对应 BindingProperties Map bindings: demo01-input: destination: DEMO-TOPIC-01 # 目的地。这里使用 Kafka Topic content-type: application/json # 内容格式。这里使用 JSON group: demo01-consumer-group # 消费者分组 # Consumer 配置项,对应 ConsumerProperties 类 consumer: concurrency: 2 # 每个 Consumer 消费线程数的初始大小,默认为 1 # Spring Cloud Stream Kafka 配置项 kafka: # Kafka Binder 配置项,对应 KafkaBinderConfigurationProperties 类 binder: brokers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 server: port: ${random.int[10000,19999]} # 随机端口,方便启动多个消费者 ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-retry/pom.xml ================================================ labx-11-spring-cloud-stream-kafka cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-11-sc-stream-kafka-consumer-retry 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-kafka ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-retry/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/ConsumerApplication.java ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo; import cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener.MySink; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySink.class) public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener; import cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.message.Demo01Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.messaging.handler.annotation.Payload; import org.springframework.stereotype.Component; @Component public class Demo01Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @StreamListener(MySink.DEMO01_INPUT) public void onMessage(@Payload Demo01Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); // 注意,此处抛出一个 RuntimeException 异常,模拟消费失败 throw new RuntimeException("我就是故意抛出一个异常"); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener; import org.springframework.cloud.stream.annotation.Input; import org.springframework.messaging.SubscribableChannel; public interface MySink { String DEMO01_INPUT = "demo01-input"; @Input(DEMO01_INPUT) SubscribableChannel demo01Input(); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-retry/src/main/resources/application.yml ================================================ spring: application: name: demo-consumer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binder 配置项,对应 BinderProperties Map # binders: # Binding 配置项,对应 BindingProperties Map bindings: demo01-input: destination: DEMO-TOPIC-01 # 目的地。这里使用 Kafka Topic content-type: application/json # 内容格式。这里使用 JSON group: demo01-consumer-group # 消费者分组 # Consumer 配置项,对应 ConsumerProperties 类 consumer: max-attempts: 3 # 重试次数,默认为 3 次。 back-off-initial-interval: 3000 # 重试间隔的初始值,单位毫秒,默认为 1000 back-off-multiplier: 2.0 # 重试间隔的递乘系数,默认为 2.0 back-off-max-interval: 10000 # 重试间隔的最大值,单位毫秒,默认为 10000 # Spring Cloud Stream Kafka 配置项 kafka: # Kafka Binder 配置项,对应 KafkaBinderConfigurationProperties 类 binder: brokers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 # Kafka Binding 配置项,对应 KafkaBindingProperties 类 bindings: demo01-input: # Kafka Consumer 配置项,对应 KafkaConsumerProperties 类 consumer: enable-dlq: true # 是否开启死信队列,默认为 false 关闭 dlq-name: # 死信队列名,默认为 `errors.{topicName}.{consumerGroup}` server: port: ${random.int[10000,19999]} # 随机端口,方便启动多个消费者 ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-transaction/pom.xml ================================================ labx-11-spring-cloud-stream-kafka cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-11-sc-stream-kafka-consumer-transaction 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-kafka ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-transaction/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/ConsumerApplication.java ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo; import cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener.MySink; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySink.class) public class ConsumerApplication { // @Bean // public ListenerContainerCustomizer> customizer() { // return (container, dest, group) -> { // KafkaTransactionManager tm = (KafkaTransactionManager) container.getContainerProperties() // .getTransactionManager(); // tm.setTransactionSynchronization(AbstractPlatformTransactionManager.SYNCHRONIZATION_ON_ACTUAL_TRANSACTION); // }; // } public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener; import cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.message.Demo01Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.messaging.handler.annotation.Payload; import org.springframework.stereotype.Component; @Component public class Demo01Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @StreamListener(MySink.DEMO01_INPUT) public void onMessage(@Payload Demo01Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener; import org.springframework.cloud.stream.annotation.Input; import org.springframework.messaging.SubscribableChannel; public interface MySink { String DEMO01_INPUT = "demo01-input"; @Input(DEMO01_INPUT) SubscribableChannel demo01Input(); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-transaction/src/main/resources/application.yml ================================================ spring: application: name: demo-consumer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binder 配置项,对应 BinderProperties Map # binders: # Binding 配置项,对应 BindingProperties Map bindings: demo01-input: destination: DEMO-TOPIC-01 # 目的地。这里使用 Kafka Topic content-type: application/json # 内容格式。这里使用 JSON group: demo01-consumer-group # 消费者分组 # Spring Cloud Stream Kafka 配置项 kafka: # Kafka Binder 配置项,对应 KafkaBinderConfigurationProperties 类 binder: brokers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 # Kafka Binding 配置项,对应 KafkaBindingProperties 类 bindings: demo01-input: # Kafka Consumer 配置项,对应 KafkaConsumerProperties 类 consumer: configuration: isolation: level: read_committed # 读取已提交的消息 server: port: ${random.int[10000,19999]} # 随机端口,方便启动多个消费者 ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-actuator/pom.xml ================================================ labx-11-spring-cloud-stream-kafka cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-11-sc-stream-kafka-producer-actuator 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-kafka org.springframework.boot spring-boot-starter-actuator ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-actuator/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/kafkademo/ProducerApplication.java ================================================ package cn.iocoder.springcloud.labx11.kafkademo.kafkademo; import cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message.MySource; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySource.class) public class ProducerApplication { public static void main(String[] args) { SpringApplication.run(ProducerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.kafkademo.controller; import cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message.Demo01Message; import cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message.MySource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.Message; import org.springframework.messaging.support.MessageBuilder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Random; @RestController @RequestMapping("/demo01") public class Demo01Controller { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private MySource mySource; @GetMapping("/send") public boolean send() { // 创建 Message Demo01Message message = new Demo01Message() .setId(new Random().nextInt()); // 创建 Spring Message 对象 Message springMessage = MessageBuilder.withPayload(message) .build(); // 发送消息 boolean result = mySource.demo01Output().send(springMessage); logger.info("[send][发送编号:[{}] 发送成功]", message.getId()); return result; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message; import org.springframework.cloud.stream.annotation.Output; import org.springframework.messaging.MessageChannel; public interface MySource { @Output("demo01-output") MessageChannel demo01Output(); } ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-actuator/src/main/resources/application.yml ================================================ spring: application: name: demo-producer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binder 配置项,对应 BinderProperties Map # binders: # Binding 配置项,对应 BindingProperties Map bindings: demo01-output: destination: DEMO-TOPIC-01 # 目的地。这里使用 Kafka Topic content-type: application/json # 内容格式。这里使用 JSON # Spring Cloud Stream Kafka 配置项 kafka: # Kafka Binder 配置项,对应 KafkaBinderConfigurationProperties 类 binder: brokers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 # Kafka 自定义 Binding 配置项,对应 KafkaBindingProperties Map bindings: demo01-output: # Kafka Producer 配置项,对应 KafkaProducerProperties 类 producer: sync: true # 是否同步发送消息,默认为 false 异步。 server: port: 18080 management: endpoints: web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 endpoint: # Health 端点配置项,对应 HealthProperties 配置类 health: enabled: true # 是否开启。默认为 true 开启。 show-details: ALWAYS # 何时显示完整的健康信息。默认为 NEVER 都不展示。可选 WHEN_AUTHORIZED 当经过授权的用户;可选 ALWAYS 总是展示。 ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-batch/pom.xml ================================================ labx-11-spring-cloud-stream-kafka cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-11-sc-stream-kafka-producer-batch 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-kafka ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-batch/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/kafkademo/ProducerApplication.java ================================================ package cn.iocoder.springcloud.labx11.kafkademo.kafkademo; import cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message.MySource; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySource.class) public class ProducerApplication { public static void main(String[] args) { SpringApplication.run(ProducerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.kafkademo.controller; import cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message.Demo01Message; import cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message.MySource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.Message; import org.springframework.messaging.support.MessageBuilder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Random; @RestController @RequestMapping("/demo01") public class Demo01Controller { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private MySource mySource; @GetMapping("/send_batch") public boolean sendBatch() { for (int i = 0; i < 3; i++) { // 创建 Message int id = new Random().nextInt(); Demo01Message message = new Demo01Message() .setId(id); // 创建 Spring Message 对象 Message springMessage = MessageBuilder.withPayload(message) .build(); // 发送消息 mySource.demo01Output().send(springMessage); logger.info("[send_batch][发送编号:[{}] 发送成功]", id); } return true; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message; import org.springframework.cloud.stream.annotation.Output; import org.springframework.messaging.MessageChannel; public interface MySource { @Output("demo01-output") MessageChannel demo01Output(); } ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-batch/src/main/resources/application.yml ================================================ spring: application: name: demo-producer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binder 配置项,对应 BinderProperties Map # binders: # Binding 配置项,对应 BindingProperties Map bindings: demo01-output: destination: DEMO-TOPIC-01 # 目的地。这里使用 Kafka Topic content-type: application/json # 内容格式。这里使用 JSON # Spring Cloud Stream Kafka 配置项 kafka: # Kafka Binder 配置项,对应 KafkaBinderConfigurationProperties 类 binder: brokers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 # Kafka 自定义 Binding 配置项,对应 KafkaBindingProperties Map bindings: demo01-output: # Kafka Producer 配置项,对应 KafkaProducerProperties 类 producer: batch-timeout: 30000 # 批处理延迟时间上限。这里配置为 30 * 1000 ms 过后,不管是否消息数量是否到达 batch-size 或者消息大小到达 buffer-memory 后,都直接发送一次请求 buffer-size: 33554432 # 每次批量发送消息的最大内存 server: port: 18080 ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-demo/pom.xml ================================================ labx-11-spring-cloud-stream-kafka cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-11-sc-stream-kafka-producer-demo 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-kafka ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-demo/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/kafkademo/ProducerApplication.java ================================================ package cn.iocoder.springcloud.labx11.kafkademo.kafkademo; import cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message.MySource; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySource.class) public class ProducerApplication { public static void main(String[] args) { SpringApplication.run(ProducerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.kafkademo.controller; import cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message.Demo01Message; import cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message.MySource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.Message; import org.springframework.messaging.support.MessageBuilder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Random; @RestController @RequestMapping("/demo01") public class Demo01Controller { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private MySource mySource; @GetMapping("/send") public boolean send() { // 创建 Message Demo01Message message = new Demo01Message() .setId(new Random().nextInt()); // 创建 Spring Message 对象 Message springMessage = MessageBuilder.withPayload(message) .build(); // 发送消息 boolean result = mySource.demo01Output().send(springMessage); logger.info("[send][发送编号:[{}] 发送成功]", message.getId()); return result; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message; import org.springframework.cloud.stream.annotation.Output; import org.springframework.messaging.MessageChannel; public interface MySource { @Output("demo01-output") MessageChannel demo01Output(); } ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-demo/src/main/resources/application.yml ================================================ spring: application: name: demo-producer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binder 配置项,对应 BinderProperties Map # binders: # Binding 配置项,对应 BindingProperties Map bindings: demo01-output: destination: DEMO-TOPIC-01 # 目的地。这里使用 Kafka Topic content-type: application/json # 内容格式。这里使用 JSON # Spring Cloud Stream Kafka 配置项 kafka: # Kafka Binder 配置项,对应 KafkaBinderConfigurationProperties 类 binder: brokers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 # Kafka 自定义 Binding 配置项,对应 KafkaBindingProperties Map bindings: demo01-output: # Kafka Producer 配置项,对应 KafkaProducerProperties 类 producer: sync: true # 是否同步发送消息,默认为 false 异步。 server: port: 18080 ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-partitioning/pom.xml ================================================ labx-11-spring-cloud-stream-kafka cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-11-sc-stream-kafka-producer-partitioning 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-kafka ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-partitioning/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/kafkademo/ProducerApplication.java ================================================ package cn.iocoder.springcloud.labx11.kafkademo.kafkademo; import cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message.MySource; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySource.class) public class ProducerApplication { public static void main(String[] args) { SpringApplication.run(ProducerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.kafkademo.controller; import cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message.Demo01Message; import cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message.MySource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.Message; import org.springframework.messaging.support.MessageBuilder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Random; @RestController @RequestMapping("/demo01") public class Demo01Controller { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private MySource mySource; @GetMapping("/send_orderly") public boolean sendOrderly() { // 发送 3 条相同 id 的消息 int id = new Random().nextInt(); for (int i = 0; i < 3; i++) { // 创建 Message Demo01Message message = new Demo01Message().setId(id); // 创建 Spring Message 对象 Message springMessage = MessageBuilder.withPayload(message) .build(); // 发送消息 mySource.demo01Output().send(springMessage); } return true; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message; import org.springframework.cloud.stream.annotation.Output; import org.springframework.messaging.MessageChannel; public interface MySource { @Output("demo01-output") MessageChannel demo01Output(); } ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-partitioning/src/main/resources/application.yml ================================================ spring: application: name: demo-producer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binder 配置项,对应 BinderProperties Map # binders: # Binding 配置项,对应 BindingProperties Map bindings: demo01-output: destination: DEMO-TOPIC-01 # 目的地。这里使用 Kafka Topic content-type: application/json # 内容格式。这里使用 JSON # Producer 配置项,对应 ProducerProperties 类 producer: partition-key-expression: payload['id'] # 分区 key 表达式。该表达式基于 Spring EL,从消息中获得分区 key。 # Spring Cloud Stream Kafka 配置项 kafka: # Kafka Binder 配置项,对应 KafkaBinderConfigurationProperties 类 binder: brokers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 # Kafka 自定义 Binding 配置项,对应 KafkaBindingProperties Map bindings: demo01-output: # Kafka Producer 配置项,对应 KafkaProducerProperties 类 producer: sync: true # 是否同步发送消息,默认为 false 异步。 server: port: 18080 ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-transaction/pom.xml ================================================ labx-11-spring-cloud-stream-kafka cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-11-sc-stream-kafka-producer-transaction 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-kafka ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-transaction/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/kafkademo/ProducerApplication.java ================================================ package cn.iocoder.springcloud.labx11.kafkademo.kafkademo; import cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message.MySource; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySource.class) public class ProducerApplication { public static void main(String[] args) { SpringApplication.run(ProducerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.kafkademo.config; import org.springframework.cloud.stream.binder.BinderFactory; import org.springframework.cloud.stream.binder.kafka.KafkaMessageChannelBinder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.kafka.core.ProducerFactory; import org.springframework.kafka.transaction.KafkaTransactionManager; import org.springframework.messaging.MessageChannel; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; @Configuration @EnableTransactionManagement public class TransactionConfig { @Bean public PlatformTransactionManager transactionManager(BinderFactory binders) { // 获得 Kafka ProducerFactory 对象 ProducerFactory pf = ((KafkaMessageChannelBinder) binders.getBinder(null, MessageChannel.class)).getTransactionalProducerFactory(); // 创建 KafkaTransactionManager 事务管理器 assert pf != null; return new KafkaTransactionManager<>(pf); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.kafkademo.controller; import cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message.Demo01Message; import cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message.MySource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.Message; import org.springframework.messaging.support.MessageBuilder; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Random; @RestController @RequestMapping("/demo01") public class Demo01Controller { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private MySource mySource; @Transactional @GetMapping("/send_transaction") public void sendTransaction() throws InterruptedException { // 创建 Message int id = new Random().nextInt(); Demo01Message message = new Demo01Message() .setId(id); // 创建 Spring Message 对象 Message springMessage = MessageBuilder.withPayload(message) .build(); // 发送消息 mySource.demo01Output().send(springMessage); logger.info("[send_transaction][发送编号:[{}] 发送成功]", id); // 等待 Thread.sleep(10 * 1000L); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message; import org.springframework.cloud.stream.annotation.Output; import org.springframework.messaging.MessageChannel; public interface MySource { @Output("demo01-output") MessageChannel demo01Output(); } ================================================ FILE: labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-transaction/src/main/resources/application.yml ================================================ spring: application: name: demo-producer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binder 配置项,对应 BinderProperties Map # binders: # Binding 配置项,对应 BindingProperties Map bindings: demo01-output: destination: DEMO-TOPIC-01 # 目的地。这里使用 Kafka Topic content-type: application/json # 内容格式。这里使用 JSON # Spring Cloud Stream Kafka 配置项 kafka: # Kafka Binder 配置项,对应 KafkaBinderConfigurationProperties 类 binder: brokers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 transaction: transaction-id-prefix: demo. # 事务编号前缀 producer: configuration: retries: 1 # 发送失败时,重试发送的次数 acks: all # 0-不应答。1-leader 应答。all-所有 leader 和 follower 应答。 # Kafka 自定义 Binding 配置项,对应 KafkaBindingProperties Map bindings: demo01-output: # Kafka Producer 配置项,对应 KafkaProducerProperties 类 producer: sync: true # 是否同步发送消息,默认为 false 异步。 server: port: 18080 ================================================ FILE: labx-11-spring-cloud-stream-kafka/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-11-spring-cloud-stream-kafka pom labx-11-sc-stream-kafka-producer-demo labx-11-sc-stream-kafka-consumer-demo labx-11-sc-stream-kafka-consumer-retry labx-11-sc-stream-kafka-consumer-error-handler labx-11-sc-stream-kafka-consumer-broadcasting labx-11-sc-stream-kafka-consumer-concurrency labx-11-sc-stream-kafka-producer-partitioning labx-11-sc-stream-kafka-consumer-partitioning labx-11-sc-stream-kafka-consumer-filter labx-11-sc-stream-kafka-producer-transaction labx-11-sc-stream-kafka-consumer-transaction labx-11-sc-stream-kafka-consumer-ack labx-11-sc-stream-kafka-producer-batch labx-11-sc-stream-kafka-consumer-batch labx-11-sc-stream-kafka-producer-actuator labx-11-sc-stream-kafka-consumer-actuator ================================================ FILE: labx-11-spring-cloud-stream-kafka/《芋道 Spring Cloud 消息队列 Kafka 入门》.md ================================================ ================================================ FILE: labx-12-spring-cloud-config/labx-12-sc-config-server-demo/pom.xml ================================================ labx-12-spring-cloud-config cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-12-sc-config-server-demo 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-config-server ================================================ FILE: labx-12-spring-cloud-config/labx-12-sc-config-server-demo/src/main/java/cn/iocoder/springcloud/labx12/configserverdemo/ConfigServerApplication.java ================================================ package cn.iocoder.springcloud.labx12.configserverdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.config.server.EnableConfigServer; @SpringBootApplication @EnableConfigServer public class ConfigServerApplication { public static void main(String[] args) { SpringApplication.run(ConfigServerApplication.class, args); } } ================================================ FILE: labx-12-spring-cloud-config/labx-12-sc-config-server-demo/src/main/resources/application.yml ================================================ server: port: 8888 spring: application: name: demo-config-server profiles: active: native # TODO 采用的方案 cloud: config: server: native: search-locations: classpath:/shared ================================================ FILE: labx-12-spring-cloud-config/labx-12-sc-config-server-demo/src/main/resources/shared/user-application.yml ================================================ order: pay-timeout-seconds: 60 # 订单支付超时时长,单位:秒。 create-frequency-seconds: 120 # 订单创建频率,单位:秒 ================================================ FILE: labx-12-spring-cloud-config/labx-12-sc-config-server-git/pom.xml ================================================ labx-12-spring-cloud-config cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-12-sc-config-server-git 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.cloud spring-cloud-config-server src/main/resource **/*.yml **/*.jks false ================================================ FILE: labx-12-spring-cloud-config/labx-12-sc-config-server-git/src/main/java/cn/iocoder/springcloud/labx12/configserverdemo/ConfigServerApplication.java ================================================ package cn.iocoder.springcloud.labx12.configserverdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.config.server.EnableConfigServer; @SpringBootApplication @EnableConfigServer public class ConfigServerApplication { public static void main(String[] args) { SpringApplication.run(ConfigServerApplication.class, args); } } ================================================ FILE: labx-12-spring-cloud-config/labx-12-sc-config-server-git/src/main/resources/application.yml ================================================ server: port: 8888 spring: application: name: demo-config-server profiles: active: git # 使用的 Spring Cloud Config Server 的存储器方案 cloud: config: server: # Spring Cloud Config Server 的 Git 存储器的配置项,对应 MultipleJGitEnvironmentProperties 类 git: uri: https://github.com/YunaiV/demo-config-server.git # Git 仓库地址 search-paths: / # 读取文件的根地址 default-label: master # 使用的默认分支,默认为 master # username: ${CODING_USERNAME} # 账号 # password: ${CODING_PASSWORD} # 密码 ================================================ FILE: labx-12-spring-cloud-config/labx-12-sc-config-server-git/src/main/resources/bootstrap.yml ================================================ # 加密配置项,对应 KeyProperties 类 encrypt: # 对称加密 # key: yudaoyuanma # 对称加密 key # salt: dfaad7761f0729b35ec3b3543604eda7 # 对称加密 salt,默认为 "deadbeef",必须是 16 进制 # 非对应加密 key-store: location: classpath:/configserver.jks # jks 文件所在的路径 password: buzhidao # 对应 storepass 参数 alias: mytestkey # 对应 alias 参数 secret: nicainicai # 对应 keypass 参数 ================================================ FILE: labx-12-spring-cloud-config/labx-12-sc-config-server-git-auto-refresh-by-bus/pom.xml ================================================ labx-12-spring-cloud-config cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-12-sc-config-server-git-auto-refresh-by-bus 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-config-server org.springframework.cloud spring-cloud-starter-bus-amqp org.springframework.cloud spring-cloud-config-monitor ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx12.configserverdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.config.server.EnableConfigServer; @SpringBootApplication @EnableConfigServer public class ConfigServerApplication { public static void main(String[] args) { SpringApplication.run(ConfigServerApplication.class, args); } } ================================================ FILE: labx-12-spring-cloud-config/labx-12-sc-config-server-git-auto-refresh-by-bus/src/main/resources/application.yml ================================================ server: port: 8888 spring: application: name: demo-config-server profiles: active: git # 使用的 Spring Cloud Config Server 的存储器方案 # Spring Cloud Config 相关配置项 cloud: config: server: # Spring Cloud Config Server 的 Git 存储器的配置项,对应 MultipleJGitEnvironmentProperties 类 git: uri: https://github.com/YunaiV/demo-config-server.git # Git 仓库地址 search-paths: / # 读取文件的根地址 default-label: master # 使用的默认分支,默认为 master # username: ${CODING_USERNAME} # 账号 # password: ${CODING_PASSWORD} # 密码 # RabbitMQ 相关配置项 rabbitmq: host: localhost port: 5672 username: guest password: guest ================================================ FILE: labx-12-spring-cloud-config/labx-12-sc-config-server-git-nacos/pom.xml ================================================ labx-12-spring-cloud-config cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-12-sc-config-server-git-nacos 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.cloud spring-cloud-config-server com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery ================================================ FILE: labx-12-spring-cloud-config/labx-12-sc-config-server-git-nacos/src/main/java/cn/iocoder/springcloud/labx12/configserverdemo/ConfigServerApplication.java ================================================ package cn.iocoder.springcloud.labx12.configserverdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.config.server.EnableConfigServer; @SpringBootApplication @EnableConfigServer public class ConfigServerApplication { public static void main(String[] args) { SpringApplication.run(ConfigServerApplication.class, args); } } ================================================ FILE: labx-12-spring-cloud-config/labx-12-sc-config-server-git-nacos/src/main/resources/application.yml ================================================ server: port: 8888 spring: application: name: demo-config-server profiles: active: git # 使用的 Spring Cloud Config Server 的存储器方案 # Spring Cloud Config 相关配置项 cloud: config: server: # Spring Cloud Config Server 的 Git 存储器的配置项,对应 MultipleJGitEnvironmentProperties 类 git: uri: https://github.com/YunaiV/demo-config-server.git # Git 仓库地址 search-paths: / # 读取文件的根地址 default-label: master # 使用的默认分支,默认为 master # username: ${CODING_USERNAME} # 账号 # password: ${CODING_PASSWORD} # 密码 # Spring Cloud Nacos Discovery 相关配置项 nacos: # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 service: ${spring.application.name} # 注册到 Nacos 的服务名。默认值为 ${spring.application.name}。 ================================================ FILE: labx-12-spring-cloud-config/labx-12-sc-config-user-application/pom.xml ================================================ labx-12-spring-cloud-config cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-12-sc-config-user-application 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-config ================================================ FILE: labx-12-spring-cloud-config/labx-12-sc-config-user-application/src/main/java/cn/iocoder/springcloud/labx12/userapplication/UserApplication.java ================================================ package cn.iocoder.springcloud.labx12.userapplication; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class UserApplication { public static void main(String[] args) { SpringApplication.run(UserApplication.class, args); } } ================================================ FILE: labx-12-spring-cloud-config/labx-12-sc-config-user-application/src/main/java/cn/iocoder/springcloud/labx12/userapplication/config/OrderProperties.java ================================================ package cn.iocoder.springcloud.labx12.userapplication.config; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component @ConfigurationProperties(prefix = "order") public class OrderProperties { /** * 订单支付超时时长,单位:秒。 */ private Integer payTimeoutSeconds; /** * 订单创建频率,单位:秒 */ private Integer createFrequencySeconds; public Integer getPayTimeoutSeconds() { return payTimeoutSeconds; } public OrderProperties setPayTimeoutSeconds(Integer payTimeoutSeconds) { this.payTimeoutSeconds = payTimeoutSeconds; return this; } public Integer getCreateFrequencySeconds() { return createFrequencySeconds; } public OrderProperties setCreateFrequencySeconds(Integer createFrequencySeconds) { this.createFrequencySeconds = createFrequencySeconds; return this; } } ================================================ FILE: labx-12-spring-cloud-config/labx-12-sc-config-user-application/src/main/java/cn/iocoder/springcloud/labx12/userapplication/controller/DemoController.java ================================================ package cn.iocoder.springcloud.labx12.userapplication.controller; import cn.iocoder.springcloud.labx12.userapplication.config.OrderProperties; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.Map; @RestController @RequestMapping("/demo") public class DemoController { @Autowired private OrderProperties orderProperties; /** * 测试 @ConfigurationProperties 注解的配置属性类 */ @GetMapping("/test01") public OrderProperties test01() { return orderProperties; } @Value(value = "${order.pay-timeout-seconds}") private Integer payTimeoutSeconds; @Value(value = "${order.create-frequency-seconds}") private Integer createFrequencySeconds; /** * 测试 @Value 注解的属性 */ @GetMapping("/test02") public Map test02() { Map result = new HashMap<>(); result.put("payTimeoutSeconds", payTimeoutSeconds); result.put("createFrequencySeconds", createFrequencySeconds); return result; } @Value(value = "${xx-password:''}") private String xxPassword; @GetMapping("/xx_password") public String xxPassword() { return xxPassword; } @Value(value = "${yy-password:''}") private String yyPassword; @GetMapping("/yy_password") public String yyPassword() { return yyPassword; } } ================================================ FILE: labx-12-spring-cloud-config/labx-12-sc-config-user-application/src/main/resources/bootstrap.yml ================================================ spring: application: name: user-application cloud: # Spring Cloud Config Client 配置项,对应 ConfigClientProperties 类 config: uri: http://127.0.0.1:8888 # Spring Cloud Config Server 的地址 name: ${spring.application.name} # 读取的配置文件的名字,默认为 ${spring.application.name} ================================================ FILE: labx-12-spring-cloud-config/labx-12-sc-config-user-application-auto-refresh-by-actuator/pom.xml ================================================ labx-12-spring-cloud-config cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-12-sc-config-user-application-auto-refresh-by-actuator 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-config org.springframework.boot spring-boot-starter-actuator ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx12.userapplication; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class UserApplication { public static void main(String[] args) { SpringApplication.run(UserApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx12.userapplication.config; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component //@NacosConfigurationProperties(prefix = "order", dataId = "${nacos.config.data-id}", type = ConfigType.YAML) @ConfigurationProperties(prefix = "order") public class OrderProperties { /** * 订单支付超时时长,单位:秒。 */ private Integer payTimeoutSeconds; /** * 订单创建频率,单位:秒 */ private Integer createFrequencySeconds; // /** // * 配置描述 // */ // private String desc; public Integer getPayTimeoutSeconds() { return payTimeoutSeconds; } public OrderProperties setPayTimeoutSeconds(Integer payTimeoutSeconds) { this.payTimeoutSeconds = payTimeoutSeconds; return this; } public Integer getCreateFrequencySeconds() { return createFrequencySeconds; } public OrderProperties setCreateFrequencySeconds(Integer createFrequencySeconds) { this.createFrequencySeconds = createFrequencySeconds; return this; } // public String getDesc() { // return desc; // } // // public OrderProperties setDesc(String desc) { // this.desc = desc; // return this; // } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx12.userapplication.controller; import cn.iocoder.springcloud.labx12.userapplication.config.OrderProperties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.Map; @RestController @RequestMapping("/demo") @RefreshScope public class DemoController { @Autowired private OrderProperties orderProperties; /** * 测试 @ConfigurationProperties 注解的配置属性类 */ @GetMapping("/test01") public OrderProperties test01() { return orderProperties; } @Value(value = "${order.pay-timeout-seconds}") // @NacosValue(value = "${order.pay-timeout-seconds}") private Integer payTimeoutSeconds; @Value(value = "${order.create-frequency-seconds}") // @NacosValue(value = "${order.create-frequency-seconds}") private Integer createFrequencySeconds; /** * 测试 @Value 注解的属性 */ @GetMapping("/test02") public Map test02() { Map result = new HashMap<>(); result.put("payTimeoutSeconds", payTimeoutSeconds); result.put("createFrequencySeconds", createFrequencySeconds); return result; } private Logger logger = LoggerFactory.getLogger(getClass()); @GetMapping("/logger") public void logger() { logger.debug("[logger][测试一下]"); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx12.userapplication.listener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.context.environment.EnvironmentChangeEvent; import org.springframework.context.ApplicationListener; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.stereotype.Component; @Component public class DemoEnvironmentChangeListener implements ApplicationListener { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private ConfigurableEnvironment environment; @Override public void onApplicationEvent(EnvironmentChangeEvent event) { for (String key : event.getKeys()) { logger.info("[onApplicationEvent][key({}) 最新 value 为 {}]", key, environment.getProperty(key)); } } } ================================================ FILE: labx-12-spring-cloud-config/labx-12-sc-config-user-application-auto-refresh-by-actuator/src/main/resources/application.yml ================================================ management: endpoints: # Actuator HTTP 配置项,对应 WebEndpointProperties 配置类 web: exposure: include: refresh # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 ================================================ FILE: labx-12-spring-cloud-config/labx-12-sc-config-user-application-auto-refresh-by-actuator/src/main/resources/bootstrap.yml ================================================ spring: application: name: user-application cloud: config: uri: http://127.0.0.1:8888 name: ${spring.application.name} fail-fast: true # TODO ================================================ FILE: labx-12-spring-cloud-config/labx-12-sc-config-user-application-auto-refresh-by-bus/pom.xml ================================================ labx-12-spring-cloud-config cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-12-sc-config-user-application-auto-refresh-by-bus 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-config org.springframework.cloud spring-cloud-starter-bus-amqp ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx12.userapplication; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class UserApplication { public static void main(String[] args) { SpringApplication.run(UserApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx12.userapplication.config; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component @ConfigurationProperties(prefix = "order") public class OrderProperties { /** * 订单支付超时时长,单位:秒。 */ private Integer payTimeoutSeconds; /** * 订单创建频率,单位:秒 */ private Integer createFrequencySeconds; public Integer getPayTimeoutSeconds() { return payTimeoutSeconds; } public OrderProperties setPayTimeoutSeconds(Integer payTimeoutSeconds) { this.payTimeoutSeconds = payTimeoutSeconds; return this; } public Integer getCreateFrequencySeconds() { return createFrequencySeconds; } public OrderProperties setCreateFrequencySeconds(Integer createFrequencySeconds) { this.createFrequencySeconds = createFrequencySeconds; return this; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx12.userapplication.controller; import cn.iocoder.springcloud.labx12.userapplication.config.OrderProperties; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.Map; @RestController @RequestMapping("/demo") @RefreshScope public class DemoController { @Autowired private OrderProperties orderProperties; /** * 测试 @ConfigurationProperties 注解的配置属性类 */ @GetMapping("/test01") public OrderProperties test01() { return orderProperties; } @Value(value = "${order.pay-timeout-seconds}") private Integer payTimeoutSeconds; @Value(value = "${order.create-frequency-seconds}") private Integer createFrequencySeconds; /** * 测试 @Value 注解的属性 */ @GetMapping("/test02") public Map test02() { Map result = new HashMap<>(); result.put("payTimeoutSeconds", payTimeoutSeconds); result.put("createFrequencySeconds", createFrequencySeconds); return result; } } ================================================ FILE: labx-12-spring-cloud-config/labx-12-sc-config-user-application-auto-refresh-by-bus/src/main/resources/application.yml ================================================ spring: # RabbitMQ 相关配置项 rabbitmq: host: localhost port: 5672 username: guest password: guest ================================================ FILE: labx-12-spring-cloud-config/labx-12-sc-config-user-application-auto-refresh-by-bus/src/main/resources/bootstrap.yml ================================================ spring: application: name: user-application cloud: # Spring Cloud Config Client 配置项,对应 ConfigClientProperties 类 config: uri: http://127.0.0.1:8888 # Spring Cloud Config Server 的地址 name: ${spring.application.name} # 读取的配置文件的名字,默认为 ${spring.application.name} ================================================ FILE: labx-12-spring-cloud-config/labx-12-sc-config-user-application-nacos/pom.xml ================================================ labx-12-spring-cloud-config cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-12-sc-config-user-application-nacos 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-config com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery ================================================ FILE: labx-12-spring-cloud-config/labx-12-sc-config-user-application-nacos/src/main/java/cn/iocoder/springcloud/labx12/userapplication/UserApplication.java ================================================ package cn.iocoder.springcloud.labx12.userapplication; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class UserApplication { public static void main(String[] args) { SpringApplication.run(UserApplication.class, args); } } ================================================ FILE: labx-12-spring-cloud-config/labx-12-sc-config-user-application-nacos/src/main/java/cn/iocoder/springcloud/labx12/userapplication/config/OrderProperties.java ================================================ package cn.iocoder.springcloud.labx12.userapplication.config; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component @ConfigurationProperties(prefix = "order") public class OrderProperties { /** * 订单支付超时时长,单位:秒。 */ private Integer payTimeoutSeconds; /** * 订单创建频率,单位:秒 */ private Integer createFrequencySeconds; public Integer getPayTimeoutSeconds() { return payTimeoutSeconds; } public OrderProperties setPayTimeoutSeconds(Integer payTimeoutSeconds) { this.payTimeoutSeconds = payTimeoutSeconds; return this; } public Integer getCreateFrequencySeconds() { return createFrequencySeconds; } public OrderProperties setCreateFrequencySeconds(Integer createFrequencySeconds) { this.createFrequencySeconds = createFrequencySeconds; return this; } } ================================================ FILE: labx-12-spring-cloud-config/labx-12-sc-config-user-application-nacos/src/main/java/cn/iocoder/springcloud/labx12/userapplication/controller/DemoController.java ================================================ package cn.iocoder.springcloud.labx12.userapplication.controller; import cn.iocoder.springcloud.labx12.userapplication.config.OrderProperties; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.Map; @RestController @RequestMapping("/demo") public class DemoController { @Autowired private OrderProperties orderProperties; /** * 测试 @ConfigurationProperties 注解的配置属性类 */ @GetMapping("/test01") public OrderProperties test01() { return orderProperties; } @Value(value = "${order.pay-timeout-seconds}") private Integer payTimeoutSeconds; @Value(value = "${order.create-frequency-seconds}") private Integer createFrequencySeconds; /** * 测试 @Value 注解的属性 */ @GetMapping("/test02") public Map test02() { Map result = new HashMap<>(); result.put("payTimeoutSeconds", payTimeoutSeconds); result.put("createFrequencySeconds", createFrequencySeconds); return result; } @Value(value = "${xx-password:''}") private String xxPassword; @GetMapping("/xx_password") public String xxPassword() { return xxPassword; } @Value(value = "${yy-password:''}") private String yyPassword; @GetMapping("/yy_password") public String yyPassword() { return yyPassword; } } ================================================ FILE: labx-12-spring-cloud-config/labx-12-sc-config-user-application-nacos/src/main/resources/bootstrap.yml ================================================ spring: application: name: user-application cloud: # Spring Cloud Config Client 配置项,对应 ConfigClientProperties 类 config: name: ${spring.application.name} # 读取的配置文件的名字,默认为 ${spring.application.name} discovery: enabled: true # 是否使用注册发现,获取配置中心的地址,默认为 false service-id: demo-config-server # 配置中心的服务名 # Spring Cloud Nacos Discovery 相关配置项 nacos: # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 service: ${spring.application.name} # 注册到 Nacos 的服务名。默认值为 ${spring.application.name}。 ================================================ FILE: labx-12-spring-cloud-config/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-12-spring-cloud-config pom labx-12-sc-config-server-demo labx-12-sc-config-user-application labx-12-sc-config-server-git labx-12-sc-config-user-application-auto-refresh-by-actuator labx-12-sc-config-server-git-auto-refresh-by-bus labx-12-sc-config-user-application-auto-refresh-by-bus labx-12-sc-config-server-git-nacos labx-12-sc-config-user-application-nacos ================================================ FILE: labx-12-spring-cloud-config/《芋道 Spring Cloud 配置中心 Spring Cloud Config 入门》.md ================================================ ================================================ FILE: labx-13/labx-13-sc-sleuth-db-elasticsearch/pom.xml ================================================ labx-13 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-13-sc-sleuth-db-elasticsearch 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-data-elasticsearch org.springframework.cloud spring-cloud-starter-zipkin io.opentracing.brave brave-opentracing 0.35.0 io.opentracing.contrib opentracing-elasticsearch6-client 0.1.6 ================================================ FILE: labx-13/labx-13-sc-sleuth-db-elasticsearch/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/UserServiceApplication.java ================================================ package cn.iocoder.springcloud.labx13.springmvcdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class UserServiceApplication { public static void main(String[] args) { SpringApplication.run(UserServiceApplication.class, args); } } ================================================ FILE: labx-13/labx-13-sc-sleuth-db-elasticsearch/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/config/SleuthConfiguration.java ================================================ package cn.iocoder.springcloud.labx13.springmvcdemo.config; import cn.iocoder.springcloud.labx13.springmvcdemo.spring.TracingTransportClientFactoryBean; import io.opentracing.Tracer; import org.elasticsearch.client.transport.TransportClient; import org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.Properties; @Configuration public class SleuthConfiguration { // ==================== Elasticsearch 相关 ==================== @Bean public TransportClient elasticsearchClient(Tracer tracer, ElasticsearchProperties elasticsearchProperties) throws Exception { // 创建 TracingTransportClientFactoryBean 对象 TracingTransportClientFactoryBean factory = new TracingTransportClientFactoryBean(tracer); // 设置其属性 factory.setClusterNodes(elasticsearchProperties.getClusterNodes()); factory.setProperties(this.createElasticsearch(elasticsearchProperties)); // 创建 TransportClient 对象,并返回 factory.afterPropertiesSet(); return factory.getObject(); } private Properties createElasticsearch(ElasticsearchProperties elasticsearchProperties) { Properties properties = new Properties(); properties.put("cluster.name", elasticsearchProperties.getClusterName()); properties.putAll(elasticsearchProperties.getProperties()); return properties; } } ================================================ FILE: labx-13/labx-13-sc-sleuth-db-elasticsearch/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/controller/UserController.java ================================================ package cn.iocoder.springcloud.labx13.springmvcdemo.controller; import cn.iocoder.springcloud.labx13.springmvcdemo.dataobject.ESUserDO; import cn.iocoder.springcloud.labx13.springmvcdemo.repository.ESUserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/user") public class UserController { @Autowired private ESUserRepository userRepository; @GetMapping("/get") public String get(@RequestParam("id") Integer id) { this.findById(id); return "success"; } public ESUserDO findById(Integer id) { return userRepository.findById(id).orElse(null); } } ================================================ FILE: labx-13/labx-13-sc-sleuth-db-elasticsearch/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/dataobject/ESUserDO.java ================================================ package cn.iocoder.springcloud.labx13.springmvcdemo.dataobject; import org.springframework.data.annotation.Id; import org.springframework.data.elasticsearch.annotations.Document; import java.util.Date; @Document(indexName = "user", // 索引名 type = "user", // 类型。未来的版本即将废弃 shards = 1, // 默认索引分区数 replicas = 0, // 每个分区的备份数 refreshInterval = "-1" // 刷新间隔 ) public class ESUserDO { @Id private Integer id; /** * 账号 */ private String username; /** * 密码 */ private String password; /** * 创建时间 */ private Date createTime; @Override public String toString() { return "UserDO{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", createTime=" + createTime + '}'; } } ================================================ FILE: labx-13/labx-13-sc-sleuth-db-elasticsearch/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/repository/ESUserRepository.java ================================================ package cn.iocoder.springcloud.labx13.springmvcdemo.repository; import cn.iocoder.springcloud.labx13.springmvcdemo.dataobject.ESUserDO; import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; public interface ESUserRepository extends ElasticsearchRepository { } ================================================ FILE: labx-13/labx-13-sc-sleuth-db-elasticsearch/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/spring/ClusterNodes.java ================================================ package cn.iocoder.springcloud.labx13.springmvcdemo.spring; import org.elasticsearch.common.transport.TransportAddress; import org.springframework.data.util.Streamable; import org.springframework.util.Assert; import org.springframework.util.StringUtils; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.stream.Collectors; class ClusterNodes implements Streamable { public static ClusterNodes DEFAULT = ClusterNodes.of("127.0.0.1:9300"); private static final String COLON = ":"; private static final String COMMA = ","; private final List clusterNodes; /** * Creates a new {@link ClusterNodes} by parsing the given source. * * @param source must not be {@literal null} or empty. */ private ClusterNodes(String source) { Assert.hasText(source, "Cluster nodes source must not be null or empty!"); String[] nodes = StringUtils.delimitedListToStringArray(source, COMMA); this.clusterNodes = Arrays.stream(nodes).map(node -> { String[] segments = StringUtils.delimitedListToStringArray(node, COLON); Assert.isTrue(segments.length == 2, () -> String.format("Invalid cluster node %s in %s! Must be in the format host:port!", node, source)); String host = segments[0].trim(); String port = segments[1].trim(); Assert.hasText(host, () -> String.format("No host name given cluster node %s!", node)); Assert.hasText(port, () -> String.format("No port given in cluster node %s!", node)); return new TransportAddress(toInetAddress(host), Integer.valueOf(port)); }).collect(Collectors.toList()); } /** * Creates a new {@link ClusterNodes} by parsing the given source. The expected format is a comma separated list of * host-port-combinations separated by a colon: {@code host:port,host:port,…}. * * @param source must not be {@literal null} or empty. * @return */ public static ClusterNodes of(String source) { return new ClusterNodes(source); } /* * (non-Javadoc) * @see java.lang.Iterable#iterator() */ @Override public Iterator iterator() { return clusterNodes.iterator(); } private static InetAddress toInetAddress(String host) { try { return InetAddress.getByName(host); } catch (UnknownHostException o_O) { throw new IllegalArgumentException(o_O); } } } ================================================ FILE: labx-13/labx-13-sc-sleuth-db-elasticsearch/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/spring/TracingTransportClientFactoryBean.java ================================================ package cn.iocoder.springcloud.labx13.springmvcdemo.spring; import io.opentracing.Tracer; import io.opentracing.contrib.elasticsearch6.TracingPreBuiltTransportClient; import org.elasticsearch.client.transport.TransportClient; import org.elasticsearch.common.settings.Settings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.data.elasticsearch.client.TransportClientFactoryBean; import java.util.Properties; /** * 参考 {@link TransportClientFactoryBean} 来实现。 */ public class TracingTransportClientFactoryBean implements FactoryBean, InitializingBean, DisposableBean { private static final Logger logger = LoggerFactory.getLogger(TransportClientFactoryBean.class); private ClusterNodes clusterNodes = ClusterNodes.of("127.0.0.1:9300"); private String clusterName = "elasticsearch"; private Boolean clientTransportSniff = true; private Boolean clientIgnoreClusterName = Boolean.FALSE; private String clientPingTimeout = "5s"; private String clientNodesSamplerInterval = "5s"; private TransportClient client; private Properties properties; private Tracer tracer; public TracingTransportClientFactoryBean(Tracer tracer) { this.tracer = tracer; } @Override public void destroy() throws Exception { try { logger.info("Closing elasticSearch client"); if (client != null) { client.close(); } } catch (final Exception e) { logger.error("Error closing ElasticSearch client: ", e); } } @Override public TransportClient getObject() throws Exception { return client; } @Override public Class getObjectType() { return TransportClient.class; } @Override public boolean isSingleton() { return true; } @Override public void afterPropertiesSet() throws Exception { buildClient(); } protected void buildClient() throws Exception { // 创建可追踪的 TracingPreBuiltTransportClient client = new TracingPreBuiltTransportClient(tracer, settings()); clusterNodes.stream() // .peek(it -> logger.info("Adding transport node : " + it.toString())) // .forEach(client::addTransportAddress); client.connectedNodes(); } private Settings settings() { if (properties != null) { Settings.Builder builder = Settings.builder(); properties.forEach((key, value) -> { builder.put(key.toString(), value.toString()); }); return builder.build(); } return Settings.builder() .put("cluster.name", clusterName) .put("client.transport.sniff", clientTransportSniff) .put("client.transport.ignore_cluster_name", clientIgnoreClusterName) .put("client.transport.ping_timeout", clientPingTimeout) .put("client.transport.nodes_sampler_interval", clientNodesSamplerInterval) .build(); } public void setClusterNodes(String clusterNodes) { this.clusterNodes = ClusterNodes.of(clusterNodes); } public void setClusterName(String clusterName) { this.clusterName = clusterName; } public void setClientTransportSniff(Boolean clientTransportSniff) { this.clientTransportSniff = clientTransportSniff; } public String getClientNodesSamplerInterval() { return clientNodesSamplerInterval; } public void setClientNodesSamplerInterval(String clientNodesSamplerInterval) { this.clientNodesSamplerInterval = clientNodesSamplerInterval; } public String getClientPingTimeout() { return clientPingTimeout; } public void setClientPingTimeout(String clientPingTimeout) { this.clientPingTimeout = clientPingTimeout; } public Boolean getClientIgnoreClusterName() { return clientIgnoreClusterName; } public void setClientIgnoreClusterName(Boolean clientIgnoreClusterName) { this.clientIgnoreClusterName = clientIgnoreClusterName; } public void setProperties(Properties properties) { this.properties = properties; } } ================================================ FILE: labx-13/labx-13-sc-sleuth-db-elasticsearch/src/main/resources/application.yml ================================================ spring: application: name: user-service # 服务名 # Zipkin 配置项,对应 ZipkinProperties 类 zipkin: base-url: http://127.0.0.1:9411 # Zipkin 服务的地址 # Spring Cloud Sleuth 配置项 sleuth: # Spring Cloud Sleuth 针对 Web 组件的配置项,例如说 SpringMVC web: enabled: true # 是否开启,默认为 true data: # Elasticsearch 配置项 elasticsearch: cluster-name: elasticsearch # 集群名 cluster-nodes: 127.0.0.1:9300 # 集群节点 ================================================ FILE: labx-13/labx-13-sc-sleuth-db-mongodb/pom.xml ================================================ labx-13 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-13-sc-sleuth-db-mongodb 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-data-mongodb org.springframework.cloud spring-cloud-starter-zipkin io.opentracing.brave brave-opentracing 0.35.0 io.opentracing.contrib opentracing-mongo-driver 0.1.5 ================================================ FILE: labx-13/labx-13-sc-sleuth-db-mongodb/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/UserServiceApplication.java ================================================ package cn.iocoder.springcloud.labx13.springmvcdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class UserServiceApplication { public static void main(String[] args) { SpringApplication.run(UserServiceApplication.class, args); } } ================================================ FILE: labx-13/labx-13-sc-sleuth-db-mongodb/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/config/SleuthConfiguration.java ================================================ package cn.iocoder.springcloud.labx13.springmvcdemo.config; import com.mongodb.MongoClientOptions; import io.opentracing.Tracer; import io.opentracing.contrib.mongo.common.TracingCommandListener; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class SleuthConfiguration { // ==================== MongoDB 相关 ==================== @Bean public MongoClientOptions mongoClientOptions(Tracer tracer) { // 创建 TracingCommandListener 对象 TracingCommandListener listener = new TracingCommandListener.Builder(tracer).build(); // 创建 MongoClientOptions 对象,并设置监听器 return MongoClientOptions.builder().addCommandListener(listener).build(); } } ================================================ FILE: labx-13/labx-13-sc-sleuth-db-mongodb/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/controller/UserController.java ================================================ package cn.iocoder.springcloud.labx13.springmvcdemo.controller; import cn.iocoder.springcloud.labx13.springmvcdemo.dataobject.UserDO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/user") public class UserController { @Autowired private MongoTemplate mongoTemplate; @GetMapping("/get") public String get(@RequestParam("id") Integer id) { this.findById(1); return "success"; } public UserDO findById(Integer id) { return mongoTemplate.findOne(new Query(Criteria.where("_id").is(id)), UserDO.class); } } ================================================ FILE: labx-13/labx-13-sc-sleuth-db-mongodb/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/dataobject/UserDO.java ================================================ package cn.iocoder.springcloud.labx13.springmvcdemo.dataobject; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.mapping.Document; import java.util.Date; /** * 用户 DO */ @Document(collection = "User") public class UserDO { @Id private Integer id; /** * 账号 */ private String username; /** * 密码 */ private String password; /** * 创建时间 */ private Date createTime; @Override public String toString() { return "UserDO{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", createTime=" + createTime + '}'; } } ================================================ FILE: labx-13/labx-13-sc-sleuth-db-mongodb/src/main/resources/application.yml ================================================ spring: application: name: user-service # 服务名 # Zipkin 配置项,对应 ZipkinProperties 类 zipkin: base-url: http://127.0.0.1:9411 # Zipkin 服务的地址 # Spring Cloud Sleuth 配置项 sleuth: # Spring Cloud Sleuth 针对 Web 组件的配置项,例如说 SpringMVC web: enabled: true # 是否开启,默认为 true data: # MongoDB 配置项,对应 MongoProperties 类 mongodb: host: 127.0.0.1 port: 27017 database: yourdatabase username: test01 password: password01 # 上述属性,也可以只配置 uri ================================================ FILE: labx-13/labx-13-sc-sleuth-db-mysql/pom.xml ================================================ labx-13 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-13-sc-sleuth-db-mysql 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 5.1.46 org.springframework.cloud spring-cloud-starter-zipkin io.zipkin.brave brave-instrumentation-mysql ================================================ FILE: labx-13/labx-13-sc-sleuth-db-mysql/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/UserServiceApplication.java ================================================ package cn.iocoder.springcloud.labx13.springmvcdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class UserServiceApplication { public static void main(String[] args) { SpringApplication.run(UserServiceApplication.class, args); } } ================================================ FILE: labx-13/labx-13-sc-sleuth-db-mysql/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/controller/UserController.java ================================================ package cn.iocoder.springcloud.labx13.springmvcdemo.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/user") public class UserController { @Autowired private JdbcTemplate template; @GetMapping("/get") public String get(@RequestParam("id") Integer id) { this.selectById(1); return "success"; } public Object selectById(Integer id) { return template.queryForObject("SELECT id, username, password FROM t_user WHERE id = ?", new BeanPropertyRowMapper<>(Object.class), // 结果转换成对应的对象。Object 理论来说是 UserDO.class ,这里偷懒了。 id); } } ================================================ FILE: labx-13/labx-13-sc-sleuth-db-mysql/src/main/resources/application.yml ================================================ spring: application: name: user-service # 服务名 # Zipkin 配置项,对应 ZipkinProperties 类 zipkin: base-url: http://127.0.0.1:9411 # Zipkin 服务的地址 # Spring Cloud Sleuth 配置项 sleuth: # Spring Cloud Sleuth 针对 Web 组件的配置项,例如说 SpringMVC web: enabled: true # 是否开启,默认为 true # datasource 数据源配置内容 datasource: 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 driver-class-name: com.mysql.jdbc.Driver username: root password: ================================================ FILE: labx-13/labx-13-sc-sleuth-db-redis/pom.xml ================================================ labx-13 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-13-sc-sleuth-db-redis 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-data-redis io.lettuce lettuce-core redis.clients jedis org.springframework.cloud spring-cloud-starter-zipkin io.opentracing.brave brave-opentracing 0.35.0 io.opentracing.contrib opentracing-redis-jedis3 0.1.14 io.opentracing.contrib opentracing-redis-spring-data 0.1.14 ================================================ FILE: labx-13/labx-13-sc-sleuth-db-redis/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/UserServiceApplication.java ================================================ package cn.iocoder.springcloud.labx13.springmvcdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class UserServiceApplication { public static void main(String[] args) { SpringApplication.run(UserServiceApplication.class, args); } } ================================================ FILE: labx-13/labx-13-sc-sleuth-db-redis/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/config/SleuthConfiguration.java ================================================ package cn.iocoder.springcloud.labx13.springmvcdemo.config; import io.opentracing.Tracer; import io.opentracing.contrib.redis.common.TracingConfiguration; import io.opentracing.contrib.redis.spring.data.connection.TracingRedisConnectionFactory; import org.springframework.boot.autoconfigure.data.redis.RedisProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; @Configuration public class SleuthConfiguration { // ==================== Redis 相关 ==================== @Bean public RedisConnectionFactory redisConnectionFactory(Tracer tracer, RedisProperties redisProperties) { // 创建 JedisConnectionFactory 对象 RedisConnectionFactory connectionFactory = new JedisConnectionFactory(); // 创建 TracingConfiguration 对象 TracingConfiguration tracingConfiguration = new TracingConfiguration.Builder(tracer) // 设置拓展 Tag ,设置 Redis 服务器地址。因为默认情况下,不会在操作 Redis 链路的 Span 上记录 Redis 服务器的地址,所以这里需要设置。 .extensionTag("Server Address", redisProperties.getHost() + ":" + redisProperties.getPort()) .build(); // 创建 TracingRedisConnectionFactory 对象 return new TracingRedisConnectionFactory(connectionFactory, tracingConfiguration); } } ================================================ FILE: labx-13/labx-13-sc-sleuth-db-redis/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/controller/UserController.java ================================================ package cn.iocoder.springcloud.labx13.springmvcdemo.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/user") public class UserController { @Autowired private StringRedisTemplate redisTemplate; @GetMapping("/get") public String get(@RequestParam("id") Integer id) { this.get("demo"); return "success"; } public void get(String key) { redisTemplate.opsForValue().get(key); } } ================================================ FILE: labx-13/labx-13-sc-sleuth-db-redis/src/main/resources/application.yml ================================================ spring: application: name: user-service # 服务名 # Zipkin 配置项,对应 ZipkinProperties 类 zipkin: base-url: http://127.0.0.1:9411 # Zipkin 服务的地址 # Spring Cloud Sleuth 配置项 sleuth: # Spring Cloud Sleuth 针对 Web 组件的配置项,例如说 SpringMVC web: enabled: true # 是否开启,默认为 true # 对应 RedisProperties 类 redis: host: 127.0.0.1 port: 6379 password: # Redis 服务器密码,默认为空。生产中,一定要设置 Redis 密码! database: 0 # Redis 数据库号,默认为 0 。 timeout: 0 # Redis 连接超时时间,单位:毫秒。 # 对应 RedisProperties.Jedis 内部类 jedis: pool: max-active: 8 # 连接池最大连接数,默认为 8 。使用负数表示没有限制。 max-idle: 8 # 默认连接数最小空闲的连接数,默认为 8 。使用负数表示没有限制。 min-idle: 0 # 默认连接池最小空闲的连接数,默认为 0 。允许设置 0 和 正数。 max-wait: -1 # 连接池最大阻塞等待时间,单位:毫秒。默认为 -1 ,表示不限制。 ================================================ FILE: labx-13/labx-13-sc-sleuth-dubbo/labx-13-sc-sleuth-dubbo-api/pom.xml ================================================ labx-13-sc-sleuth-dubbo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-13-sc-sleuth-dubbo-api ================================================ FILE: labx-13/labx-13-sc-sleuth-dubbo/labx-13-sc-sleuth-dubbo-api/src/main/java/cn/iocoder/springcloud/labx13/api/UserService.java ================================================ package cn.iocoder.springcloud.labx13.api; /** * 用户服务 RPC Service 接口 */ public interface UserService { /** * 根据指定用户编号,获得用户信息 * * @param id 用户编号 * @return 用户信息 */ String get(Integer id); } ================================================ FILE: labx-13/labx-13-sc-sleuth-dubbo/labx-13-sc-sleuth-dubbo-api/src/main/java/cn/iocoder/springcloud/labx13/package-info.java ================================================ package cn.iocoder.springcloud.labx13; ================================================ FILE: labx-13/labx-13-sc-sleuth-dubbo/labx-13-sc-sleuth-dubbo-consumer/pom.xml ================================================ labx-13-sc-sleuth-dubbo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-13-sc-sleuth-dubbo-consumer 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import cn.iocoder.springboot.labs labx-13-sc-sleuth-dubbo-api 1.0-SNAPSHOT org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery com.alibaba.cloud spring-cloud-starter-dubbo org.springframework.cloud spring-cloud-starter-zipkin io.zipkin.brave brave-instrumentation-dubbo 5.10.1 ================================================ FILE: labx-13/labx-13-sc-sleuth-dubbo/labx-13-sc-sleuth-dubbo-consumer/src/main/java/cn/iocoder/springcloud/labx13/consumerdemo/ConsumerApplication.java ================================================ package cn.iocoder.springcloud.labx13.consumerdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class); } } ================================================ FILE: labx-13/labx-13-sc-sleuth-dubbo/labx-13-sc-sleuth-dubbo-consumer/src/main/java/cn/iocoder/springcloud/labx13/consumerdemo/controller/UserController.java ================================================ package cn.iocoder.springcloud.labx13.consumerdemo.controller; import cn.iocoder.springcloud.labx13.api.UserService; import org.apache.dubbo.config.annotation.Reference; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/user") public class UserController { @Reference(protocol = "dubbo", version = "1.0.0") private UserService userService; @GetMapping("/get") public String get(@RequestParam("id") Integer id) { return userService.get(id); } } ================================================ FILE: labx-13/labx-13-sc-sleuth-dubbo/labx-13-sc-sleuth-dubbo-consumer/src/main/resources/application.yaml ================================================ spring: application: name: demo-consumer cloud: # Nacos 作为注册中心的配置项 nacos: discovery: server-addr: 127.0.0.1:8848 # Zipkin 配置项,对应 ZipkinProperties 类 zipkin: base-url: http://127.0.0.1:9411 # Zipkin 服务的地址 # Dubbo 配置项,对应 DubboConfigurationProperties 类 dubbo: # Dubbo 服务注册中心配置,对应 RegistryConfig 类 registry: address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址 # Dubbo 服务提供者的配置,对应 ConsumerConfig 类 consumer: filter: tracing # Spring Cloud Alibaba Dubbo 专属配置项,对应 DubboCloudProperties 类 cloud: subscribed-services: demo-provider # 设置订阅的应用列表,默认为 * 订阅所有应用。 ================================================ FILE: labx-13/labx-13-sc-sleuth-dubbo/labx-13-sc-sleuth-dubbo-provider/pom.xml ================================================ labx-13-sc-sleuth-dubbo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-13-sc-sleuth-dubbo-provider 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import cn.iocoder.springboot.labs labx-13-sc-sleuth-dubbo-api 1.0-SNAPSHOT com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery com.alibaba.cloud spring-cloud-starter-dubbo org.springframework.cloud spring-cloud-starter-zipkin io.zipkin.brave brave-instrumentation-dubbo 5.10.1 ================================================ FILE: labx-13/labx-13-sc-sleuth-dubbo/labx-13-sc-sleuth-dubbo-provider/src/main/java/cn/iocoder/springcloud/labx13/providerdemo/ProviderApplication.java ================================================ package cn.iocoder.springcloud.labx13.providerdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ProviderApplication { public static void main(String[] args) { SpringApplication.run(ProviderApplication.class); } } ================================================ FILE: labx-13/labx-13-sc-sleuth-dubbo/labx-13-sc-sleuth-dubbo-provider/src/main/java/cn/iocoder/springcloud/labx13/providerdemo/service/UserServiceImpl.java ================================================ package cn.iocoder.springcloud.labx13.providerdemo.service; import cn.iocoder.springcloud.labx13.api.UserService; @org.apache.dubbo.config.annotation.Service(protocol = "dubbo", version = "1.0.0") public class UserServiceImpl implements UserService { @Override public String get(Integer id) { return "user:" + id; } } ================================================ FILE: labx-13/labx-13-sc-sleuth-dubbo/labx-13-sc-sleuth-dubbo-provider/src/main/resources/application.yaml ================================================ spring: application: name: demo-provider cloud: # Nacos 作为注册中心的配置项 nacos: discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 # Zipkin 配置项,对应 ZipkinProperties 类 zipkin: base-url: http://127.0.0.1:9411 # Zipkin 服务的地址 # Dubbo 配置项,对应 DubboConfigurationProperties 类 dubbo: scan: base-packages: cn.iocoder.springcloud.labx13.providerdemo.service # 指定 Dubbo 服务实现类的扫描基准包 # Dubbo 服务暴露的协议配置,对应 ProtocolConfig Map protocols: dubbo: name: dubbo # 协议名称 port: -1 # 协议端口,-1 表示自增端口,从 20880 开始 # Dubbo 服务注册中心配置,对应 RegistryConfig 类 registry: address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址 # Dubbo 服务提供者的配置,对应 ProviderConfig 类 provider: filter: tracing # Spring Cloud Alibaba Dubbo 专属配置项,对应 DubboCloudProperties 类 cloud: subscribed-services: '' # 设置订阅的应用列表,默认为 * 订阅所有应用。 ================================================ FILE: labx-13/labx-13-sc-sleuth-dubbo/pom.xml ================================================ labx-13 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-13-sc-sleuth-dubbo pom labx-13-sc-sleuth-dubbo-api labx-13-sc-sleuth-dubbo-provider labx-13-sc-sleuth-dubbo-consumer ================================================ FILE: labx-13/labx-13-sc-sleuth-feign/pom.xml ================================================ labx-13 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-13-sc-sleuth-feign 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-zipkin org.springframework.cloud spring-cloud-starter-openfeign ================================================ FILE: labx-13/labx-13-sc-sleuth-feign/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/FeignApplication.java ================================================ package cn.iocoder.springcloud.labx13.springmvcdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication @EnableFeignClients public class FeignApplication { public static void main(String[] args) { SpringApplication.run(FeignApplication.class, args); } } ================================================ FILE: labx-13/labx-13-sc-sleuth-feign/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/controller/FeignController.java ================================================ package cn.iocoder.springcloud.labx13.springmvcdemo.controller; import cn.iocoder.springcloud.labx13.springmvcdemo.feign.UserServiceFeignClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/feign") public class FeignController { @Autowired private UserServiceFeignClient userServiceFeignClient; @GetMapping("/get") public String get(@RequestParam("id") Integer id) { return userServiceFeignClient.get(id); } } ================================================ FILE: labx-13/labx-13-sc-sleuth-feign/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/feign/UserServiceFeignClient.java ================================================ package cn.iocoder.springcloud.labx13.springmvcdemo.feign; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; @FeignClient(name = "user-service", url = "http://127.0.0.1:8080") public interface UserServiceFeignClient { @GetMapping("/user/get") String get(@RequestParam("id") Integer id); } ================================================ FILE: labx-13/labx-13-sc-sleuth-feign/src/main/resources/application.yml ================================================ server: port: 8081 spring: application: name: feign-service # 服务名 # Zipkin 配置项,对应 ZipkinProperties 类 zipkin: base-url: http://127.0.0.1:9411 # Zipkin 服务的地址 # Spring Cloud Sleuth 配置项 sleuth: # Spring Cloud Sleuth 针对 Web 组件的配置项,例如说 SpringMVC web: enabled: true # 是否开启,默认为 true # Spring Cloud Sleuth 针对 Feign 组件的配置项,对应 SleuthFeignProperties 类 feign: enabled: true # 是否开启,默认为 true ================================================ FILE: labx-13/labx-13-sc-sleuth-logback/pom.xml ================================================ labx-13 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-13-sc-sleuth-logback 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-zipkin ================================================ FILE: labx-13/labx-13-sc-sleuth-logback/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/UserServiceApplication.java ================================================ package cn.iocoder.springcloud.labx13.springmvcdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class UserServiceApplication { public static void main(String[] args) { SpringApplication.run(UserServiceApplication.class, args); } } ================================================ FILE: labx-13/labx-13-sc-sleuth-logback/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/controller/UserController.java ================================================ package cn.iocoder.springcloud.labx13.springmvcdemo.controller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/user") public class UserController { private Logger logger = LoggerFactory.getLogger(getClass()); @GetMapping("/get") public String get(@RequestParam("id") Integer id) { logger.info("测试日志"); return "user:" + id; } } ================================================ FILE: labx-13/labx-13-sc-sleuth-logback/src/main/resources/application.yml ================================================ spring: application: name: user-service # 服务名 # Zipkin 配置项,对应 ZipkinProperties 类 zipkin: base-url: http://127.0.0.1:9411 # Zipkin 服务的地址 # Spring Cloud Sleuth 配置项 sleuth: # Spring Cloud Sleuth 针对 Web 组件的配置项,例如说 SpringMVC web: enabled: true # 是否开启,默认为 true # Spring Cloud Sleuth 针对 Slf4j 组件的配置项 log: slf4j: enabled: true # 是否开启,默认为 true ================================================ FILE: labx-13/labx-13-sc-sleuth-mq-activemq/pom.xml ================================================ labx-13 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-13-sc-sleuth-mq-activemq 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-zipkin org.springframework.boot spring-boot-starter-activemq ================================================ FILE: labx-13/labx-13-sc-sleuth-mq-activemq/src/main/java/cn/iocoder/springboot/labx13/activemqdemo/ActiveMQApplication.java ================================================ package cn.iocoder.springboot.labx13.activemqdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ActiveMQApplication { public static void main(String[] args) { SpringApplication.run(ActiveMQApplication.class, args); } } ================================================ FILE: labx-13/labx-13-sc-sleuth-mq-activemq/src/main/java/cn/iocoder/springboot/labx13/activemqdemo/consumer/DemoConsumer.java ================================================ package cn.iocoder.springboot.labx13.activemqdemo.consumer; import cn.iocoder.springboot.labx13.activemqdemo.message.DemoMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jms.annotation.JmsListener; import org.springframework.stereotype.Component; @Component public class DemoConsumer { private Logger logger = LoggerFactory.getLogger(getClass()); @JmsListener(destination = DemoMessage.QUEUE) public void onMessage(DemoMessage message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: labx-13/labx-13-sc-sleuth-mq-activemq/src/main/java/cn/iocoder/springboot/labx13/activemqdemo/controller/DemoController.java ================================================ package cn.iocoder.springboot.labx13.activemqdemo.controller; import cn.iocoder.springboot.labx13.activemqdemo.producer.DemoProducer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @Autowired private DemoProducer producer; @GetMapping("/activemq") public String echo() { this.sendMessage(1); return "activemq"; } public void sendMessage(Integer id) { producer.syncSend(id); } } ================================================ FILE: labx-13/labx-13-sc-sleuth-mq-activemq/src/main/java/cn/iocoder/springboot/labx13/activemqdemo/message/DemoMessage.java ================================================ package cn.iocoder.springboot.labx13.activemqdemo.message; import java.io.Serializable; public class DemoMessage implements Serializable { public static final String QUEUE = "QUEUE_DEMO_"; /** * 编号 */ private Integer id; public DemoMessage setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "DemoMessage{" + "id=" + id + '}'; } } ================================================ FILE: labx-13/labx-13-sc-sleuth-mq-activemq/src/main/java/cn/iocoder/springboot/labx13/activemqdemo/producer/DemoProducer.java ================================================ package cn.iocoder.springboot.labx13.activemqdemo.producer; import cn.iocoder.springboot.labx13.activemqdemo.message.DemoMessage; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jms.core.JmsMessagingTemplate; import org.springframework.stereotype.Component; @Component public class DemoProducer { @Autowired private JmsMessagingTemplate jmsTemplate; public void syncSend(Integer id) { // 创建 DemoMessage 消息 DemoMessage message = new DemoMessage(); message.setId(id); // 同步发送消息 jmsTemplate.convertAndSend(DemoMessage.QUEUE, message); } } ================================================ FILE: labx-13/labx-13-sc-sleuth-mq-activemq/src/main/resources/application.yaml ================================================ spring: application: name: demo-application-activemq # ActiveMQ 配置项,对应 ActiveMQProperties 配置类 activemq: broker-url: tcp://127.0.0.1:61616 # Activemq Broker 的地址 user: admin # 账号 password: admin # 密码 packages: trust-all: true # 可信任的反序列化包 # Zipkin 配置项,对应 ZipkinProperties 类 zipkin: base-url: http://127.0.0.1:9411 # Zipkin 服务的地址 # Spring Cloud Sleuth 配置项 sleuth: messaging: # Spring Cloud Sleuth 针对 JMS 组件的配置项 jms: enabled: true # 是否开启 remote-service-name: jms # 远程服务名,默认为 jms ================================================ FILE: labx-13/labx-13-sc-sleuth-mq-kafka/labx-13-sc-sleuth-mq-kafka-producer/pom.xml ================================================ labx-13-sc-sleuth-mq-kafka cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-13-sc-sleuth-mq-kafka-producer 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-kafka org.springframework.cloud spring-cloud-starter-zipkin ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx13.kafkademo.producerdemo; import cn.iocoder.springcloud.labx13.kafkademo.producerdemo.message.MySource; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySource.class) public class ProducerApplication { public static void main(String[] args) { SpringApplication.run(ProducerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx13.kafkademo.producerdemo.controller; import cn.iocoder.springcloud.labx13.kafkademo.producerdemo.message.Demo01Message; import cn.iocoder.springcloud.labx13.kafkademo.producerdemo.message.MySource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.Message; import org.springframework.messaging.support.MessageBuilder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Random; @RestController @RequestMapping("/demo01") public class Demo01Controller { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private MySource mySource; @GetMapping("/send") public boolean send() { // 创建 Message Demo01Message message = new Demo01Message() .setId(new Random().nextInt()); // 创建 Spring Message 对象 Message springMessage = MessageBuilder.withPayload(message) .build(); // 发送消息 return mySource.demo01Output().send(springMessage); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx13.kafkademo.producerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx13.kafkademo.producerdemo.message; import org.springframework.cloud.stream.annotation.Output; import org.springframework.messaging.MessageChannel; public interface MySource { @Output("demo01-output") MessageChannel demo01Output(); } ================================================ FILE: labx-13/labx-13-sc-sleuth-mq-kafka/labx-13-sc-sleuth-mq-kafka-producer/src/main/resources/application.yml ================================================ spring: application: name: demo-producer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binder 配置项,对应 BinderProperties Map # binders: # Binding 配置项,对应 BindingProperties Map bindings: demo01-output: destination: DEMO-TOPIC-01 # 目的地。这里使用 Kafka Topic content-type: application/json # 内容格式。这里使用 JSON # Spring Cloud Stream Kafka 配置项 kafka: # Kafka Binder 配置项,对应 KafkaBinderConfigurationProperties 类 binder: brokers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 # Kafka 自定义 Binding 配置项,对应 KafkaBindingProperties Map bindings: demo01-output: # Kafka Producer 配置项,对应 KafkaProducerProperties 类 producer: sync: true # 是否同步发送消息,默认为 false 异步。 # Zipkin 配置项,对应 ZipkinProperties 类 zipkin: base-url: http://127.0.0.1:9411 # Zipkin 服务的地址 # Spring Cloud Sleuth 配置项 sleuth: messaging: # Spring Cloud Sleuth 针对 Kafka 组件的配置项 kafka: enabled: true # 是否开启 remote-service-name: kafka # 远程服务名,默认为 kafka server: port: 18080 ================================================ FILE: labx-13/labx-13-sc-sleuth-mq-kafka/labx-13-sc-stream-mq-kafka-consumer/pom.xml ================================================ labx-13-sc-sleuth-mq-kafka cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-13-sc-stream-mq-kafka-consumer 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-kafka org.springframework.cloud spring-cloud-starter-zipkin ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx13.kafkademo.consumerdemo; import cn.iocoder.springcloud.labx13.kafkademo.consumerdemo.listener.MySink; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySink.class) public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx13.kafkademo.consumerdemo.listener; import cn.iocoder.springcloud.labx13.kafkademo.consumerdemo.message.Demo01Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.messaging.handler.annotation.Payload; import org.springframework.stereotype.Component; @Component public class Demo01Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @StreamListener(MySink.DEMO01_INPUT) public void onMessage(@Payload Demo01Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx13.kafkademo.consumerdemo.listener; import org.springframework.cloud.stream.annotation.Input; import org.springframework.messaging.SubscribableChannel; public interface MySink { String DEMO01_INPUT = "demo01-input"; @Input(DEMO01_INPUT) SubscribableChannel demo01Input(); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx13.kafkademo.consumerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: labx-13/labx-13-sc-sleuth-mq-kafka/labx-13-sc-stream-mq-kafka-consumer/src/main/resources/application.yml ================================================ spring: application: name: demo-consumer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binder 配置项,对应 BinderProperties Map # binders: # Binding 配置项,对应 BindingProperties Map bindings: demo01-input: destination: DEMO-TOPIC-01 # 目的地。这里使用 Kafka Topic content-type: application/json # 内容格式。这里使用 JSON group: demo01-consumer-group # 消费者分组 # Spring Cloud Stream Kafka 配置项 kafka: # Kafka Binder 配置项,对应 KafkaBinderConfigurationProperties 类 binder: brokers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 # Zipkin 配置项,对应 ZipkinProperties 类 zipkin: base-url: http://127.0.0.1:9411 # Zipkin 服务的地址 # Spring Cloud Sleuth 配置项 sleuth: messaging: # Spring Cloud Sleuth 针对 kafka 组件的配置项kafka kafka: enabled: true # 是否开启 remote-service-name: kafka # 远程服务名,默认为 kafka server: port: ${random.int[10000,19999]} # 随机端口,方便启动多个消费者 ================================================ FILE: labx-13/labx-13-sc-sleuth-mq-kafka/pom.xml ================================================ labx-13 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-13-sc-sleuth-mq-kafka pom labx-13-sc-sleuth-mq-kafka-producer labx-13-sc-stream-mq-kafka-consumer ================================================ FILE: labx-13/labx-13-sc-sleuth-mq-rabbitmq/labx-13-sc-sleuth-mq-rabbitmq-producer/pom.xml ================================================ labx-13-sc-sleuth-mq-rabbitmq cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-13-sc-sleuth-mq-rabbitmq-producer 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-rabbit org.springframework.cloud spring-cloud-starter-zipkin ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx13.rabbitmqdemo.producerdemo; import cn.iocoder.springcloud.labx13.rabbitmqdemo.producerdemo.message.MySource; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySource.class) public class ProducerApplication { public static void main(String[] args) { SpringApplication.run(ProducerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx13.rabbitmqdemo.producerdemo.controller; import cn.iocoder.springcloud.labx13.rabbitmqdemo.producerdemo.message.Demo01Message; import cn.iocoder.springcloud.labx13.rabbitmqdemo.producerdemo.message.MySource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.Message; import org.springframework.messaging.support.MessageBuilder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Random; @RestController @RequestMapping("/demo01") public class Demo01Controller { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private MySource mySource; @GetMapping("/send") public boolean send() { // 创建 Message Demo01Message message = new Demo01Message() .setId(new Random().nextInt()); // 创建 Spring Message 对象 Message springMessage = MessageBuilder.withPayload(message) .build(); // 发送消息 return mySource.demo01Output().send(springMessage); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx13.rabbitmqdemo.producerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx13.rabbitmqdemo.producerdemo.message; import org.springframework.cloud.stream.annotation.Output; import org.springframework.messaging.MessageChannel; public interface MySource { @Output("demo01-output") MessageChannel demo01Output(); } ================================================ FILE: labx-13/labx-13-sc-sleuth-mq-rabbitmq/labx-13-sc-sleuth-mq-rabbitmq-producer/src/main/resources/application.yml ================================================ spring: application: name: demo-producer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binder 配置项,对应 BinderProperties Map binders: rabbit001: type: rabbit # 设置 Binder 的类型 environment: # 设置 Binder 的环境配置 # 如果是 RabbitMQ 类型的时候,则对应的是 RabbitProperties 类 spring: rabbitmq: host: 127.0.0.1 # RabbitMQ 服务的地址 port: 5672 # RabbitMQ 服务的端口 username: guest # RabbitMQ 服务的账号 password: guest # RabbitMQ 服务的密码 # Binding 配置项,对应 BindingProperties Map bindings: demo01-output: destination: DEMO-TOPIC-01 # 目的地。这里使用 RabbitMQ Exchange content-type: application/json # 内容格式。这里使用 JSON binder: rabbit001 # 设置使用的 Binder 名字 # Zipkin 配置项,对应 ZipkinProperties 类 zipkin: base-url: http://127.0.0.1:9411 # Zipkin 服务的地址 # Spring Cloud Sleuth 配置项 sleuth: messaging: # Spring Cloud Sleuth 针对 RabbitMQ 组件的配置项 rabbit: enabled: true # 是否开启 remote-service-name: rabbitmq # 远程服务名,默认为 rabbitmq server: port: 18080 ================================================ FILE: labx-13/labx-13-sc-sleuth-mq-rabbitmq/labx-13-sc-stream-mq-rabbitmq-consumer/pom.xml ================================================ labx-13-sc-sleuth-mq-rabbitmq cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-13-sc-stream-mq-rabbitmq-consumer 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-rabbit org.springframework.cloud spring-cloud-starter-zipkin ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx13.rabbitmqdemo.consumerdemo; import cn.iocoder.springcloud.labx13.rabbitmqdemo.consumerdemo.listener.MySink; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySink.class) public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx13.rabbitmqdemo.consumerdemo.listener; import cn.iocoder.springcloud.labx13.rabbitmqdemo.consumerdemo.message.Demo01Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.messaging.handler.annotation.Payload; import org.springframework.stereotype.Component; @Component public class Demo01Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @StreamListener(MySink.DEMO01_INPUT) public void onMessage(@Payload Demo01Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx13.rabbitmqdemo.consumerdemo.listener; import org.springframework.cloud.stream.annotation.Input; import org.springframework.messaging.SubscribableChannel; public interface MySink { String DEMO01_INPUT = "demo01-input"; @Input(DEMO01_INPUT) SubscribableChannel demo01Input(); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx13.rabbitmqdemo.consumerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: labx-13/labx-13-sc-sleuth-mq-rabbitmq/labx-13-sc-stream-mq-rabbitmq-consumer/src/main/resources/application.yml ================================================ spring: application: name: demo-consumer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binder 配置项,对应 BinderProperties Map binders: rabbit001: type: rabbit # 设置 Binder 的类型 environment: # 设置 Binder 的环境配置 # 如果是 RabbitMQ 类型的时候,则对应的是 RabbitProperties 类 spring: rabbitmq: host: 127.0.0.1 # RabbitMQ 服务的地址 port: 5672 # RabbitMQ 服务的端口 username: guest # RabbitMQ 服务的账号 password: guest # RabbitMQ 服务的密码 # Binding 配置项,对应 BindingProperties Map bindings: demo01-input: destination: DEMO-TOPIC-01 # 目的地。这里使用 RabbitMQ Exchange content-type: application/json # 内容格式。这里使用 JSON group: demo01-consumer-group-DEMO-TOPIC-01 # 消费者分组 binder: rabbit001 # 设置使用的 Binder 名字 # Zipkin 配置项,对应 ZipkinProperties 类 zipkin: base-url: http://127.0.0.1:9411 # Zipkin 服务的地址 # Spring Cloud Sleuth 配置项 sleuth: messaging: # Spring Cloud Sleuth 针对 RabbitMQ 组件的配置项 rabbit: enabled: true # 是否开启 remote-service-name: rabbitmq # 远程服务名,默认为 rabbitmq server: port: ${random.int[10000,19999]} # 随机端口,方便启动多个消费者 ================================================ FILE: labx-13/labx-13-sc-sleuth-mq-rabbitmq/pom.xml ================================================ labx-13 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-13-sc-sleuth-mq-rabbitmq pom labx-13-sc-sleuth-mq-rabbitmq-producer labx-13-sc-stream-mq-rabbitmq-consumer ================================================ FILE: labx-13/labx-13-sc-sleuth-opentracing/pom.xml ================================================ labx-13 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-13-sc-sleuth-opentracing 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-zipkin io.opentracing.brave brave-opentracing 0.35.0 ================================================ FILE: labx-13/labx-13-sc-sleuth-opentracing/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/UserServiceApplication.java ================================================ package cn.iocoder.springcloud.labx13.springmvcdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class UserServiceApplication { public static void main(String[] args) { SpringApplication.run(UserServiceApplication.class, args); } } ================================================ FILE: labx-13/labx-13-sc-sleuth-opentracing/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/controller/UserController.java ================================================ package cn.iocoder.springcloud.labx13.springmvcdemo.controller; import io.opentracing.Tracer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/user") public class UserController { @Autowired private Tracer tracer; @GetMapping("/get") public String get(@RequestParam("id") Integer id) { // 创建一个 Span tracer.buildSpan("custom_operation").withTag("mp", "芋道源码").start().finish(); return "user:" + id; } } ================================================ FILE: labx-13/labx-13-sc-sleuth-opentracing/src/main/resources/application.yml ================================================ spring: application: name: user-service # 服务名 # Zipkin 配置项,对应 ZipkinProperties 类 zipkin: base-url: http://127.0.0.1:9411 # Zipkin 服务的地址 # Spring Cloud Sleuth 配置项 sleuth: # Spring Cloud Sleuth 针对 Web 组件的配置项,例如说 SpringMVC web: enabled: true # 是否开启,默认为 true # Spring Cloud Sleuth 针对 OpenTracing 组件的配置项 opentracing: enabled: true # 是否开启,默认为 true ================================================ FILE: labx-13/labx-13-sc-sleuth-sampler/pom.xml ================================================ labx-13 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-13-sc-sleuth-sampler 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-zipkin ================================================ FILE: labx-13/labx-13-sc-sleuth-sampler/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/UserServiceApplication.java ================================================ package cn.iocoder.springcloud.labx13.springmvcdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class UserServiceApplication { public static void main(String[] args) { SpringApplication.run(UserServiceApplication.class, args); } } ================================================ FILE: labx-13/labx-13-sc-sleuth-sampler/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/controller/UserController.java ================================================ package cn.iocoder.springcloud.labx13.springmvcdemo.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/user") public class UserController { @GetMapping("/get") public String get(@RequestParam("id") Integer id) { return "user:" + id; } } ================================================ FILE: labx-13/labx-13-sc-sleuth-sampler/src/main/resources/application.yml ================================================ spring: application: name: user-service # 服务名 # Zipkin 配置项,对应 ZipkinProperties 类 zipkin: base-url: http://127.0.0.1:9411 # Zipkin 服务的地址 # Spring Cloud Sleuth 配置项 sleuth: # Spring Cloud Sleuth 针对 Web 组件的配置项,例如说 SpringMVC web: enabled: true # 是否开启,默认为 true # Spring Cloud Sleuth 针对抽样收集的配置项 sampler: probability: 0.1 # 采样百分比,默认为空。 # rate: 1 # 限流采样,即每秒可收集链路的数量,默认为 10。 ================================================ FILE: labx-13/labx-13-sc-sleuth-springcloudgateway/pom.xml ================================================ labx-13 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-13-sc-sleuth-springcloudgateway 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.cloud spring-cloud-starter-gateway org.springframework.cloud spring-cloud-starter-zipkin ================================================ FILE: labx-13/labx-13-sc-sleuth-springcloudgateway/src/main/java/cn/iocoder/springcloud/labx13/gatewaydemo/GatewayApplication.java ================================================ package cn.iocoder.springcloud.labx13.gatewaydemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } } ================================================ FILE: labx-13/labx-13-sc-sleuth-springcloudgateway/src/main/resources/application.yaml ================================================ server: port: 8888 spring: application: name: gateway-application # Zipkin 配置项,对应 ZipkinProperties 类 zipkin: base-url: http://127.0.0.1:9411 # Zipkin 服务的地址 # Spring Cloud Sleuth 配置项 sleuth: # Spring Cloud Sleuth 针对 Web 组件的配置项,例如说 SpringMVC web: enabled: true # 是否开启,默认为 true cloud: # Spring Cloud Gateway 配置项,对应 GatewayProperties 类 gateway: # 路由配置项,对应 RouteDefinition 数组 routes: - id: feign-service-route uri: http://127.0.0.1:8081 predicates: - Path=/** ================================================ FILE: labx-13/labx-13-sc-sleuth-springmvc/pom.xml ================================================ labx-13 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-13-sc-sleuth-springmvc 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-zipkin ================================================ FILE: labx-13/labx-13-sc-sleuth-springmvc/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/UserServiceApplication.java ================================================ package cn.iocoder.springcloud.labx13.springmvcdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class UserServiceApplication { public static void main(String[] args) { SpringApplication.run(UserServiceApplication.class, args); } } ================================================ FILE: labx-13/labx-13-sc-sleuth-springmvc/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/controller/UserController.java ================================================ package cn.iocoder.springcloud.labx13.springmvcdemo.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/user") public class UserController { @GetMapping("/get") public String get(@RequestParam("id") Integer id) { return "user:" + id; } } ================================================ FILE: labx-13/labx-13-sc-sleuth-springmvc/src/main/resources/application.yml ================================================ spring: application: name: user-service # 服务名 # Zipkin 配置项,对应 ZipkinProperties 类 zipkin: base-url: http://127.0.0.1:9411 # Zipkin 服务的地址 # Spring Cloud Sleuth 配置项 sleuth: # Spring Cloud Sleuth 针对 Web 组件的配置项,例如说 SpringMVC web: enabled: true # 是否开启,默认为 true ================================================ FILE: labx-13/labx-13-sc-sleuth-zipkin-server-demo-in-memory/pom.xml ================================================ labx-13 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-13-sc-sleuth-zipkin-server-demo-in-memory 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.2.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web io.zipkin.java zipkin-server 2.12.9 io.zipkin.java zipkin-autoconfigure-ui 2.12.9 org.springframework.cloud spring-cloud-starter-sleuth 2.2.2.RELEASE ================================================ FILE: labx-13/labx-13-sc-sleuth-zipkin-server-demo-in-memory/src/main/java/cn/iocoder/springcloud/labx13/sleuthzipkinserverdemo/SleuthZipkinServerApplication.java ================================================ package cn.iocoder.springcloud.labx13.sleuthzipkinserverdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import zipkin2.server.internal.EnableZipkinServer; @SpringBootApplication @EnableZipkinServer public class SleuthZipkinServerApplication { public static void main(String[] args) { SpringApplication.run(SleuthZipkinServerApplication.class, args); } } ================================================ FILE: labx-13/labx-13-sc-sleuth-zipkin-server-demo-in-memory/src/main/resources/application.yml ================================================ server: port: 9411 ================================================ FILE: labx-13/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-13 pom labx-13-sc-sleuth-zipkin-server-demo-in-memory labx-13-sc-sleuth-springmvc labx-13-sc-sleuth-feign labx-13-sc-sleuth-springcloudgateway labx-13-sc-sleuth-dubbo labx-13-sc-sleuth-db-mysql labx-13-sc-sleuth-db-redis labx-13-sc-sleuth-db-mongodb labx-13-sc-sleuth-db-elasticsearch labx-13-sc-sleuth-mq-rabbitmq labx-13-sc-sleuth-mq-kafka labx-13-sc-sleuth-mq-activemq labx-13-sc-sleuth-logback labx-13-sc-sleuth-opentracing labx-13-sc-sleuth-sampler ================================================ FILE: labx-13/《芋道 Spring Cloud 链路追踪 Spring Cloud Sleuth》.md ================================================ ================================================ FILE: labx-14/labx-14-sc-skywalking-dubbo/labx-14-sc-skywalking-dubbo-api/pom.xml ================================================ labx-14-sc-skywalking-dubbo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-14-sc-skywalking-dubbo-api ================================================ FILE: labx-14/labx-14-sc-skywalking-dubbo/labx-14-sc-skywalking-dubbo-api/src/main/java/cn/iocoder/springcloud/labx14/api/UserService.java ================================================ package cn.iocoder.springcloud.labx14.api; /** * 用户服务 RPC Service 接口 */ public interface UserService { /** * 根据指定用户编号,获得用户信息 * * @param id 用户编号 * @return 用户信息 */ String get(Integer id); } ================================================ FILE: labx-14/labx-14-sc-skywalking-dubbo/labx-14-sc-skywalking-dubbo-api/src/main/java/cn/iocoder/springcloud/labx14/package-info.java ================================================ package cn.iocoder.springcloud.labx13; ================================================ FILE: labx-14/labx-14-sc-skywalking-dubbo/labx-14-sc-skywalking-dubbo-consumer/pom.xml ================================================ labx-14-sc-skywalking-dubbo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-14-sc-skywalking-dubbo-consumer 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import cn.iocoder.springboot.labs labx-14-sc-skywalking-dubbo-api 1.0-SNAPSHOT org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery com.alibaba.cloud spring-cloud-starter-dubbo ================================================ FILE: labx-14/labx-14-sc-skywalking-dubbo/labx-14-sc-skywalking-dubbo-consumer/src/main/java/cn/iocoder/springcloud/labx14/consumerdemo/ConsumerApplication.java ================================================ package cn.iocoder.springcloud.labx14.consumerdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class); } } ================================================ FILE: labx-14/labx-14-sc-skywalking-dubbo/labx-14-sc-skywalking-dubbo-consumer/src/main/java/cn/iocoder/springcloud/labx14/consumerdemo/controller/UserController.java ================================================ package cn.iocoder.springcloud.labx14.consumerdemo.controller; import cn.iocoder.springcloud.labx14.api.UserService; import org.apache.dubbo.config.annotation.Reference; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/user") public class UserController { @Reference(protocol = "dubbo", version = "1.0.0") private UserService userService; @GetMapping("/get") public String get(@RequestParam("id") Integer id) { return userService.get(id); } } ================================================ FILE: labx-14/labx-14-sc-skywalking-dubbo/labx-14-sc-skywalking-dubbo-consumer/src/main/resources/application.yaml ================================================ server: port: 8079 spring: application: name: demo-consumer cloud: # Nacos 作为注册中心的配置项 nacos: discovery: server-addr: 127.0.0.1:8848 # Dubbo 配置项,对应 DubboConfigurationProperties 类 dubbo: # Dubbo 服务注册中心配置,对应 RegistryConfig 类 registry: address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址 # Spring Cloud Alibaba Dubbo 专属配置项,对应 DubboCloudProperties 类 cloud: subscribed-services: demo-provider # 设置订阅的应用列表,默认为 * 订阅所有应用。 ================================================ FILE: labx-14/labx-14-sc-skywalking-dubbo/labx-14-sc-skywalking-dubbo-provider/pom.xml ================================================ labx-14-sc-skywalking-dubbo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-14-sc-skywalking-dubbo-provider 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import cn.iocoder.springboot.labs labx-14-sc-skywalking-dubbo-api 1.0-SNAPSHOT com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery com.alibaba.cloud spring-cloud-starter-dubbo ================================================ FILE: labx-14/labx-14-sc-skywalking-dubbo/labx-14-sc-skywalking-dubbo-provider/src/main/java/cn/iocoder/springcloud/labx14/providerdemo/ProviderApplication.java ================================================ package cn.iocoder.springcloud.labx14.providerdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ProviderApplication { public static void main(String[] args) { SpringApplication.run(ProviderApplication.class); } } ================================================ FILE: labx-14/labx-14-sc-skywalking-dubbo/labx-14-sc-skywalking-dubbo-provider/src/main/java/cn/iocoder/springcloud/labx14/providerdemo/service/UserServiceImpl.java ================================================ package cn.iocoder.springcloud.labx14.providerdemo.service; import cn.iocoder.springcloud.labx14.api.UserService; @org.apache.dubbo.config.annotation.Service(protocol = "dubbo", version = "1.0.0") public class UserServiceImpl implements UserService { @Override public String get(Integer id) { return "user:" + id; } } ================================================ FILE: labx-14/labx-14-sc-skywalking-dubbo/labx-14-sc-skywalking-dubbo-provider/src/main/resources/application.yaml ================================================ spring: application: name: demo-provider # Dubbo 配置项,对应 DubboConfigurationProperties 类 dubbo: scan: base-packages: cn.iocoder.springcloud.labx14.providerdemo.service # 指定 Dubbo 服务实现类的扫描基准包 # Dubbo 服务暴露的协议配置,对应 ProtocolConfig Map protocols: dubbo: name: dubbo # 协议名称 port: -1 # 协议端口,-1 表示自增端口,从 20880 开始 # Dubbo 服务注册中心配置,对应 RegistryConfig 类 registry: address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址 # Spring Cloud Alibaba Dubbo 专属配置项,对应 DubboCloudProperties 类 cloud: subscribed-services: '' # 设置订阅的应用列表,默认为 * 订阅所有应用。 ================================================ FILE: labx-14/labx-14-sc-skywalking-dubbo/pom.xml ================================================ labx-14 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-14-sc-skywalking-dubbo pom labx-14-sc-skywalking-dubbo-api labx-14-sc-skywalking-dubbo-provider labx-14-sc-skywalking-dubbo-consumer ================================================ FILE: labx-14/labx-14-sc-skywalking-feign/pom.xml ================================================ labx-14 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-14-sc-skywalking-feign 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-openfeign ================================================ FILE: labx-14/labx-14-sc-skywalking-feign/src/main/java/cn/iocoder/springcloud/labx14/springmvcdemo/FeignApplication.java ================================================ package cn.iocoder.springcloud.labx14.springmvcdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication @EnableFeignClients public class FeignApplication { public static void main(String[] args) { SpringApplication.run(FeignApplication.class, args); } } ================================================ FILE: labx-14/labx-14-sc-skywalking-feign/src/main/java/cn/iocoder/springcloud/labx14/springmvcdemo/controller/FeignController.java ================================================ package cn.iocoder.springcloud.labx14.springmvcdemo.controller; import cn.iocoder.springcloud.labx14.springmvcdemo.feign.UserServiceFeignClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/feign") public class FeignController { @Autowired private UserServiceFeignClient userServiceFeignClient; @GetMapping("/get") public String get(@RequestParam("id") Integer id) { return userServiceFeignClient.get(id); } } ================================================ FILE: labx-14/labx-14-sc-skywalking-feign/src/main/java/cn/iocoder/springcloud/labx14/springmvcdemo/feign/UserServiceFeignClient.java ================================================ package cn.iocoder.springcloud.labx14.springmvcdemo.feign; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; @FeignClient(name = "user-service", url = "http://127.0.0.1:8079") public interface UserServiceFeignClient { @GetMapping("/user/get") String get(@RequestParam("id") Integer id); } ================================================ FILE: labx-14/labx-14-sc-skywalking-feign/src/main/resources/application.yml ================================================ server: port: 8081 spring: application: name: feign-service # 服务名 ================================================ FILE: labx-14/labx-14-sc-skywalking-mq-kafka/labx-14-sc-skywalking-mq-kafka-consumer/pom.xml ================================================ labx-14-sc-skywalking-mq-kafka cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-14-sc-skywalking-mq-kafka-consumer 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-kafka ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx14.kafkademo.consumerdemo; import cn.iocoder.springcloud.labx14.kafkademo.consumerdemo.listener.MySink; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySink.class) public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx14.kafkademo.consumerdemo.listener; import cn.iocoder.springcloud.labx14.kafkademo.consumerdemo.message.Demo01Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.messaging.handler.annotation.Payload; import org.springframework.stereotype.Component; @Component public class Demo01Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @StreamListener(MySink.DEMO01_INPUT) public void onMessage(@Payload Demo01Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx14.kafkademo.consumerdemo.listener; import org.springframework.cloud.stream.annotation.Input; import org.springframework.messaging.SubscribableChannel; public interface MySink { String DEMO01_INPUT = "demo01-input"; @Input(DEMO01_INPUT) SubscribableChannel demo01Input(); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx14.kafkademo.consumerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: labx-14/labx-14-sc-skywalking-mq-kafka/labx-14-sc-skywalking-mq-kafka-consumer/src/main/resources/application.yml ================================================ spring: application: name: demo-consumer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binder 配置项,对应 BinderProperties Map # binders: # Binding 配置项,对应 BindingProperties Map bindings: demo01-input: destination: DEMO-TOPIC-01 # 目的地。这里使用 Kafka Topic content-type: application/json # 内容格式。这里使用 JSON group: demo01-consumer-group # 消费者分组 # Spring Cloud Stream Kafka 配置项 kafka: # Kafka Binder 配置项,对应 KafkaBinderConfigurationProperties 类 binder: brokers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 server: port: ${random.int[10000,19999]} # 随机端口,方便启动多个消费者 ================================================ FILE: labx-14/labx-14-sc-skywalking-mq-kafka/labx-14-sc-skywalking-mq-kafka-producer/pom.xml ================================================ labx-14-sc-skywalking-mq-kafka cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-14-sc-skywalking-mq-kafka-producer 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-kafka ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx14.kafkademo.producerdemo; import cn.iocoder.springcloud.labx14.kafkademo.producerdemo.message.MySource; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySource.class) public class ProducerApplication { public static void main(String[] args) { SpringApplication.run(ProducerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx14.kafkademo.producerdemo.controller; import cn.iocoder.springcloud.labx14.kafkademo.producerdemo.message.Demo01Message; import cn.iocoder.springcloud.labx14.kafkademo.producerdemo.message.MySource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.Message; import org.springframework.messaging.support.MessageBuilder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Random; @RestController @RequestMapping("/demo01") public class Demo01Controller { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private MySource mySource; @GetMapping("/send") public boolean send() { // 创建 Message Demo01Message message = new Demo01Message() .setId(new Random().nextInt()); // 创建 Spring Message 对象 Message springMessage = MessageBuilder.withPayload(message) .build(); // 发送消息 return mySource.demo01Output().send(springMessage); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx14.kafkademo.producerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx14.kafkademo.producerdemo.message; import org.springframework.cloud.stream.annotation.Output; import org.springframework.messaging.MessageChannel; public interface MySource { @Output("demo01-output") MessageChannel demo01Output(); } ================================================ FILE: labx-14/labx-14-sc-skywalking-mq-kafka/labx-14-sc-skywalking-mq-kafka-producer/src/main/resources/application.yml ================================================ spring: application: name: demo-producer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binder 配置项,对应 BinderProperties Map # binders: # Binding 配置项,对应 BindingProperties Map bindings: demo01-output: destination: DEMO-TOPIC-01 # 目的地。这里使用 Kafka Topic content-type: application/json # 内容格式。这里使用 JSON # Spring Cloud Stream Kafka 配置项 kafka: # Kafka Binder 配置项,对应 KafkaBinderConfigurationProperties 类 binder: brokers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 # Kafka 自定义 Binding 配置项,对应 KafkaBindingProperties Map bindings: demo01-output: # Kafka Producer 配置项,对应 KafkaProducerProperties 类 producer: sync: true # 是否同步发送消息,默认为 false 异步。 server: port: 18080 ================================================ FILE: labx-14/labx-14-sc-skywalking-mq-kafka/pom.xml ================================================ labx-14 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-14-sc-skywalking-mq-kafka pom labx-14-sc-skywalking-mq-kafka-producer labx-14-sc-skywalking-mq-kafka-consumer ================================================ FILE: labx-14/labx-14-sc-skywalking-mq-rabbitmq/labx-14-sc-skywalking-mq-rabbitmq-consumer/pom.xml ================================================ labx-14-sc-skywalking-mq-rabbitmq cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-14-sc-skywalking-mq-rabbitmq-consumer 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-rabbit ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx14.rabbitmqdemo.consumerdemo; import cn.iocoder.springcloud.labx14.rabbitmqdemo.consumerdemo.listener.MySink; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySink.class) public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx14.rabbitmqdemo.consumerdemo.listener; import cn.iocoder.springcloud.labx14.rabbitmqdemo.consumerdemo.message.Demo01Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.messaging.handler.annotation.Payload; import org.springframework.stereotype.Component; @Component public class Demo01Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @StreamListener(MySink.DEMO01_INPUT) public void onMessage(@Payload Demo01Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx14.rabbitmqdemo.consumerdemo.listener; import org.springframework.cloud.stream.annotation.Input; import org.springframework.messaging.SubscribableChannel; public interface MySink { String DEMO01_INPUT = "demo01-input"; @Input(DEMO01_INPUT) SubscribableChannel demo01Input(); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx14.rabbitmqdemo.consumerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: labx-14/labx-14-sc-skywalking-mq-rabbitmq/labx-14-sc-skywalking-mq-rabbitmq-consumer/src/main/resources/application.yml ================================================ spring: application: name: demo-consumer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binder 配置项,对应 BinderProperties Map binders: rabbit001: type: rabbit # 设置 Binder 的类型 environment: # 设置 Binder 的环境配置 # 如果是 RabbitMQ 类型的时候,则对应的是 RabbitProperties 类 spring: rabbitmq: host: 127.0.0.1 # RabbitMQ 服务的地址 port: 5672 # RabbitMQ 服务的端口 username: guest # RabbitMQ 服务的账号 password: guest # RabbitMQ 服务的密码 # Binding 配置项,对应 BindingProperties Map bindings: demo01-input: destination: DEMO-TOPIC-01 # 目的地。这里使用 RabbitMQ Exchange content-type: application/json # 内容格式。这里使用 JSON group: demo01-consumer-group-DEMO-TOPIC-01 # 消费者分组 binder: rabbit001 # 设置使用的 Binder 名字 server: port: ${random.int[10000,19999]} # 随机端口,方便启动多个消费者 ================================================ FILE: labx-14/labx-14-sc-skywalking-mq-rabbitmq/labx-14-sc-skywalking-mq-rabbitmq-producer/pom.xml ================================================ labx-14-sc-skywalking-mq-rabbitmq cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-14-sc-skywalking-mq-rabbitmq-producer 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-stream-rabbit ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx14.rabbitmqdemo.producerdemo; import cn.iocoder.springcloud.labx14.rabbitmqdemo.producerdemo.message.MySource; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySource.class) public class ProducerApplication { public static void main(String[] args) { SpringApplication.run(ProducerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx14.rabbitmqdemo.producerdemo.controller; import cn.iocoder.springcloud.labx14.rabbitmqdemo.producerdemo.message.Demo01Message; import cn.iocoder.springcloud.labx14.rabbitmqdemo.producerdemo.message.MySource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.Message; import org.springframework.messaging.support.MessageBuilder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Random; @RestController @RequestMapping("/demo01") public class Demo01Controller { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private MySource mySource; @GetMapping("/send") public boolean send() { // 创建 Message Demo01Message message = new Demo01Message() .setId(new Random().nextInt()); // 创建 Spring Message 对象 Message springMessage = MessageBuilder.withPayload(message) .build(); // 发送消息 return mySource.demo01Output().send(springMessage); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx14.rabbitmqdemo.producerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx14.rabbitmqdemo.producerdemo.message; import org.springframework.cloud.stream.annotation.Output; import org.springframework.messaging.MessageChannel; public interface MySource { @Output("demo01-output") MessageChannel demo01Output(); } ================================================ FILE: labx-14/labx-14-sc-skywalking-mq-rabbitmq/labx-14-sc-skywalking-mq-rabbitmq-producer/src/main/resources/application.yml ================================================ spring: application: name: demo-producer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binder 配置项,对应 BinderProperties Map binders: rabbit001: type: rabbit # 设置 Binder 的类型 environment: # 设置 Binder 的环境配置 # 如果是 RabbitMQ 类型的时候,则对应的是 RabbitProperties 类 spring: rabbitmq: host: 127.0.0.1 # RabbitMQ 服务的地址 port: 5672 # RabbitMQ 服务的端口 username: guest # RabbitMQ 服务的账号 password: guest # RabbitMQ 服务的密码 # Binding 配置项,对应 BindingProperties Map bindings: demo01-output: destination: DEMO-TOPIC-01 # 目的地。这里使用 RabbitMQ Exchange content-type: application/json # 内容格式。这里使用 JSON binder: rabbit001 # 设置使用的 Binder 名字 server: port: 18080 ================================================ FILE: labx-14/labx-14-sc-skywalking-mq-rabbitmq/pom.xml ================================================ labx-14 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-14-sc-skywalking-mq-rabbitmq pom labx-14-sc-skywalking-mq-rabbitmq-producer labx-14-sc-skywalking-mq-rabbitmq-consumer ================================================ FILE: labx-14/labx-14-sc-skywalking-mq-rocketmq/labx-14-sc-skywalking-mq-rocketmq-consumer/pom.xml ================================================ labx-14-sc-skywalking-mq-rocketmq cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-14-sc-skywalking-mq-rocketmq-consumer 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-stream-rocketmq ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx14.rocketmqdemo.consumerdemo; import cn.iocoder.springcloud.labx14.rocketmqdemo.consumerdemo.listener.MySink; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySink.class) public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx14.rocketmqdemo.consumerdemo.listener; import cn.iocoder.springcloud.labx14.rocketmqdemo.consumerdemo.message.Demo01Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.messaging.handler.annotation.Payload; import org.springframework.stereotype.Component; @Component public class Demo01Consumer { private Logger logger = LoggerFactory.getLogger(getClass()); @StreamListener(MySink.DEMO01_INPUT) public void onMessage(@Payload Demo01Message message) { logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx14.rocketmqdemo.consumerdemo.listener; import org.springframework.cloud.stream.annotation.Input; import org.springframework.messaging.SubscribableChannel; public interface MySink { String DEMO01_INPUT = "demo01-input"; @Input(DEMO01_INPUT) SubscribableChannel demo01Input(); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx14.rocketmqdemo.consumerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: labx-14/labx-14-sc-skywalking-mq-rocketmq/labx-14-sc-skywalking-mq-rocketmq-consumer/src/main/resources/application.yml ================================================ spring: application: name: demo-consumer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binding 配置项,对应 BindingProperties Map bindings: demo01-input: destination: DEMO-TOPIC-01 # 目的地。这里使用 RocketMQ Topic content-type: application/json # 内容格式。这里使用 JSON group: demo01-consumer-group-DEMO-TOPIC-01 # 消费者分组 # Spring Cloud Stream RocketMQ 配置项 rocketmq: # RocketMQ Binder 配置项,对应 RocketMQBinderConfigurationProperties 类 binder: name-server: 127.0.0.1:9876 # RocketMQ Namesrv 地址 # RocketMQ 自定义 Binding 配置项,对应 RocketMQBindingProperties Map bindings: demo01-input: # RocketMQ Consumer 配置项,对应 RocketMQConsumerProperties 类 consumer: enabled: true # 是否开启消费,默认为 true broadcasting: false # 是否使用广播消费,默认为 false 使用集群消费 server: port: ${random.int[10000,19999]} # 随机端口,方便启动多个消费者 ================================================ FILE: labx-14/labx-14-sc-skywalking-mq-rocketmq/labx-14-sc-skywalking-mq-rocketmq-producer/pom.xml ================================================ labx-14-sc-skywalking-mq-rocketmq cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-14-sc-skywalking-mq-rocketmq-producer 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-stream-rocketmq ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx14.rocketmqdemo.producerdemo; import cn.iocoder.springcloud.labx14.rocketmqdemo.producerdemo.message.MySource; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.stream.annotation.EnableBinding; @SpringBootApplication @EnableBinding(MySource.class) public class ProducerApplication { public static void main(String[] args) { SpringApplication.run(ProducerApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx14.rocketmqdemo.producerdemo.controller; import cn.iocoder.springcloud.labx14.rocketmqdemo.producerdemo.message.Demo01Message; import cn.iocoder.springcloud.labx14.rocketmqdemo.producerdemo.message.MySource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.Message; import org.springframework.messaging.support.MessageBuilder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Random; @RestController @RequestMapping("/demo01") public class Demo01Controller { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private MySource mySource; @GetMapping("/send") public boolean send() { // 创建 Message Demo01Message message = new Demo01Message() .setId(new Random().nextInt()); // 创建 Spring Message 对象 Message springMessage = MessageBuilder.withPayload(message) .build(); // 发送消息 return mySource.demo01Output().send(springMessage); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx14.rocketmqdemo.producerdemo.message; /** * 示例 01 的 Message 消息 */ public class Demo01Message { /** * 编号 */ private Integer id; public Demo01Message setId(Integer id) { this.id = id; return this; } public Integer getId() { return id; } @Override public String toString() { return "Demo01Message{" + "id=" + id + '}'; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx14.rocketmqdemo.producerdemo.message; import org.springframework.cloud.stream.annotation.Output; import org.springframework.messaging.MessageChannel; public interface MySource { @Output("demo01-output") MessageChannel demo01Output(); } ================================================ FILE: labx-14/labx-14-sc-skywalking-mq-rocketmq/labx-14-sc-skywalking-mq-rocketmq-producer/src/main/resources/application.yml ================================================ spring: application: name: demo-producer-application cloud: # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类 stream: # Binding 配置项,对应 BindingProperties Map bindings: demo01-output: destination: DEMO-TOPIC-01 # 目的地。这里使用 RocketMQ Topic content-type: application/json # 内容格式。这里使用 JSON # Spring Cloud Stream RocketMQ 配置项 rocketmq: # RocketMQ Binder 配置项,对应 RocketMQBinderConfigurationProperties 类 binder: name-server: 127.0.0.1:9876 # RocketMQ Namesrv 地址 # RocketMQ 自定义 Binding 配置项,对应 RocketMQBindingProperties Map bindings: demo01-output: # RocketMQ Producer 配置项,对应 RocketMQProducerProperties 类 producer: group: test # 生产者分组 sync: true # 是否同步发送消息,默认为 false 异步。 server: port: 18080 ================================================ FILE: labx-14/labx-14-sc-skywalking-mq-rocketmq/pom.xml ================================================ labx-14 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-14-sc-skywalking-mq-rocketmq pom labx-14-sc-skywalking-mq-rocketmq-producer labx-14-sc-skywalking-mq-rocketmq-consumer ================================================ FILE: labx-14/labx-14-sc-skywalking-springcloudgateway/pom.xml ================================================ labx-14 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-14-sc-skywalking-springcloudgateway 2.1.13.RELEASE Greenwich.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.cloud spring-cloud-starter-gateway ================================================ FILE: labx-14/labx-14-sc-skywalking-springcloudgateway/src/main/java/cn/iocoder/springcloud/labx14/gatewaydemo/GatewayApplication.java ================================================ package cn.iocoder.springcloud.labx14.gatewaydemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } } ================================================ FILE: labx-14/labx-14-sc-skywalking-springcloudgateway/src/main/resources/application.yaml ================================================ server: port: 8888 spring: application: name: gateway-application cloud: # Spring Cloud Gateway 配置项,对应 GatewayProperties 类 gateway: # 路由配置项,对应 RouteDefinition 数组 routes: - id: feign-service-route uri: http://127.0.0.1:8081 predicates: - Path=/** ================================================ FILE: labx-14/labx-14-sc-skywalking-springmvc/pom.xml ================================================ labx-14 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-14-sc-skywalking-springmvc 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web ================================================ FILE: labx-14/labx-14-sc-skywalking-springmvc/src/main/java/cn/iocoder/springcloud/labx14/springmvcdemo/UserServiceApplication.java ================================================ package cn.iocoder.springcloud.labx14.springmvcdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class UserServiceApplication { public static void main(String[] args) { SpringApplication.run(UserServiceApplication.class, args); } } ================================================ FILE: labx-14/labx-14-sc-skywalking-springmvc/src/main/java/cn/iocoder/springcloud/labx14/springmvcdemo/controller/UserController.java ================================================ package cn.iocoder.springcloud.labx14.springmvcdemo.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/user") public class UserController { @GetMapping("/get") public String get(@RequestParam("id") Integer id) { return "user:" + id; } } ================================================ FILE: labx-14/labx-14-sc-skywalking-springmvc/src/main/resources/application.yml ================================================ server: port: 8079 spring: application: name: user-service # 服务名 ================================================ FILE: labx-14/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-14 pom labx-14-sc-skywalking-springmvc labx-14-sc-skywalking-feign labx-14-sc-skywalking-springcloudgateway labx-14-sc-skywalking-dubbo labx-14-sc-skywalking-mq-rocketmq labx-14-sc-skywalking-mq-rabbitmq labx-14-sc-skywalking-mq-kafka ================================================ FILE: labx-14/《芋道 Spring Cloud 链路追踪 SkyWalking》.md ================================================ ================================================ FILE: labx-15/labx-15-admin-02-adminserver/pom.xml ================================================ labx-15 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-15-admin-02-adminserver 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import de.codecentric spring-boot-admin-starter-server 2.2.2 com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery ================================================ FILE: labx-15/labx-15-admin-02-adminserver/src/main/java/cn/iocoder/springcloud/labx15/adminserver/AdminServerApplication.java ================================================ package cn.iocoder.springcloud.labx15.adminserver; import de.codecentric.boot.admin.server.config.EnableAdminServer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @EnableAdminServer public class AdminServerApplication { public static void main(String[] args) { SpringApplication.run(AdminServerApplication.class, args); } } ================================================ FILE: labx-15/labx-15-admin-02-adminserver/src/main/resources/application.yaml ================================================ spring: application: name: demo-admin-server # Spring 应用名 cloud: nacos: # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 service: ${spring.application.name} # 注册到 Nacos 的服务名。默认值为 ${spring.application.name}。 ================================================ FILE: labx-15/labx-15-admin-02-demo-application/pom.xml ================================================ labx-15 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-15-admin-02-demo-application 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery ================================================ FILE: labx-15/labx-15-admin-02-demo-application/src/main/java/cn/iocoder/springcloud/labx15/demo/Demo01Application.java ================================================ package cn.iocoder.springcloud.labx15.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Demo01Application { public static void main(String[] args) { System.setProperty("server.port", "18081"); // 端口 18081 SpringApplication.run(Demo01Application.class, args); } } ================================================ FILE: labx-15/labx-15-admin-02-demo-application/src/main/java/cn/iocoder/springcloud/labx15/demo/Demo02Application.java ================================================ package cn.iocoder.springcloud.labx15.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Demo02Application { public static void main(String[] args) { System.setProperty("server.port", "18082"); // 端口 18082 SpringApplication.run(Demo02Application.class, args); } } ================================================ FILE: labx-15/labx-15-admin-02-demo-application/src/main/java/cn/iocoder/springcloud/labx15/demo/Demo03Application.java ================================================ package cn.iocoder.springcloud.labx15.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Demo03Application { public static void main(String[] args) throws InterruptedException { System.setProperty("server.port", "18083"); // 端口 18082 SpringApplication.run(Demo03Application.class, args); Thread.sleep(1000000L); } } ================================================ FILE: labx-15/labx-15-admin-02-demo-application/src/main/resources/application.yaml ================================================ management: endpoints: # Actuator HTTP 配置项,对应 WebEndpointProperties 配置类 web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 spring: application: name: demo-application # 应用名 cloud: nacos: # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 service: ${spring.application.name} # 注册到 Nacos 的服务名。默认值为 ${spring.application.name}。 ================================================ FILE: labx-15/labx-15-admin-03-adminserver/pom.xml ================================================ labx-15 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-15-admin-03-adminserver 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import de.codecentric spring-boot-admin-starter-server 2.2.2 com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery org.springframework.boot spring-boot-starter-security ================================================ FILE: labx-15/labx-15-admin-03-adminserver/src/main/java/cn/iocoder/springcloud/labx15/adminserver/AdminServerApplication.java ================================================ package cn.iocoder.springcloud.labx15.adminserver; import de.codecentric.boot.admin.server.config.EnableAdminServer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @EnableAdminServer public class AdminServerApplication { public static void main(String[] args) { SpringApplication.run(AdminServerApplication.class, args); } } ================================================ FILE: labx-15/labx-15-admin-03-adminserver/src/main/java/cn/iocoder/springcloud/labx15/adminserver/config/SecurityConfig.java ================================================ package cn.iocoder.springcloud.labx15.adminserver.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; import org.springframework.security.config.web.server.ServerHttpSecurity; import org.springframework.security.core.userdetails.MapReactiveUserDetailsService; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.web.server.SecurityWebFilterChain; @Configuration @EnableWebFluxSecurity // 开启 Security 对 WebFlux 的安全功能 public class SecurityConfig { @Bean public MapReactiveUserDetailsService userDetailsService() { // 创建一个用户 UserDetails user = User.withDefaultPasswordEncoder() .username("user") .password("user") .roles("USER") .build(); // 如果胖友有更多用户的诉求,这里可以继续创建 // 创建 MapReactiveUserDetailsService return new MapReactiveUserDetailsService(user); } @Bean public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { http.authorizeExchange(exchanges -> // 设置权限配置 exchanges .pathMatchers("/assets/**").permitAll() // 静态资源,允许匿名访问 .pathMatchers("/login").permitAll() // 登陆接口,允许匿名访问 .anyExchange().authenticated() // ) .formLogin().loginPage("/login") // 登陆页面 .and().logout().logoutUrl("/logout") // 登出界面 .and().httpBasic() // HTTP Basic 认证方式 .and().csrf().disable(); // csrf 禁用 return http.build(); } } ================================================ FILE: labx-15/labx-15-admin-03-adminserver/src/main/resources/application.yaml ================================================ spring: application: name: demo-admin-server # Spring 应用名 cloud: nacos: # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 service: ${spring.application.name} # 注册到 Nacos 的服务名。默认值为 ${spring.application.name}。 metadata: user.name: user # Spring Security 安全认证的账号 user.password: user # Spring Security 安全认证的密码 ================================================ FILE: labx-15/labx-15-admin-03-demo-application/pom.xml ================================================ labx-15 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-15-admin-03-demo-application 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery org.springframework.boot spring-boot-starter-security ================================================ FILE: labx-15/labx-15-admin-03-demo-application/src/main/java/cn/iocoder/springcloud/labx15/demo/Demo01Application.java ================================================ package cn.iocoder.springcloud.labx15.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Demo01Application { public static void main(String[] args) { System.setProperty("server.port", "18081"); // 端口 18081 SpringApplication.run(Demo01Application.class, args); } } ================================================ FILE: labx-15/labx-15-admin-03-demo-application/src/main/java/cn/iocoder/springcloud/labx15/demo/Demo02Application.java ================================================ package cn.iocoder.springcloud.labx15.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Demo02Application { public static void main(String[] args) { System.setProperty("server.port", "18082"); // 端口 18082 SpringApplication.run(Demo02Application.class, args); } } ================================================ FILE: labx-15/labx-15-admin-03-demo-application/src/main/resources/application.yaml ================================================ management: endpoints: # Actuator HTTP 配置项,对应 WebEndpointProperties 配置类 web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 spring: application: name: demo-application # 应用名 # Spring Security 配置项,对应 SecurityProperties 配置类 security: # 配置默认的 InMemoryUserDetailsManager 的用户账号与密码。 user: name: test # 账号 password: test # 密码 cloud: nacos: # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 service: ${spring.application.name} # 注册到 Nacos 的服务名。默认值为 ${spring.application.name}。 metadata: user.name: ${spring.security.user.name} # Spring Security 认证账号 user.password: ${spring.security.user.password} # Spring Security 认证密码 ================================================ FILE: labx-15/labx-15-admin-04-adminserver-custom/pom.xml ================================================ labx-15 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-15-admin-04-adminserver-custom 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import de.codecentric spring-boot-admin-starter-server 2.2.2 com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery ================================================ FILE: labx-15/labx-15-admin-04-adminserver-custom/src/main/java/cn/iocoder/springcloud/labx15/adminserver/AdminServerApplication.java ================================================ package cn.iocoder.springcloud.labx15.adminserver; import de.codecentric.boot.admin.server.config.EnableAdminServer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @EnableAdminServer public class AdminServerApplication { public static void main(String[] args) { SpringApplication.run(AdminServerApplication.class, args); } } ================================================ FILE: labx-15/labx-15-admin-04-adminserver-custom/src/main/java/cn/iocoder/springcloud/labx15/adminserver/notify/LoggerNotifier.java ================================================ package cn.iocoder.springcloud.labx15.adminserver.notify; import de.codecentric.boot.admin.server.domain.entities.Instance; import de.codecentric.boot.admin.server.domain.entities.InstanceRepository; import de.codecentric.boot.admin.server.domain.events.InstanceEvent; import de.codecentric.boot.admin.server.domain.events.InstanceStatusChangedEvent; import de.codecentric.boot.admin.server.notify.AbstractEventNotifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import reactor.core.publisher.Mono; @Component public class LoggerNotifier extends AbstractEventNotifier { private Logger logger = LoggerFactory.getLogger(getClass()); public LoggerNotifier(InstanceRepository repository) { super(repository); } @Override protected Mono doNotify(InstanceEvent event, Instance instance) { return Mono.fromRunnable(() -> { if (event instanceof InstanceStatusChangedEvent) { logger.info("Instance {} ({}) is {}", instance.getRegistration().getName(), event.getInstance(), ((InstanceStatusChangedEvent) event).getStatusInfo().getStatus()); } else { logger.info("Instance {} ({}) {}", instance.getRegistration().getName(), event.getInstance(), event.getType()); } }); } } ================================================ FILE: labx-15/labx-15-admin-04-adminserver-custom/src/main/resources/application.yaml ================================================ spring: application: name: demo-admin-server # Spring 应用名 cloud: nacos: # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 service: ${spring.application.name} # 注册到 Nacos 的服务名。默认值为 ${spring.application.name}。 ================================================ FILE: labx-15/labx-15-admin-04-adminserver-mail/pom.xml ================================================ labx-15 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-15-admin-04-adminserver-mail 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import de.codecentric spring-boot-admin-starter-server 2.2.2 com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery org.springframework.boot spring-boot-starter-mail ================================================ FILE: labx-15/labx-15-admin-04-adminserver-mail/src/main/java/cn/iocoder/springcloud/labx15/adminserver/AdminServerApplication.java ================================================ package cn.iocoder.springcloud.labx15.adminserver; import de.codecentric.boot.admin.server.config.EnableAdminServer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @EnableAdminServer public class AdminServerApplication { public static void main(String[] args) { SpringApplication.run(AdminServerApplication.class, args); } } ================================================ FILE: labx-15/labx-15-admin-04-adminserver-mail/src/main/resources/application.yaml ================================================ spring: application: name: demo-admin-server # Spring 应用名 cloud: nacos: # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 service: ${spring.application.name} # 注册到 Nacos 的服务名。默认值为 ${spring.application.name}。 mail: # 配置发送告警的邮箱 host: smtp.126.com username: wwbmlhh@126.com password: '******' default-encoding: UTF-8 boot: admin: notify: mail: from: ${spring.mail.username} # 告警发件人 to: 7685413@qq.com # 告警收件人 ================================================ FILE: labx-15/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-15 pom labx-15-admin-02-adminserver labx-15-admin-02-demo-application labx-15-admin-03-adminserver labx-15-admin-03-demo-application labx-15-admin-04-adminserver-mail labx-15-admin-04-adminserver-custom ================================================ FILE: labx-15/《芋道 Spring Cloud 监控工具 Admin 入门》.md ================================================ ================================================ FILE: labx-16/deploy.sh ================================================ #!/bin/bash set -e # 基础 # export JAVA_HOME=/work/programs/jdk/jdk1.8.0_181 # export PATH=PATH=$PATH:$JAVA_HOME/bin # export CLASSPATH=$JAVA_HOME/jre/lib/rt.jar:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar DATE=$(date +%Y%m%d%H%M) # 基础路径 BASE_PATH=/work/projects/labx-16-demo-01 # 编译后 jar 的地址。部署时,Jenkins 会上传 jar 包到该目录下 SOURCE_PATH=$BASE_PATH/build # 服务名称。同时约定部署服务的 jar 包名字也为它。 SERVER_NAME=labx-16-demo-01 # 环境 PROFILES_ACTIVE=prod # 健康检查 URL HEALTH_CHECK_URL=http://127.0.0.1:8078/actuator/health/ # 修改状态 URL STATUS_URL=http://127.0.0.1:8078/actuator/service-registry/ # heapError 存放路径 HEAP_ERROR_PATH=$BASE_PATH/heapError # JVM 参数 JAVA_OPS="-Xms1024m -Xmx1024m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=$HEAP_ERROR_PATH" # JavaAgent 参数。可用于配置 SkyWalking 等链路追踪 JAVA_AGENT= # 备份 function backup() { # 如果不存在,则无需备份 if [ ! -f "$BASE_PATH/$SERVER_NAME.jar" ]; then echo "[backup] $BASE_PATH/$SERVER_NAME.jar 不存在,跳过备份" # 如果存在,则备份到 backup 目录下,使用时间作为后缀 else echo "[backup] 开始备份 $SERVER_NAME ..." cp $BASE_PATH/$SERVER_NAME.jar $BASE_PATH/backup/$SERVER_NAME-$DATE.jar echo "[backup] 备份 $SERVER_NAME 完成" fi } # 最新构建代码 移动到项目环境 function transfer() { echo "[transfer] 开始转移 $SERVER_NAME.jar" # 删除原 jar 包 if [ ! -f "$BASE_PATH/$SERVER_NAME.jar" ]; then echo "[transfer] $BASE_PATH/$SERVER_NAME.jar 不存在,跳过删除" else echo "[transfer] 移除 $BASE_PATH/$SERVER_NAME.jar 完成" rm $BASE_PATH/$SERVER_NAME.jar fi # 复制新 jar 包 echo "[transfer] 从 $SOURCE_PATH 中获取 $SERVER_NAME.jar 并迁移至 $BASE_PATH ...." cp $SOURCE_PATH/$SERVER_NAME.jar $BASE_PATH echo "[transfer] 转移 $SERVER_NAME.jar 完成" } # 停止 function stop() { echo "[stop] 开始停止 $BASE_PATH/$SERVER_NAME" PID=$(ps -ef | grep $BASE_PATH/$SERVER_NAME | grep -v "grep" | awk '{print $2}') # 如果 Java 服务启动中,则进行关闭 if [ -n "$PID" ]; then # 从注册中心下线 echo "[stop] 从注册中心下线当前实例,并 sleep 20 秒" curl -X POST $STATUS_URL -d '{"status": "DOWN"}' -H 'content-type: application/json' sleep 20 # 正常关闭 echo "[stop] $BASE_PATH/$SERVER_NAME 运行中,开始 kill [$PID]" kill -15 $PID # 等待最大 60 秒,直到关闭完成。 for ((i = 0; i < 60; i++)) do sleep 1 PID=$(ps -ef | grep $BASE_PATH/$SERVER_NAME | grep -v "grep" | awk '{print $2}') if [ -n "$PID" ]; then echo -e ".\c" else echo '[stop] 停止 $BASE_PATH/$SERVER_NAME 成功' break fi done # 如果正常关闭失败,那么进行强制 kill -9 进行关闭 if [ -n "$PID" ]; then echo "[stop] $BASE_PATH/$SERVER_NAME 失败,强制 kill -9 $PID" kill -9 $PID fi # 如果 Java 服务未启动,则无需关闭 else echo "[stop] $BASE_PATH/$SERVER_NAME 未启动,无需停止" fi } # 启动 function start() { # 开启启动前,打印启动参数 echo "[start] 开始启动 $BASE_PATH/$SERVER_NAME" echo "[start] JAVA_OPS: $JAVA_OPS" echo "[start] JAVA_AGENT: $JAVA_AGENT" echo "[start] PROFILES: $PROFILES_ACTIVE" # 开始启动 BUILD_ID=dontKillMe nohup java -server $JAVA_OPS $JAVA_AGENT -jar $BASE_PATH/$SERVER_NAME.jar --spring.profiles.active=$PROFILES_ACTIVE & echo "[start] 启动 $BASE_PATH/$SERVER_NAME 完成" } # 健康检查 function healthCheck() { # 如果配置健康检查,则进行健康检查 if [ -n "$HEALTH_CHECK_URL" ]; then # 健康检查最大 60 秒,直到健康检查通过 echo "[healthCheck] 开始通过 $HEALTH_CHECK_URL 地址,进行健康检查"; for ((i = 0; i < 60; i++)) do # 请求健康检查地址,只获取状态码。 result=`curl -I -m 10 -o /dev/null -s -w %{http_code} $HEALTH_CHECK_URL || echo "000"` # 如果状态码为 200,则说明健康检查通过 if [ "$result" == "200" ]; then echo "[healthCheck] 健康检查通过"; break # 如果状态码非 200,则说明未通过。sleep 1 秒后,继续重试 else echo -e ".\c" sleep 1 fi done # 健康检查未通过,则异常退出 shell 脚本,不继续部署。 if [ ! "$result" == "200" ]; then echo "[healthCheck] 健康检查不通过,可能部署失败。查看日志,自行判断是否启动成功"; tail -n 10 nohup.out exit 1; # 健康检查通过,打印最后 10 行日志,可能部署的人想看下日志。 else tail -n 10 nohup.out fi # 如果未配置健康检查,则 slepp 60 秒,人工看日志是否部署成功。 else echo "[healthCheck] HEALTH_CHECK_URL 未配置,开始 sleep 60 秒"; sleep 60 echo "[healthCheck] sleep 60 秒完成,查看日志,自行判断是否启动成功"; tail -n 50 nohup.out fi } # 部署 function deploy() { cd $BASE_PATH # 备份原 jar backup # 停止 Java 服务 stop # 部署新 jar transfer # 启动 Java 服务 start # 健康检查 healthCheck } deploy ================================================ FILE: labx-16/labx-16-demo-01/pom.xml ================================================ labx-16 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-16-demo-01 jar 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery ${project.artifactId} org.springframework.boot spring-boot-maven-plugin ${spring.boot.version} repackage ================================================ FILE: labx-16/labx-16-demo-01/src/main/java/cn/iocoder/springcloud/lab16/jenkinsdemo/Application.java ================================================ package cn.iocoder.springcloud.lab16.jenkinsdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextClosedEvent; import org.springframework.stereotype.Component; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } @Component public class Listener implements ApplicationListener { @Override public void onApplicationEvent(ApplicationEvent event) { if (event instanceof ContextClosedEvent) { this.sleep(10); } } private void sleep(int seconds) { try { Thread.sleep(seconds * 1000L); } catch (InterruptedException ignore) { } } } } ================================================ FILE: labx-16/labx-16-demo-01/src/main/java/cn/iocoder/springcloud/lab16/jenkinsdemo/controller/DemoController.java ================================================ package cn.iocoder.springcloud.lab16.jenkinsdemo.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @GetMapping("/echo") public String echo() { return "echo"; } } ================================================ FILE: labx-16/labx-16-demo-01/src/main/resources/application-dev.yaml ================================================ server: port: 8079 management: server: port: 8078 # 自定义端口,避免 Nginx 暴露出去 endpoints: web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 spring: application: name: demo-service cloud: nacos: # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 ================================================ FILE: labx-16/labx-16-demo-01/src/main/resources/application-local.yaml ================================================ server: port: 8079 management: server: port: 8078 # 自定义端口,避免 Nginx 暴露出去 endpoints: web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 spring: application: name: demo-service cloud: nacos: # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 ================================================ FILE: labx-16/labx-16-demo-01/src/main/resources/application-pre.yaml ================================================ server: port: 8079 management: server: port: 8078 # 自定义端口,避免 Nginx 暴露出去 endpoints: web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 spring: application: name: demo-service cloud: nacos: # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 ================================================ FILE: labx-16/labx-16-demo-01/src/main/resources/application-prod.yaml ================================================ server: port: 8079 management: server: port: 8078 # 自定义端口,避免 Nginx 暴露出去 endpoints: web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 spring: application: name: demo-service cloud: nacos: # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 ================================================ FILE: labx-16/labx-16-demo-01/src/main/resources/application-uat.yaml ================================================ server: port: 8079 management: server: port: 8078 # 自定义端口,避免 Nginx 暴露出去 endpoints: web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 spring: application: name: demo-service cloud: nacos: # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 ================================================ FILE: labx-16/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-16 pom labx-16-demo-01 ================================================ FILE: labx-16/《芋道 Spring Cloud 持续交付 Jenkins 入门》.md ================================================ ================================================ FILE: labx-17/labx-17-sc-seata-at-feign-demo/data.sql ================================================ # Order DROP DATABASE IF EXISTS seata_order; CREATE DATABASE seata_order; CREATE TABLE seata_order.orders ( id INT(11) NOT NULL AUTO_INCREMENT, user_id INT(11) DEFAULT NULL, product_id INT(11) DEFAULT NULL, pay_amount DECIMAL(10, 0) DEFAULT NULL, add_time DATETIME DEFAULT CURRENT_TIMESTAMP, last_update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8; CREATE TABLE seata_order.undo_log ( id BIGINT(20) NOT NULL AUTO_INCREMENT, branch_id BIGINT(20) NOT NULL, xid VARCHAR(100) NOT NULL, context VARCHAR(128) NOT NULL, rollback_info LONGBLOB NOT NULL, log_status INT(11) NOT NULL, log_created DATETIME NOT NULL, log_modified DATETIME NOT NULL, PRIMARY KEY (id), UNIQUE KEY ux_undo_log (xid, branch_id) ) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8; # Product DROP DATABASE IF EXISTS seata_product; CREATE DATABASE seata_product; CREATE TABLE seata_product.product ( id INT(11) NOT NULL AUTO_INCREMENT, stock INT(11) DEFAULT NULL, last_update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8; INSERT INTO seata_product.product (id, stock) VALUES (1, 10); # 插入一条产品的库存 CREATE TABLE seata_product.undo_log ( id BIGINT(20) NOT NULL AUTO_INCREMENT, branch_id BIGINT(20) NOT NULL, xid VARCHAR(100) NOT NULL, context VARCHAR(128) NOT NULL, rollback_info LONGBLOB NOT NULL, log_status INT(11) NOT NULL, log_created DATETIME NOT NULL, log_modified DATETIME NOT NULL, PRIMARY KEY (id), UNIQUE KEY ux_undo_log (xid, branch_id) ) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8; # Account DROP DATABASE IF EXISTS seata_account; CREATE DATABASE seata_account; CREATE TABLE seata_account.account ( id INT(11) NOT NULL AUTO_INCREMENT, balance DOUBLE DEFAULT NULL, last_update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8; CREATE TABLE seata_account.undo_log ( id BIGINT(20) NOT NULL AUTO_INCREMENT, branch_id BIGINT(20) NOT NULL, xid VARCHAR(100) NOT NULL, context VARCHAR(128) NOT NULL, rollback_info LONGBLOB NOT NULL, log_status INT(11) NOT NULL, log_created DATETIME NOT NULL, log_modified DATETIME NOT NULL, PRIMARY KEY (id), UNIQUE KEY ux_undo_log (xid, branch_id) ) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8; INSERT INTO seata_account.account (id, balance) VALUES (1, 10); ================================================ FILE: labx-17/labx-17-sc-seata-at-feign-demo/labx-17-sc-seata-at-feign-demo-account-service/pom.xml ================================================ labx-17-sc-seata-at-feign-demo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-17-sc-seata-at-feign-demo-account-service 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 5.1.48 com.alibaba druid-spring-boot-starter 1.1.10 org.mybatis.spring.boot mybatis-spring-boot-starter 2.1.2 com.alibaba.cloud spring-cloud-alibaba-seata io.seata seata-spring-boot-starter 1.1.0 com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx17.accountservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class AccountServiceApplication { public static void main(String[] args) { SpringApplication.run(AccountServiceApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx17.accountservice.controller; import cn.iocoder.springcloud.labx17.accountservice.dto.AccountReduceBalanceDTO; import cn.iocoder.springcloud.labx17.accountservice.service.AccountService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/account") public class AccountController { private Logger logger = LoggerFactory.getLogger(AccountController.class); @Autowired private AccountService accountService; @PostMapping("/reduce-balance") public void reduceBalance(@RequestBody AccountReduceBalanceDTO accountReduceBalanceDTO) throws Exception { logger.info("[reduceBalance] 收到减少余额请求, 用户:{}, 金额:{}", accountReduceBalanceDTO.getUserId(), accountReduceBalanceDTO.getPrice()); accountService.reduceBalance(accountReduceBalanceDTO.getUserId(), accountReduceBalanceDTO.getPrice()); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx17.accountservice.dao; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; import org.springframework.stereotype.Repository; @Mapper @Repository public interface AccountDao { /** * 获取账户余额 * * @param userId 用户 ID * @return 账户余额 */ @Select("SELECT balance FROM account WHERE id = #{userId}") Integer getBalance(@Param("userId") Long userId); /** * 扣减余额 * * @param price 需要扣减的数目 * @return 影响记录行数 */ @Update("UPDATE account SET balance = balance - #{price} WHERE id = 1 AND balance >= ${price}") int reduceBalance(@Param("price") Integer price); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx17.accountservice.dto; /** * 账户减少余额 DTO */ public class AccountReduceBalanceDTO { /** * 用户编号 */ private Long userId; /** * 扣减金额 */ private Integer price; public Long getUserId() { return userId; } public AccountReduceBalanceDTO setUserId(Long userId) { this.userId = userId; return this; } public Integer getPrice() { return price; } public AccountReduceBalanceDTO setPrice(Integer price) { this.price = price; return this; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx17.accountservice.service; /** * 账户 Service */ public interface AccountService { /** * 扣除余额 * * @param userId 用户编号 * @param price 扣减金额 * @throws Exception 失败时抛出异常 */ void reduceBalance(Long userId, Integer price) throws Exception; } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx17.accountservice.service.impl; import cn.iocoder.springcloud.labx17.accountservice.dao.AccountDao; import cn.iocoder.springcloud.labx17.accountservice.service.AccountService; import io.seata.core.context.RootContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class AccountServiceImpl implements AccountService { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private AccountDao accountDao; @Override @Transactional // 开启新事物 public void reduceBalance(Long userId, Integer price) throws Exception { logger.info("[reduceBalance] 当前 XID: {}", RootContext.getXID()); // 检查余额 checkBalance(userId, price); logger.info("[reduceBalance] 开始扣减用户 {} 余额", userId); // 扣除余额 int updateCount = accountDao.reduceBalance(price); // 扣除成功 if (updateCount == 0) { logger.warn("[reduceBalance] 扣除用户 {} 余额失败", userId); throw new Exception("余额不足"); } logger.info("[reduceBalance] 扣除用户 {} 余额成功", userId); } private void checkBalance(Long userId, Integer price) throws Exception { logger.info("[checkBalance] 检查用户 {} 余额", userId); Integer balance = accountDao.getBalance(userId); if (balance < price) { logger.warn("[checkBalance] 用户 {} 余额不足,当前余额:{}", userId, balance); throw new Exception("余额不足"); } } } ================================================ FILE: labx-17/labx-17-sc-seata-at-feign-demo/labx-17-sc-seata-at-feign-demo-account-service/src/main/resources/application-file.yaml ================================================ server: port: 8083 spring: application: name: account-service datasource: url: jdbc:mysql://127.0.0.1:3306/seata_account?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: cloud: # Nacos 作为注册中心的配置项 nacos: discovery: server-addr: 127.0.0.1:8848 # Seata 配置项,对应 SeataProperties 类 seata: application-id: ${spring.application.name} # Seata 应用编号,默认为 ${spring.application.name} tx-service-group: ${spring.application.name}-group # Seata 事务组编号,用于 TC 集群名 # 服务配置项,对应 ServiceProperties 类 service: # 虚拟组和分组的映射 vgroup-mapping: account-service-group: default # 分组和 Seata 服务的映射 grouplist: default: 127.0.0.1:8091 ================================================ FILE: labx-17/labx-17-sc-seata-at-feign-demo/labx-17-sc-seata-at-feign-demo-account-service/src/main/resources/application.yaml ================================================ server: port: 8083 spring: application: name: account-service datasource: url: jdbc:mysql://127.0.0.1:3306/seata_account?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: cloud: # Nacos 作为注册中心的配置项 nacos: discovery: server-addr: 127.0.0.1:8848 # Seata 配置项,对应 SeataProperties 类 seata: application-id: ${spring.application.name} # Seata 应用编号,默认为 ${spring.application.name} tx-service-group: ${spring.application.name}-group # Seata 事务组编号,用于 TC 集群名 # Seata 服务配置项,对应 ServiceProperties 类 service: # 虚拟组和分组的映射 vgroup-mapping: account-service-group: default # Seata 注册中心配置项,对应 RegistryProperties 类 registry: type: nacos # 注册中心类型,默认为 file nacos: cluster: default # 使用的 Seata 分组 namespace: # Nacos 命名空间 serverAddr: localhost # Nacos 服务地址 ================================================ FILE: labx-17/labx-17-sc-seata-at-feign-demo/labx-17-sc-seata-at-feign-demo-order-service/pom.xml ================================================ labx-17-sc-seata-at-feign-demo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-17-sc-seata-at-feign-demo-order-service 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 5.1.48 com.alibaba druid-spring-boot-starter 1.1.10 org.mybatis.spring.boot mybatis-spring-boot-starter 2.1.2 com.alibaba.cloud spring-cloud-alibaba-seata io.seata seata-spring-boot-starter 1.1.0 com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery org.springframework.cloud spring-cloud-starter-openfeign ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx17.orderservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication @EnableFeignClients public class OrderServiceApplication { public static void main(String[] args) { SpringApplication.run(OrderServiceApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx17.orderservice.controller; import cn.iocoder.springcloud.labx17.orderservice.service.OrderService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/order") public class OrderController { private Logger logger = LoggerFactory.getLogger(OrderController.class); @Autowired private OrderService orderService; @PostMapping("/create") public Integer createOrder(@RequestParam("userId") Long userId, @RequestParam("productId") Long productId, @RequestParam("price") Integer price) throws Exception { logger.info("[createOrder] 收到下单请求,用户:{}, 商品:{}, 价格:{}", userId, productId, price); return orderService.createOrder(userId, productId, price); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx17.orderservice.dao; import cn.iocoder.springcloud.labx17.orderservice.entity.OrderDO; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Options; import org.springframework.stereotype.Repository; @Mapper @Repository public interface OrderDao { /** * 插入订单记录 * * @param order 订单 * @return 影响记录数量 */ @Insert("INSERT INTO orders (user_id, product_id, pay_amount) VALUES (#{userId}, #{productId}, #{payAmount})") @Options(useGeneratedKeys = true, keyColumn = "id", keyProperty = "id") int saveOrder(OrderDO order); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx17.orderservice.entity; /** * 订单实体 */ public class OrderDO { /** 订单编号 **/ private Integer id; /** 用户编号 **/ private Long userId; /** 产品编号 **/ private Long productId; /** 支付金额 **/ private Integer payAmount; public Integer getId() { return id; } public OrderDO setId(Integer id) { this.id = id; return this; } public Long getUserId() { return userId; } public OrderDO setUserId(Long userId) { this.userId = userId; return this; } public Long getProductId() { return productId; } public OrderDO setProductId(Long productId) { this.productId = productId; return this; } public Integer getPayAmount() { return payAmount; } public OrderDO setPayAmount(Integer payAmount) { this.payAmount = payAmount; return this; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx17.orderservice.feign; import cn.iocoder.springcloud.labx17.orderservice.feign.dto.AccountReduceBalanceDTO; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; /** * `account-service` 服务的 Feign 客户端 */ @FeignClient(name = "account-service") public interface AccountServiceFeignClient { @PostMapping("/account/reduce-balance") void reduceBalance(@RequestBody AccountReduceBalanceDTO accountReduceBalanceDTO); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx17.orderservice.feign; import cn.iocoder.springcloud.labx17.orderservice.feign.dto.ProductReduceStockDTO; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; /** * `product-service` 服务的 Feign 客户端 */ @FeignClient(name = "product-service") public interface ProductServiceFeignClient { @PostMapping("/product/reduce-stock") void reduceStock(@RequestBody ProductReduceStockDTO productReduceStockDTO); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx17.orderservice.feign.dto; /** * 账户减少余额 DTO */ public class AccountReduceBalanceDTO { /** * 用户编号 */ private Long userId; /** * 扣减金额 */ private Integer price; public Long getUserId() { return userId; } public AccountReduceBalanceDTO setUserId(Long userId) { this.userId = userId; return this; } public Integer getPrice() { return price; } public AccountReduceBalanceDTO setPrice(Integer price) { this.price = price; return this; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx17.orderservice.feign.dto; /** * 商品减少库存 DTO */ public class ProductReduceStockDTO { /** * 商品编号 */ private Long productId; /** * 数量 */ private Integer amount; public Long getProductId() { return productId; } public ProductReduceStockDTO setProductId(Long productId) { this.productId = productId; return this; } public Integer getAmount() { return amount; } public ProductReduceStockDTO setAmount(Integer amount) { this.amount = amount; return this; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx17.orderservice.service; /** * 订单 Service */ public interface OrderService { /** * 创建订单 * * @param userId 用户编号 * @param productId 产品编号 * @param price 价格 * @return 订单编号 * @throws Exception 创建订单失败,抛出异常 */ Integer createOrder(Long userId, Long productId, Integer price) throws Exception; } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx17.orderservice.service.impl; import cn.iocoder.springcloud.labx17.orderservice.dao.OrderDao; import cn.iocoder.springcloud.labx17.orderservice.entity.OrderDO; import cn.iocoder.springcloud.labx17.orderservice.feign.*; import cn.iocoder.springcloud.labx17.orderservice.feign.dto.*; import cn.iocoder.springcloud.labx17.orderservice.service.OrderService; import io.seata.core.context.RootContext; import io.seata.spring.annotation.GlobalTransactional; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class OrderServiceImpl implements OrderService { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private OrderDao orderDao; @Autowired private AccountServiceFeignClient accountService; @Autowired private ProductServiceFeignClient productService; @Override @GlobalTransactional public Integer createOrder(Long userId, Long productId, Integer price) { Integer amount = 1; // 购买数量,暂时设置为 1。 logger.info("[createOrder] 当前 XID: {}", RootContext.getXID()); // 扣减库存 productService.reduceStock(new ProductReduceStockDTO().setProductId(productId).setAmount(amount)); // 扣减余额 accountService.reduceBalance(new AccountReduceBalanceDTO().setUserId(userId).setPrice(price)); // 保存订单 OrderDO order = new OrderDO().setUserId(userId).setProductId(productId).setPayAmount(amount * price); orderDao.saveOrder(order); logger.info("[createOrder] 保存订单: {}", order.getId()); // 返回订单编号 return order.getId(); } } ================================================ FILE: labx-17/labx-17-sc-seata-at-feign-demo/labx-17-sc-seata-at-feign-demo-order-service/src/main/resources/application-file.yaml ================================================ server: port: 8081 # 端口 spring: application: name: order-service datasource: url: jdbc:mysql://127.0.0.1:3306/seata_order?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: cloud: # Nacos 作为注册中心的配置项 nacos: discovery: server-addr: 127.0.0.1:8848 # Seata 配置项,对应 SeataProperties 类 seata: application-id: ${spring.application.name} # Seata 应用编号,默认为 ${spring.application.name} tx-service-group: ${spring.application.name}-group # Seata 事务组编号,用于 TC 集群名 # 服务配置项,对应 ServiceProperties 类 service: # 虚拟组和分组的映射 vgroup-mapping: order-service-group: default # 分组和 Seata 服务的映射 grouplist: default: 127.0.0.1:8091 ================================================ FILE: labx-17/labx-17-sc-seata-at-feign-demo/labx-17-sc-seata-at-feign-demo-order-service/src/main/resources/application.yaml ================================================ server: port: 8081 # 端口 spring: application: name: order-service datasource: url: jdbc:mysql://127.0.0.1:3306/seata_order?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: cloud: # Nacos 作为注册中心的配置项 nacos: discovery: server-addr: 127.0.0.1:8848 # Seata 配置项,对应 SeataProperties 类 seata: application-id: ${spring.application.name} # Seata 应用编号,默认为 ${spring.application.name} tx-service-group: ${spring.application.name}-group # Seata 事务组编号,用于 TC 集群名 # Seata 服务配置项,对应 ServiceProperties 类 service: # 虚拟组和分组的映射 vgroup-mapping: order-service-group: default # Seata 注册中心配置项,对应 RegistryProperties 类 registry: type: nacos # 注册中心类型,默认为 file nacos: cluster: default # 使用的 Seata 分组 namespace: # Nacos 命名空间 serverAddr: localhost # Nacos 服务地址 ================================================ FILE: labx-17/labx-17-sc-seata-at-feign-demo/labx-17-sc-seata-at-feign-demo-product-service/pom.xml ================================================ labx-17-sc-seata-at-feign-demo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-17-sc-seata-at-feign-demo-product-service 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 5.1.48 com.alibaba druid-spring-boot-starter 1.1.10 org.mybatis.spring.boot mybatis-spring-boot-starter 2.1.2 com.alibaba.cloud spring-cloud-alibaba-seata io.seata seata-spring-boot-starter 1.1.0 com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx17.productservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ProductServiceApplication { public static void main(String[] args) { SpringApplication.run(ProductServiceApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx17.productservice.controller; import cn.iocoder.springcloud.labx17.productservice.dto.ProductReduceStockDTO; import cn.iocoder.springcloud.labx17.productservice.service.ProductService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/product") public class ProductController { private Logger logger = LoggerFactory.getLogger(ProductController.class); @Autowired private ProductService productService; @PostMapping("/reduce-stock") public void reduceStock(@RequestBody ProductReduceStockDTO productReduceStockDTO) throws Exception { logger.info("[reduceStock] 收到减少库存请求, 商品:{}, 价格:{}", productReduceStockDTO.getProductId(), productReduceStockDTO.getAmount()); productService.reduceStock(productReduceStockDTO.getProductId(), productReduceStockDTO.getAmount()); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx17.productservice.dao; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; import org.springframework.stereotype.Repository; @Mapper @Repository public interface ProductDao { /** * 获取库存 * * @param productId 商品编号 * @return 库存 */ @Select("SELECT stock FROM product WHERE id = #{productId}") Integer getStock(@Param("productId") Long productId); /** * 扣减库存 * * @param productId 商品编号 * @param amount 扣减数量 * @return 影响记录行数 */ @Update("UPDATE product SET stock = stock - #{amount} WHERE id = #{productId} AND stock >= #{amount}") int reduceStock(@Param("productId") Long productId, @Param("amount") Integer amount); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx17.productservice.dto; /** * 商品减少库存 DTO */ public class ProductReduceStockDTO { /** * 商品编号 */ private Long productId; /** * 数量 */ private Integer amount; public Long getProductId() { return productId; } public ProductReduceStockDTO setProductId(Long productId) { this.productId = productId; return this; } public Integer getAmount() { return amount; } public ProductReduceStockDTO setAmount(Integer amount) { this.amount = amount; return this; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx17.productservice.service; /** * 商品 Service */ public interface ProductService { /** * 扣减库存 * * @param productId 商品 ID * @param amount 扣减数量 * @throws Exception 扣减失败时抛出异常 */ void reduceStock(Long productId, Integer amount) throws Exception; } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx17.productservice.service.impl; import cn.iocoder.springcloud.labx17.productservice.dao.ProductDao; import cn.iocoder.springcloud.labx17.productservice.service.ProductService; import io.seata.core.context.RootContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class ProductServiceImpl implements ProductService { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private ProductDao productDao; @Override @Transactional // 开启新事物 public void reduceStock(Long productId, Integer amount) throws Exception { logger.info("[reduceStock] 当前 XID: {}", RootContext.getXID()); // 检查库存 checkStock(productId, amount); logger.info("[reduceStock] 开始扣减 {} 库存", productId); // 扣减库存 int updateCount = productDao.reduceStock(productId, amount); // 扣除成功 if (updateCount == 0) { logger.warn("[reduceStock] 扣除 {} 库存失败", productId); throw new Exception("库存不足"); } // 扣除失败 logger.info("[reduceStock] 扣除 {} 库存成功", productId); } private void checkStock(Long productId, Integer requiredAmount) throws Exception { logger.info("[checkStock] 检查 {} 库存", productId); Integer stock = productDao.getStock(productId); if (stock < requiredAmount) { logger.warn("[checkStock] {} 库存不足,当前库存: {}", productId, stock); throw new Exception("库存不足"); } } } ================================================ FILE: labx-17/labx-17-sc-seata-at-feign-demo/labx-17-sc-seata-at-feign-demo-product-service/src/main/resources/application-file.yaml ================================================ server: port: 8082 spring: application: name: product-service datasource: url: jdbc:mysql://127.0.0.1:3306/seata_product?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: cloud: # Nacos 作为注册中心的配置项 nacos: discovery: server-addr: 127.0.0.1:8848 # Seata 配置项,对应 SeataProperties 类 seata: application-id: ${spring.application.name} # Seata 应用编号,默认为 ${spring.application.name} tx-service-group: ${spring.application.name}-group # Seata 事务组编号,用于 TC 集群名 # 服务配置项,对应 ServiceProperties 类 service: # 虚拟组和分组的映射 vgroup-mapping: product-service-group: default # 分组和 Seata 服务的映射 grouplist: default: 127.0.0.1:8091 ================================================ FILE: labx-17/labx-17-sc-seata-at-feign-demo/labx-17-sc-seata-at-feign-demo-product-service/src/main/resources/application.yaml ================================================ server: port: 8082 spring: application: name: product-service datasource: url: jdbc:mysql://127.0.0.1:3306/seata_product?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: cloud: # Nacos 作为注册中心的配置项 nacos: discovery: server-addr: 127.0.0.1:8848 # Seata 配置项,对应 SeataProperties 类 seata: application-id: ${spring.application.name} # Seata 应用编号,默认为 ${spring.application.name} tx-service-group: ${spring.application.name}-group # Seata 事务组编号,用于 TC 集群名 # Seata 服务配置项,对应 ServiceProperties 类 service: # 虚拟组和分组的映射 vgroup-mapping: product-service-group: default # Seata 注册中心配置项,对应 RegistryProperties 类 registry: type: nacos # 注册中心类型,默认为 file nacos: cluster: default # 使用的 Seata 分组 namespace: # Nacos 命名空间 serverAddr: localhost # Nacos 服务地址 ================================================ FILE: labx-17/labx-17-sc-seata-at-feign-demo/pom.xml ================================================ labx-17 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-17-sc-seata-at-feign-demo labx-17-sc-seata-at-feign-demo-account-service labx-17-sc-seata-at-feign-demo-product-service labx-17-sc-seata-at-feign-demo-order-service ================================================ FILE: labx-17/labx-17-sca-seata-at-dubbo-demo/data.sql ================================================ # Order DROP DATABASE IF EXISTS seata_order; CREATE DATABASE seata_order; CREATE TABLE seata_order.orders ( id INT(11) NOT NULL AUTO_INCREMENT, user_id INT(11) DEFAULT NULL, product_id INT(11) DEFAULT NULL, pay_amount DECIMAL(10, 0) DEFAULT NULL, add_time DATETIME DEFAULT CURRENT_TIMESTAMP, last_update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8; CREATE TABLE seata_order.undo_log ( id BIGINT(20) NOT NULL AUTO_INCREMENT, branch_id BIGINT(20) NOT NULL, xid VARCHAR(100) NOT NULL, context VARCHAR(128) NOT NULL, rollback_info LONGBLOB NOT NULL, log_status INT(11) NOT NULL, log_created DATETIME NOT NULL, log_modified DATETIME NOT NULL, PRIMARY KEY (id), UNIQUE KEY ux_undo_log (xid, branch_id) ) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8; # Product DROP DATABASE IF EXISTS seata_product; CREATE DATABASE seata_product; CREATE TABLE seata_product.product ( id INT(11) NOT NULL AUTO_INCREMENT, stock INT(11) DEFAULT NULL, last_update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8; INSERT INTO seata_product.product (id, stock) VALUES (1, 10); # 插入一条产品的库存 CREATE TABLE seata_product.undo_log ( id BIGINT(20) NOT NULL AUTO_INCREMENT, branch_id BIGINT(20) NOT NULL, xid VARCHAR(100) NOT NULL, context VARCHAR(128) NOT NULL, rollback_info LONGBLOB NOT NULL, log_status INT(11) NOT NULL, log_created DATETIME NOT NULL, log_modified DATETIME NOT NULL, PRIMARY KEY (id), UNIQUE KEY ux_undo_log (xid, branch_id) ) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8; # Account DROP DATABASE IF EXISTS seata_account; CREATE DATABASE seata_account; CREATE TABLE seata_account.account ( id INT(11) NOT NULL AUTO_INCREMENT, balance DOUBLE DEFAULT NULL, last_update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8; CREATE TABLE seata_account.undo_log ( id BIGINT(20) NOT NULL AUTO_INCREMENT, branch_id BIGINT(20) NOT NULL, xid VARCHAR(100) NOT NULL, context VARCHAR(128) NOT NULL, rollback_info LONGBLOB NOT NULL, log_status INT(11) NOT NULL, log_created DATETIME NOT NULL, log_modified DATETIME NOT NULL, PRIMARY KEY (id), UNIQUE KEY ux_undo_log (xid, branch_id) ) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8; INSERT INTO seata_account.account (id, balance) VALUES (1, 10); ================================================ FILE: labx-17/labx-17-sca-seata-at-dubbo-demo/labx-17-sca-seata-at-dubbo-demo-account-service/pom.xml ================================================ labx-17-sca-seata-at-dubbo-demo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-17-sca-seata-at-dubbo-demo-account-service 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import cn.iocoder.springboot.labs labx-17-sca-seata-at-dubbo-demo-account-service-api 1.0-SNAPSHOT org.springframework.boot spring-boot-starter org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 5.1.48 com.alibaba druid-spring-boot-starter 1.1.10 org.mybatis.spring.boot mybatis-spring-boot-starter 2.1.2 com.alibaba.cloud spring-cloud-alibaba-seata io.seata seata-spring-boot-starter 1.1.0 com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery com.alibaba.cloud spring-cloud-starter-dubbo ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx17.accountservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class AccountServiceApplication { public static void main(String[] args) { SpringApplication.run(AccountServiceApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx17.accountservice.dao; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; import org.springframework.stereotype.Repository; @Mapper @Repository public interface AccountDao { /** * 获取账户余额 * * @param userId 用户 ID * @return 账户余额 */ @Select("SELECT balance FROM account WHERE id = #{userId}") Integer getBalance(@Param("userId") Long userId); /** * 扣减余额 * * @param price 需要扣减的数目 * @return 影响记录行数 */ @Update("UPDATE account SET balance = balance - #{price} WHERE id = 1 AND balance >= ${price}") int reduceBalance(@Param("price") Integer price); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx17.accountservice.service; import cn.iocoder.springcloudalibaba.labx17.accountservice.api.AccountService; import cn.iocoder.springcloudalibaba.labx17.accountservice.dao.AccountDao; import io.seata.core.context.RootContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; @org.apache.dubbo.config.annotation.Service public class AccountServiceImpl implements AccountService { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private AccountDao accountDao; @Override @Transactional // 开启新事物 public void reduceBalance(Long userId, Integer price) throws Exception { logger.info("[reduceBalance] 当前 XID: {}", RootContext.getXID()); // 检查余额 checkBalance(userId, price); logger.info("[reduceBalance] 开始扣减用户 {} 余额", userId); // 扣除余额 int updateCount = accountDao.reduceBalance(price); // 扣除成功 if (updateCount == 0) { logger.warn("[reduceBalance] 扣除用户 {} 余额失败", userId); throw new Exception("余额不足"); } logger.info("[reduceBalance] 扣除用户 {} 余额成功", userId); } private void checkBalance(Long userId, Integer price) throws Exception { logger.info("[checkBalance] 检查用户 {} 余额", userId); Integer balance = accountDao.getBalance(userId); if (balance < price) { logger.warn("[checkBalance] 用户 {} 余额不足,当前余额:{}", userId, balance); throw new Exception("余额不足"); } } } ================================================ FILE: labx-17/labx-17-sca-seata-at-dubbo-demo/labx-17-sca-seata-at-dubbo-demo-account-service/src/main/resources/application-file.yaml ================================================ spring: application: name: account-service datasource: url: jdbc:mysql://127.0.0.1:3306/seata_account?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: cloud: # Nacos 作为注册中心的配置项 nacos: discovery: server-addr: 127.0.0.1:8848 # dubbo 配置项,对应 DubboConfigurationProperties 配置类 dubbo: # Dubbo 服务注册中心配置,对应 RegistryConfig 类 registry: address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址 # Dubbo 服务提供者协议配置 protocol: port: -1 # 协议端口。使用 -1 表示随机端口。 name: dubbo # 使用 `dubbo://` 协议。更多协议,可见 http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html 文档 # 配置扫描 Dubbo 自定义的 @Service 注解,暴露成 Dubbo 服务提供者 scan: base-packages: cn.iocoder.springcloudalibaba.labx17.accountservice.service # Spring Cloud Alibaba Dubbo 专属配置项,对应 DubboCloudProperties 类 cloud: subscribed-services: '' # 设置订阅的应用列表,默认为 * 订阅所有应用。 # Seata 配置项,对应 SeataProperties 类 seata: application-id: ${spring.application.name} # Seata 应用编号,默认为 ${spring.application.name} tx-service-group: ${spring.application.name}-group # Seata 事务组编号,用于 TC 集群名 # 服务配置项,对应 ServiceProperties 类 service: # 虚拟组和分组的映射 vgroup-mapping: account-service-group: default # 分组和 Seata 服务的映射 grouplist: default: 127.0.0.1:8091 ================================================ FILE: labx-17/labx-17-sca-seata-at-dubbo-demo/labx-17-sca-seata-at-dubbo-demo-account-service/src/main/resources/application.yaml ================================================ spring: application: name: account-service datasource: url: jdbc:mysql://127.0.0.1:3306/seata_account?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: cloud: # Nacos 作为注册中心的配置项 nacos: discovery: server-addr: 127.0.0.1:8848 # dubbo 配置项,对应 DubboConfigurationProperties 配置类 dubbo: # Dubbo 服务注册中心配置,对应 RegistryConfig 类 registry: address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址 # Dubbo 服务提供者协议配置 protocol: port: -1 # 协议端口。使用 -1 表示随机端口。 name: dubbo # 使用 `dubbo://` 协议。更多协议,可见 http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html 文档 # 配置扫描 Dubbo 自定义的 @Service 注解,暴露成 Dubbo 服务提供者 scan: base-packages: cn.iocoder.springcloudalibaba.labx17.accountservice.service # Spring Cloud Alibaba Dubbo 专属配置项,对应 DubboCloudProperties 类 cloud: subscribed-services: '' # 设置订阅的应用列表,默认为 * 订阅所有应用。 # Seata 配置项,对应 SeataProperties 类 seata: application-id: ${spring.application.name} # Seata 应用编号,默认为 ${spring.application.name} tx-service-group: ${spring.application.name}-group # Seata 事务组编号,用于 TC 集群名 # Seata 服务配置项,对应 ServiceProperties 类 service: # 虚拟组和分组的映射 vgroup-mapping: account-service-group: default # Seata 注册中心配置项,对应 RegistryProperties 类 registry: type: nacos # 注册中心类型,默认为 file nacos: cluster: default # 使用的 Seata 分组 namespace: # Nacos 命名空间 serverAddr: localhost # Nacos 服务地址 ================================================ FILE: labx-17/labx-17-sca-seata-at-dubbo-demo/labx-17-sca-seata-at-dubbo-demo-account-service-api/pom.xml ================================================ labx-17-sca-seata-at-dubbo-demo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-17-sca-seata-at-dubbo-demo-account-service-api ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx17.accountservice.api; /** * 账户 Service */ public interface AccountService { /** * 扣除余额 * * @param userId 用户编号 * @param price 扣减金额 * @throws Exception 失败时抛出异常 */ void reduceBalance(Long userId, Integer price) throws Exception; } ================================================ FILE: labx-17/labx-17-sca-seata-at-dubbo-demo/labx-17-sca-seata-at-dubbo-demo-order-service/pom.xml ================================================ labx-17-sca-seata-at-dubbo-demo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-17-sca-seata-at-dubbo-demo-order-service 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import cn.iocoder.springboot.labs labx-17-sca-seata-at-dubbo-demo-order-service-api 1.0-SNAPSHOT cn.iocoder.springboot.labs labx-17-sca-seata-at-dubbo-demo-account-service-api 1.0-SNAPSHOT cn.iocoder.springboot.labs labx-17-sca-seata-at-dubbo-demo-product-service-api 1.0-SNAPSHOT org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 5.1.48 com.alibaba druid-spring-boot-starter 1.1.10 org.mybatis.spring.boot mybatis-spring-boot-starter 2.1.2 com.alibaba.cloud spring-cloud-alibaba-seata io.seata seata-spring-boot-starter 1.1.0 com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery com.alibaba.cloud spring-cloud-starter-dubbo ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx17.orderservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class OrderServiceApplication { public static void main(String[] args) { SpringApplication.run(OrderServiceApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx17.orderservice.controller; import cn.iocoder.springcloudalibaba.labx17.orderservice.api.OrderService; import org.apache.dubbo.config.annotation.Reference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/order") public class OrderController { private Logger logger = LoggerFactory.getLogger(OrderController.class); @Reference private OrderService orderService; @PostMapping("/create") public Integer createOrder(@RequestParam("userId") Long userId, @RequestParam("productId") Long productId, @RequestParam("price") Integer price) throws Exception { logger.info("[createOrder] 收到下单请求,用户:{}, 商品:{}, 价格:{}", userId, productId, price); return orderService.createOrder(userId, productId, price); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx17.orderservice.dao; import cn.iocoder.springcloudalibaba.labx17.orderservice.entity.OrderDO; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Options; import org.springframework.stereotype.Repository; @Mapper @Repository public interface OrderDao { /** * 插入订单记录 * * @param order 订单 * @return 影响记录数量 */ @Insert("INSERT INTO orders (user_id, product_id, pay_amount) VALUES (#{userId}, #{productId}, #{payAmount})") @Options(useGeneratedKeys = true, keyColumn = "id", keyProperty = "id") int saveOrder(OrderDO order); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx17.orderservice.entity; /** * 订单实体 */ public class OrderDO { /** 订单编号 **/ private Integer id; /** 用户编号 **/ private Long userId; /** 产品编号 **/ private Long productId; /** 支付金额 **/ private Integer payAmount; public Integer getId() { return id; } public OrderDO setId(Integer id) { this.id = id; return this; } public Long getUserId() { return userId; } public OrderDO setUserId(Long userId) { this.userId = userId; return this; } public Long getProductId() { return productId; } public OrderDO setProductId(Long productId) { this.productId = productId; return this; } public Integer getPayAmount() { return payAmount; } public OrderDO setPayAmount(Integer payAmount) { this.payAmount = payAmount; return this; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx17.orderservice.service; import cn.iocoder.springcloudalibaba.labx17.accountservice.api.AccountService; import cn.iocoder.springcloudalibaba.labx17.orderservice.api.OrderService; import cn.iocoder.springcloudalibaba.labx17.orderservice.dao.OrderDao; import cn.iocoder.springcloudalibaba.labx17.orderservice.entity.OrderDO; import cn.iocoder.springcloudalibaba.labx17.producctservice.api.ProductService; import io.seata.core.context.RootContext; import io.seata.spring.annotation.GlobalTransactional; import org.apache.dubbo.config.annotation.Reference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @org.apache.dubbo.config.annotation.Service public class OrderServiceImpl implements OrderService { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private OrderDao orderDao; @Reference private AccountService accountService; @Reference private ProductService productService; @Override @GlobalTransactional public Integer createOrder(Long userId, Long productId, Integer price) throws Exception { Integer amount = 1; // 购买数量,暂时设置为 1。 logger.info("[createOrder] 当前 XID: {}", RootContext.getXID()); // 扣减库存 productService.reduceStock(productId, amount); // 扣减余额 accountService.reduceBalance(userId, price); // 保存订单 OrderDO order = new OrderDO().setUserId(userId).setProductId(productId).setPayAmount(amount * price); orderDao.saveOrder(order); logger.info("[createOrder] 保存订单: {}", order.getId()); // 返回订单编号 return order.getId(); } } ================================================ FILE: labx-17/labx-17-sca-seata-at-dubbo-demo/labx-17-sca-seata-at-dubbo-demo-order-service/src/main/resources/application-file.yaml ================================================ server: port: 8081 # 端口 spring: application: name: order-service datasource: url: jdbc:mysql://127.0.0.1:3306/seata_order?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: cloud: # Nacos 作为注册中心的配置项 nacos: discovery: server-addr: 127.0.0.1:8848 # dubbo 配置项,对应 DubboConfigurationProperties 配置类 dubbo: # Dubbo 服务注册中心配置,对应 RegistryConfig 类 registry: address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址 # Dubbo 服务提供者协议配置 protocol: port: -1 # 协议端口。使用 -1 表示随机端口。 name: dubbo # 使用 `dubbo://` 协议。更多协议,可见 http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html 文档 # 配置扫描 Dubbo 自定义的 @Service 注解,暴露成 Dubbo 服务提供者 scan: base-packages: cn.iocoder.springcloudalibaba.labx17.orderservice.service # Spring Cloud Alibaba Dubbo 专属配置项,对应 DubboCloudProperties 类 cloud: subscribed-services: account-service, product-service # 设置订阅的应用列表,默认为 * 订阅所有应用。 # Seata 配置项,对应 SeataProperties 类 seata: application-id: ${spring.application.name} # Seata 应用编号,默认为 ${spring.application.name} tx-service-group: ${spring.application.name}-group # Seata 事务组编号,用于 TC 集群名 # 服务配置项,对应 ServiceProperties 类 service: # 虚拟组和分组的映射 vgroup-mapping: order-service-group: default # 分组和 Seata 服务的映射 grouplist: default: 127.0.0.1:8091 ================================================ FILE: labx-17/labx-17-sca-seata-at-dubbo-demo/labx-17-sca-seata-at-dubbo-demo-order-service/src/main/resources/application.yaml ================================================ server: port: 8081 # 端口 spring: application: name: order-service datasource: url: jdbc:mysql://127.0.0.1:3306/seata_order?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: cloud: # Nacos 作为注册中心的配置项 nacos: discovery: server-addr: 127.0.0.1:8848 # dubbo 配置项,对应 DubboConfigurationProperties 配置类 dubbo: # Dubbo 服务注册中心配置,对应 RegistryConfig 类 registry: address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址 # Dubbo 服务提供者协议配置 protocol: port: -1 # 协议端口。使用 -1 表示随机端口。 name: dubbo # 使用 `dubbo://` 协议。更多协议,可见 http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html 文档 # 配置扫描 Dubbo 自定义的 @Service 注解,暴露成 Dubbo 服务提供者 scan: base-packages: cn.iocoder.springcloudalibaba.labx17.orderservice.service # Spring Cloud Alibaba Dubbo 专属配置项,对应 DubboCloudProperties 类 cloud: subscribed-services: account-service, product-service # 设置订阅的应用列表,默认为 * 订阅所有应用。 # Seata 配置项,对应 SeataProperties 类 seata: application-id: ${spring.application.name} # Seata 应用编号,默认为 ${spring.application.name} tx-service-group: ${spring.application.name}-group # Seata 事务组编号,用于 TC 集群名 # Seata 服务配置项,对应 ServiceProperties 类 service: # 虚拟组和分组的映射 vgroup-mapping: order-service-group: default # Seata 注册中心配置项,对应 RegistryProperties 类 registry: type: nacos # 注册中心类型,默认为 file nacos: cluster: default # 使用的 Seata 分组 namespace: # Nacos 命名空间 serverAddr: localhost # Nacos 服务地址 ================================================ FILE: labx-17/labx-17-sca-seata-at-dubbo-demo/labx-17-sca-seata-at-dubbo-demo-order-service-api/pom.xml ================================================ labx-17-sca-seata-at-dubbo-demo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-17-sca-seata-at-dubbo-demo-order-service-api ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx17.orderservice.api; /** * 订单 Service */ public interface OrderService { /** * 创建订单 * * @param userId 用户编号 * @param productId 产品编号 * @param price 价格 * @return 订单编号 * @throws Exception 创建订单失败,抛出异常 */ Integer createOrder(Long userId, Long productId, Integer price) throws Exception; } ================================================ FILE: labx-17/labx-17-sca-seata-at-dubbo-demo/labx-17-sca-seata-at-dubbo-demo-product-service/pom.xml ================================================ labx-17-sca-seata-at-dubbo-demo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-17-sca-seata-at-dubbo-demo-product-service 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import cn.iocoder.springboot.labs labx-17-sca-seata-at-dubbo-demo-product-service-api 1.0-SNAPSHOT org.springframework.boot spring-boot-starter org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 5.1.48 com.alibaba druid-spring-boot-starter 1.1.10 org.mybatis.spring.boot mybatis-spring-boot-starter 2.1.2 com.alibaba.cloud spring-cloud-alibaba-seata io.seata seata-spring-boot-starter 1.1.0 com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery com.alibaba.cloud spring-cloud-starter-dubbo ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx17.productservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ProductServiceApplication { public static void main(String[] args) { SpringApplication.run(ProductServiceApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx17.productservice.dao; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; import org.springframework.stereotype.Repository; @Mapper @Repository public interface ProductDao { /** * 获取库存 * * @param productId 商品编号 * @return 库存 */ @Select("SELECT stock FROM product WHERE id = #{productId}") Integer getStock(@Param("productId") Long productId); /** * 扣减库存 * * @param productId 商品编号 * @param amount 扣减数量 * @return 影响记录行数 */ @Update("UPDATE product SET stock = stock - #{amount} WHERE id = #{productId} AND stock >= #{amount}") int reduceStock(@Param("productId") Long productId, @Param("amount") Integer amount); } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx17.productservice.service; import cn.iocoder.springcloudalibaba.labx17.producctservice.api.ProductService; import cn.iocoder.springcloudalibaba.labx17.productservice.dao.ProductDao; import io.seata.core.context.RootContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; @org.apache.dubbo.config.annotation.Service public class ProductServiceImpl implements ProductService { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private ProductDao productDao; @Override @Transactional // 开启新事物 public void reduceStock(Long productId, Integer amount) throws Exception { logger.info("[reduceStock] 当前 XID: {}", RootContext.getXID()); // 检查库存 checkStock(productId, amount); logger.info("[reduceStock] 开始扣减 {} 库存", productId); // 扣减库存 int updateCount = productDao.reduceStock(productId, amount); // 扣除成功 if (updateCount == 0) { logger.warn("[reduceStock] 扣除 {} 库存失败", productId); throw new Exception("库存不足"); } // 扣除失败 logger.info("[reduceStock] 扣除 {} 库存成功", productId); } private void checkStock(Long productId, Integer requiredAmount) throws Exception { logger.info("[checkStock] 检查 {} 库存", productId); Integer stock = productDao.getStock(productId); if (stock < requiredAmount) { logger.warn("[checkStock] {} 库存不足,当前库存: {}", productId, stock); throw new Exception("库存不足"); } } } ================================================ FILE: labx-17/labx-17-sca-seata-at-dubbo-demo/labx-17-sca-seata-at-dubbo-demo-product-service/src/main/resources/application-file.yaml ================================================ spring: application: name: product-service datasource: url: jdbc:mysql://127.0.0.1:3306/seata_product?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: cloud: # Nacos 作为注册中心的配置项 nacos: discovery: server-addr: 127.0.0.1:8848 # dubbo 配置项,对应 DubboConfigurationProperties 配置类 dubbo: # Dubbo 服务注册中心配置,对应 RegistryConfig 类 registry: address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址 # Dubbo 服务提供者协议配置 protocol: port: -1 # 协议端口。使用 -1 表示随机端口。 name: dubbo # 使用 `dubbo://` 协议。更多协议,可见 http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html 文档 # 配置扫描 Dubbo 自定义的 @Service 注解,暴露成 Dubbo 服务提供者 scan: base-packages: cn.iocoder.springcloudalibaba.labx17.productservice.service # Spring Cloud Alibaba Dubbo 专属配置项,对应 DubboCloudProperties 类 cloud: subscribed-services: '' # 设置订阅的应用列表,默认为 * 订阅所有应用。 # Seata 配置项,对应 SeataProperties 类 seata: application-id: ${spring.application.name} # Seata 应用编号,默认为 ${spring.application.name} tx-service-group: ${spring.application.name}-group # Seata 事务组编号,用于 TC 集群名 # 服务配置项,对应 ServiceProperties 类 service: # 虚拟组和分组的映射 vgroup-mapping: product-service-group: default # 分组和 Seata 服务的映射 grouplist: default: 127.0.0.1:8091 ================================================ FILE: labx-17/labx-17-sca-seata-at-dubbo-demo/labx-17-sca-seata-at-dubbo-demo-product-service/src/main/resources/application.yaml ================================================ spring: application: name: product-service datasource: url: jdbc:mysql://127.0.0.1:3306/seata_product?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver username: root password: cloud: # Nacos 作为注册中心的配置项 nacos: discovery: server-addr: 127.0.0.1:8848 # dubbo 配置项,对应 DubboConfigurationProperties 配置类 dubbo: # Dubbo 服务注册中心配置,对应 RegistryConfig 类 registry: address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址 # Dubbo 服务提供者协议配置 protocol: port: -1 # 协议端口。使用 -1 表示随机端口。 name: dubbo # 使用 `dubbo://` 协议。更多协议,可见 http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html 文档 # 配置扫描 Dubbo 自定义的 @Service 注解,暴露成 Dubbo 服务提供者 scan: base-packages: cn.iocoder.springcloudalibaba.labx17.productservice.service # Spring Cloud Alibaba Dubbo 专属配置项,对应 DubboCloudProperties 类 cloud: subscribed-services: '' # 设置订阅的应用列表,默认为 * 订阅所有应用。 # Seata 配置项,对应 SeataProperties 类 seata: application-id: ${spring.application.name} # Seata 应用编号,默认为 ${spring.application.name} tx-service-group: ${spring.application.name}-group # Seata 事务组编号,用于 TC 集群名 # Seata 服务配置项,对应 ServiceProperties 类 service: # 虚拟组和分组的映射 vgroup-mapping: product-service-group: default # Seata 注册中心配置项,对应 RegistryProperties 类 registry: type: nacos # 注册中心类型,默认为 file nacos: cluster: default # 使用的 Seata 分组 namespace: # Nacos 命名空间 serverAddr: localhost # Nacos 服务地址 ================================================ FILE: labx-17/labx-17-sca-seata-at-dubbo-demo/labx-17-sca-seata-at-dubbo-demo-product-service-api/pom.xml ================================================ labx-17-sca-seata-at-dubbo-demo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-17-sca-seata-at-dubbo-demo-product-service-api ================================================ FILE: 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 ================================================ package cn.iocoder.springcloudalibaba.labx17.producctservice.api; /** * 商品 Service */ public interface ProductService { /** * 扣减库存 * * @param productId 商品 ID * @param amount 扣减数量 * @throws Exception 扣减失败时抛出异常 */ void reduceStock(Long productId, Integer amount) throws Exception; } ================================================ FILE: labx-17/labx-17-sca-seata-at-dubbo-demo/pom.xml ================================================ labx-17 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-17-sca-seata-at-dubbo-demo labx-17-sca-seata-at-dubbo-demo-account-service-api labx-17-sca-seata-at-dubbo-demo-account-service labx-17-sca-seata-at-dubbo-demo-product-service-api labx-17-sca-seata-at-dubbo-demo-product-service labx-17-sca-seata-at-dubbo-demo-order-service-api labx-17-sca-seata-at-dubbo-demo-order-service ================================================ FILE: labx-17/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-17 pom labx-17-sca-seata-at-dubbo-demo labx-17-sc-seata-at-feign-demo ================================================ FILE: labx-17/《芋道 Spring Cloud Alibaba 分布式事务 Seata 入门》.md ================================================ ================================================ FILE: labx-18/labx-18-sc-bus-rabbitmq-demo-listener/pom.xml ================================================ labx-18 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-18-sc-bus-rabbitmq-demo-listener 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-bus-amqp ================================================ FILE: labx-18/labx-18-sc-bus-rabbitmq-demo-listener/src/main/java/cn/iocoder/springcloud/labx18/listenerdemo/ListenerDemoApplication.java ================================================ package cn.iocoder.springcloud.labx18.listenerdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.bus.jackson.RemoteApplicationEventScan; @SpringBootApplication @RemoteApplicationEventScan public class ListenerDemoApplication { public static void main(String[] args) { SpringApplication.run(ListenerDemoApplication.class, args); } } ================================================ FILE: labx-18/labx-18-sc-bus-rabbitmq-demo-listener/src/main/java/cn/iocoder/springcloud/labx18/listenerdemo/event/UserRegisterEvent.java ================================================ package cn.iocoder.springcloud.labx18.listenerdemo.event; import org.springframework.cloud.bus.event.RemoteApplicationEvent; /** * 用户注册事件 */ public class UserRegisterEvent extends RemoteApplicationEvent { /** * 用户名 */ private String username; public UserRegisterEvent() { // 序列化 } public UserRegisterEvent(Object source, String originService, String destinationService, String username) { super(source, originService); this.username = username; } public String getUsername() { return username; } } ================================================ FILE: labx-18/labx-18-sc-bus-rabbitmq-demo-listener/src/main/java/cn/iocoder/springcloud/labx18/listenerdemo/listener/UserRegisterListener.java ================================================ package cn.iocoder.springcloud.labx18.listenerdemo.listener; import cn.iocoder.springcloud.labx18.listenerdemo.event.UserRegisterEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; /** * 用户注册事件的监听器 */ @Component public class UserRegisterListener implements ApplicationListener { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public void onApplicationEvent(UserRegisterEvent event) { logger.info("[onApplicationEvent][监听到用户({}) 注册]", event.getUsername()); } } ================================================ FILE: labx-18/labx-18-sc-bus-rabbitmq-demo-listener/src/main/resources/application.yml ================================================ spring: application: name: listener-demo # RabbitMQ 相关配置项 rabbitmq: host: localhost port: 5672 username: guest password: guest server: port: ${random.int[10000,19999]} # 随机端口,方便启动多个消费者 ================================================ FILE: labx-18/labx-18-sc-bus-rabbitmq-demo-listener-actuator/pom.xml ================================================ labx-18 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-18-sc-bus-rabbitmq-demo-listener-actuator 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-bus-amqp org.springframework.boot spring-boot-starter-actuator ================================================ FILE: labx-18/labx-18-sc-bus-rabbitmq-demo-listener-actuator/src/main/java/cn/iocoder/springcloud/labx18/listenerdemo/ListenerDemoApplication.java ================================================ package cn.iocoder.springcloud.labx18.listenerdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.bus.jackson.RemoteApplicationEventScan; @SpringBootApplication @RemoteApplicationEventScan public class ListenerDemoApplication { public static void main(String[] args) { SpringApplication.run(ListenerDemoApplication.class, args); } } ================================================ FILE: labx-18/labx-18-sc-bus-rabbitmq-demo-listener-actuator/src/main/java/cn/iocoder/springcloud/labx18/listenerdemo/event/UserRegisterEvent.java ================================================ package cn.iocoder.springcloud.labx18.listenerdemo.event; import org.springframework.cloud.bus.event.RemoteApplicationEvent; /** * 用户注册事件 */ public class UserRegisterEvent extends RemoteApplicationEvent { /** * 用户名 */ private String username; public UserRegisterEvent() { // 序列化 } public UserRegisterEvent(Object source, String originService, String destinationService, String username) { super(source, originService); this.username = username; } public String getUsername() { return username; } } ================================================ FILE: labx-18/labx-18-sc-bus-rabbitmq-demo-listener-actuator/src/main/java/cn/iocoder/springcloud/labx18/listenerdemo/listener/UserRegisterListener.java ================================================ package cn.iocoder.springcloud.labx18.listenerdemo.listener; import cn.iocoder.springcloud.labx18.listenerdemo.event.UserRegisterEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; /** * 用户注册事件的监听器 */ @Component public class UserRegisterListener implements ApplicationListener { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public void onApplicationEvent(UserRegisterEvent event) { logger.info("[onApplicationEvent][监听到用户({}) 注册]", event.getUsername()); } } ================================================ FILE: labx-18/labx-18-sc-bus-rabbitmq-demo-listener-actuator/src/main/resources/application.yml ================================================ spring: application: name: listener-demo # RabbitMQ 相关配置项 rabbitmq: host: localhost port: 5672 username: guest password: guest server: port: 18080 # 随机端口,方便启动多个消费者 management: endpoints: # Actuator HTTP 配置项,对应 WebEndpointProperties 配置类 web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 ================================================ FILE: labx-18/labx-18-sc-bus-rabbitmq-demo-publisher/pom.xml ================================================ labx-18 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-18-sc-bus-rabbitmq-demo-publisher 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-bus-amqp ================================================ FILE: labx-18/labx-18-sc-bus-rabbitmq-demo-publisher/src/main/java/cn/iocoder/springcloud/labx18/publisherdemo/PublisherDemoApplication.java ================================================ package cn.iocoder.springcloud.labx18.publisherdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication //@RemoteApplicationEventScan public class PublisherDemoApplication { public static void main(String[] args) { SpringApplication.run(PublisherDemoApplication.class, args); } } ================================================ FILE: labx-18/labx-18-sc-bus-rabbitmq-demo-publisher/src/main/java/cn/iocoder/springcloud/labx18/publisherdemo/controller/DemoController.java ================================================ package cn.iocoder.springcloud.labx18.publisherdemo.controller; import cn.iocoder.springcloud.labx18.publisherdemo.event.UserRegisterEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.bus.ServiceMatcher; import org.springframework.context.ApplicationEventPublisher; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private ApplicationEventPublisher applicationEventPublisher; @Autowired private ServiceMatcher busServiceMatcher; @GetMapping("/register") public String register(String username) { // ... 执行注册逻辑 logger.info("[register][执行用户({}) 的注册逻辑]", username); // ... 发布 applicationEventPublisher.publishEvent(new UserRegisterEvent(this, busServiceMatcher.getServiceId(), null, username)); return "success"; } } ================================================ FILE: labx-18/labx-18-sc-bus-rabbitmq-demo-publisher/src/main/java/cn/iocoder/springcloud/labx18/publisherdemo/event/UserRegisterEvent.java ================================================ package cn.iocoder.springcloud.labx18.publisherdemo.event; import org.springframework.cloud.bus.event.RemoteApplicationEvent; /** * 用户注册事件 */ public class UserRegisterEvent extends RemoteApplicationEvent { /** * 用户名 */ private String username; public UserRegisterEvent() { // 序列化 } public UserRegisterEvent(Object source, String originService, String destinationService, String username) { super(source, originService); this.username = username; } public String getUsername() { return username; } } ================================================ FILE: labx-18/labx-18-sc-bus-rabbitmq-demo-publisher/src/main/resources/application.yml ================================================ spring: application: name: publisher-demo # RabbitMQ 相关配置项 rabbitmq: host: localhost port: 5672 username: guest password: guest # Bus 相关配置项,对应 BusProperties cloud: bus: enabled: true # 是否开启,默认为 true destination: springCloudBus # 目标消息队列,默认为 springCloudBus ================================================ FILE: labx-18/labx-18-sc-bus-rabbitmq-demo-publisher-actuator/pom.xml ================================================ labx-18 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-18-sc-bus-rabbitmq-demo-publisher-actuator 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-bus-amqp org.springframework.boot spring-boot-starter-actuator ================================================ FILE: labx-18/labx-18-sc-bus-rabbitmq-demo-publisher-actuator/src/main/java/cn/iocoder/springcloud/labx18/publisherdemo/PublisherDemoApplication.java ================================================ package cn.iocoder.springcloud.labx18.publisherdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication //@RemoteApplicationEventScan public class PublisherDemoApplication { public static void main(String[] args) { SpringApplication.run(PublisherDemoApplication.class, args); } } ================================================ FILE: labx-18/labx-18-sc-bus-rabbitmq-demo-publisher-actuator/src/main/java/cn/iocoder/springcloud/labx18/publisherdemo/controller/DemoController.java ================================================ package cn.iocoder.springcloud.labx18.publisherdemo.controller; import cn.iocoder.springcloud.labx18.publisherdemo.event.UserRegisterEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.bus.ServiceMatcher; import org.springframework.context.ApplicationEventPublisher; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private ApplicationEventPublisher applicationEventPublisher; @Autowired private ServiceMatcher busServiceMatcher; @GetMapping("/register") public String register(String username) { // ... 执行注册逻辑 logger.info("[register][执行用户({}) 的注册逻辑]", username); // ... 发布 applicationEventPublisher.publishEvent(new UserRegisterEvent(this, busServiceMatcher.getServiceId(), null, username)); return "success"; } } ================================================ FILE: labx-18/labx-18-sc-bus-rabbitmq-demo-publisher-actuator/src/main/java/cn/iocoder/springcloud/labx18/publisherdemo/event/UserRegisterEvent.java ================================================ package cn.iocoder.springcloud.labx18.publisherdemo.event; import org.springframework.cloud.bus.event.RemoteApplicationEvent; /** * 用户注册事件 */ public class UserRegisterEvent extends RemoteApplicationEvent { /** * 用户名 */ private String username; public UserRegisterEvent() { // 序列化 } public UserRegisterEvent(Object source, String originService, String destinationService, String username) { super(source, originService); this.username = username; } public String getUsername() { return username; } } ================================================ FILE: labx-18/labx-18-sc-bus-rabbitmq-demo-publisher-actuator/src/main/resources/application.yml ================================================ spring: application: name: publisher-demo # RabbitMQ 相关配置项 rabbitmq: host: localhost port: 5672 username: guest password: guest management: endpoints: # Actuator HTTP 配置项,对应 WebEndpointProperties 配置类 web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 ================================================ FILE: labx-18/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-18 pom labx-18-sc-bus-rabbitmq-demo-publisher labx-18-sc-bus-rabbitmq-demo-listener labx-18-sc-bus-rabbitmq-demo-listener-actuator labx-18-sc-bus-rabbitmq-demo-publisher-actuator ================================================ FILE: labx-18/《芋道 Spring Cloud 事件总线 RabbitMQ 入门》.md ================================================ ================================================ FILE: labx-19/labx-19-sc-bus-kafka-demo-listener/pom.xml ================================================ labx-19 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-19-sc-bus-kafka-demo-listener 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-bus-kafka ================================================ FILE: labx-19/labx-19-sc-bus-kafka-demo-listener/src/main/java/cn/iocoder/springcloud/labx19/listenerdemo/ListenerDemoApplication.java ================================================ package cn.iocoder.springcloud.labx19.listenerdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.bus.jackson.RemoteApplicationEventScan; @SpringBootApplication @RemoteApplicationEventScan public class ListenerDemoApplication { public static void main(String[] args) { SpringApplication.run(ListenerDemoApplication.class, args); } } ================================================ FILE: labx-19/labx-19-sc-bus-kafka-demo-listener/src/main/java/cn/iocoder/springcloud/labx19/listenerdemo/event/UserRegisterEvent.java ================================================ package cn.iocoder.springcloud.labx19.listenerdemo.event; import org.springframework.cloud.bus.event.RemoteApplicationEvent; /** * 用户注册事件 */ public class UserRegisterEvent extends RemoteApplicationEvent { /** * 用户名 */ private String username; public UserRegisterEvent() { // 序列化 } public UserRegisterEvent(Object source, String originService, String destinationService, String username) { super(source, originService); this.username = username; } public String getUsername() { return username; } } ================================================ FILE: labx-19/labx-19-sc-bus-kafka-demo-listener/src/main/java/cn/iocoder/springcloud/labx19/listenerdemo/listener/UserRegisterListener.java ================================================ package cn.iocoder.springcloud.labx19.listenerdemo.listener; import cn.iocoder.springcloud.labx19.listenerdemo.event.UserRegisterEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; /** * 用户注册事件的监听器 */ @Component public class UserRegisterListener implements ApplicationListener { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public void onApplicationEvent(UserRegisterEvent event) { logger.info("[onApplicationEvent][监听到用户({}) 注册]", event.getUsername()); } } ================================================ FILE: labx-19/labx-19-sc-bus-kafka-demo-listener/src/main/resources/application.yml ================================================ spring: application: name: listener-demo # Kafka 配置项,对应 KafkaProperties 配置类 kafka: bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 server: port: ${random.int[10000,19999]} # 随机端口,方便启动多个消费者 ================================================ FILE: labx-19/labx-19-sc-bus-kafka-demo-listener-actuator/pom.xml ================================================ labx-19 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-19-sc-bus-kafka-demo-listener-actuator 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-bus-kafka org.springframework.boot spring-boot-starter-actuator ================================================ FILE: labx-19/labx-19-sc-bus-kafka-demo-listener-actuator/src/main/java/cn/iocoder/springcloud/labx19/listenerdemo/ListenerDemoApplication.java ================================================ package cn.iocoder.springcloud.labx19.listenerdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.bus.jackson.RemoteApplicationEventScan; @SpringBootApplication @RemoteApplicationEventScan public class ListenerDemoApplication { public static void main(String[] args) { SpringApplication.run(ListenerDemoApplication.class, args); } } ================================================ FILE: labx-19/labx-19-sc-bus-kafka-demo-listener-actuator/src/main/java/cn/iocoder/springcloud/labx19/listenerdemo/event/UserRegisterEvent.java ================================================ package cn.iocoder.springcloud.labx19.listenerdemo.event; import org.springframework.cloud.bus.event.RemoteApplicationEvent; /** * 用户注册事件 */ public class UserRegisterEvent extends RemoteApplicationEvent { /** * 用户名 */ private String username; public UserRegisterEvent() { // 序列化 } public UserRegisterEvent(Object source, String originService, String destinationService, String username) { super(source, originService); this.username = username; } public String getUsername() { return username; } } ================================================ FILE: labx-19/labx-19-sc-bus-kafka-demo-listener-actuator/src/main/java/cn/iocoder/springcloud/labx19/listenerdemo/listener/UserRegisterListener.java ================================================ package cn.iocoder.springcloud.labx19.listenerdemo.listener; import cn.iocoder.springcloud.labx19.listenerdemo.event.UserRegisterEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; /** * 用户注册事件的监听器 */ @Component public class UserRegisterListener implements ApplicationListener { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public void onApplicationEvent(UserRegisterEvent event) { logger.info("[onApplicationEvent][监听到用户({}) 注册]", event.getUsername()); } } ================================================ FILE: labx-19/labx-19-sc-bus-kafka-demo-listener-actuator/src/main/resources/application.yml ================================================ spring: application: name: listener-demo # Kafka 配置项,对应 KafkaProperties 配置类 kafka: bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 server: port: 18080 # 随机端口,方便启动多个消费者 management: endpoints: # Actuator HTTP 配置项,对应 WebEndpointProperties 配置类 web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 ================================================ FILE: labx-19/labx-19-sc-bus-kafka-demo-publisher/pom.xml ================================================ labx-19 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-19-sc-bus-kafka-demo-publisher 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-bus-kafka ================================================ FILE: labx-19/labx-19-sc-bus-kafka-demo-publisher/src/main/java/cn/iocoder/springcloud/labx19/publisherdemo/PublisherDemoApplication.java ================================================ package cn.iocoder.springcloud.labx19.publisherdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication //@RemoteApplicationEventScan public class PublisherDemoApplication { public static void main(String[] args) { SpringApplication.run(PublisherDemoApplication.class, args); } } ================================================ FILE: labx-19/labx-19-sc-bus-kafka-demo-publisher/src/main/java/cn/iocoder/springcloud/labx19/publisherdemo/controller/DemoController.java ================================================ package cn.iocoder.springcloud.labx19.publisherdemo.controller; import cn.iocoder.springcloud.labx19.publisherdemo.event.UserRegisterEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.bus.ServiceMatcher; import org.springframework.context.ApplicationEventPublisher; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private ApplicationEventPublisher applicationEventPublisher; @Autowired private ServiceMatcher busServiceMatcher; @GetMapping("/register") public String register(String username) { // ... 执行注册逻辑 logger.info("[register][执行用户({}) 的注册逻辑]", username); // ... 发布 applicationEventPublisher.publishEvent(new UserRegisterEvent(this, busServiceMatcher.getServiceId(), null, username)); return "success"; } } ================================================ FILE: labx-19/labx-19-sc-bus-kafka-demo-publisher/src/main/java/cn/iocoder/springcloud/labx19/publisherdemo/event/UserRegisterEvent.java ================================================ package cn.iocoder.springcloud.labx19.publisherdemo.event; import org.springframework.cloud.bus.event.RemoteApplicationEvent; /** * 用户注册事件 */ public class UserRegisterEvent extends RemoteApplicationEvent { /** * 用户名 */ private String username; public UserRegisterEvent() { // 序列化 } public UserRegisterEvent(Object source, String originService, String destinationService, String username) { super(source, originService); this.username = username; } public String getUsername() { return username; } } ================================================ FILE: labx-19/labx-19-sc-bus-kafka-demo-publisher/src/main/resources/application.yml ================================================ spring: application: name: publisher-demo # Kafka 配置项,对应 KafkaProperties 配置类 kafka: bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 # Bus 相关配置项,对应 BusProperties cloud: bus: enabled: true # 是否开启,默认为 true destination: springCloudBus # 目标消息队列,默认为 springCloudBus ================================================ FILE: labx-19/labx-19-sc-bus-kafka-demo-publisher-actuator/pom.xml ================================================ labx-19 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-19-sc-bus-kafka-demo-publisher-actuator 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-bus-kafka org.springframework.boot spring-boot-starter-actuator ================================================ FILE: labx-19/labx-19-sc-bus-kafka-demo-publisher-actuator/src/main/java/cn/iocoder/springcloud/labx19/publisherdemo/PublisherDemoApplication.java ================================================ package cn.iocoder.springcloud.labx19.publisherdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication //@RemoteApplicationEventScan public class PublisherDemoApplication { public static void main(String[] args) { SpringApplication.run(PublisherDemoApplication.class, args); } } ================================================ FILE: labx-19/labx-19-sc-bus-kafka-demo-publisher-actuator/src/main/java/cn/iocoder/springcloud/labx19/publisherdemo/controller/DemoController.java ================================================ package cn.iocoder.springcloud.labx19.publisherdemo.controller; import cn.iocoder.springcloud.labx19.publisherdemo.event.UserRegisterEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.bus.ServiceMatcher; import org.springframework.context.ApplicationEventPublisher; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private ApplicationEventPublisher applicationEventPublisher; @Autowired private ServiceMatcher busServiceMatcher; @GetMapping("/register") public String register(String username) { // ... 执行注册逻辑 logger.info("[register][执行用户({}) 的注册逻辑]", username); // ... 发布 applicationEventPublisher.publishEvent(new UserRegisterEvent(this, busServiceMatcher.getServiceId(), null, username)); return "success"; } } ================================================ FILE: labx-19/labx-19-sc-bus-kafka-demo-publisher-actuator/src/main/java/cn/iocoder/springcloud/labx19/publisherdemo/event/UserRegisterEvent.java ================================================ package cn.iocoder.springcloud.labx19.publisherdemo.event; import org.springframework.cloud.bus.event.RemoteApplicationEvent; /** * 用户注册事件 */ public class UserRegisterEvent extends RemoteApplicationEvent { /** * 用户名 */ private String username; public UserRegisterEvent() { // 序列化 } public UserRegisterEvent(Object source, String originService, String destinationService, String username) { super(source, originService); this.username = username; } public String getUsername() { return username; } } ================================================ FILE: labx-19/labx-19-sc-bus-kafka-demo-publisher-actuator/src/main/resources/application.yml ================================================ spring: application: name: publisher-demo # Kafka 配置项,对应 KafkaProperties 配置类 kafka: bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 management: endpoints: # Actuator HTTP 配置项,对应 WebEndpointProperties 配置类 web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 ================================================ FILE: labx-19/labx-19-sc-config-server-git-auto-refresh-by-bus/pom.xml ================================================ labx-12 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-19-sc-config-server-git-auto-refresh-by-bus 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-config-server org.springframework.cloud spring-cloud-starter-bus-kafka org.springframework.cloud spring-cloud-config-monitor ================================================ FILE: labx-19/labx-19-sc-config-server-git-auto-refresh-by-bus/src/main/java/cn/iocoder/springcloud/labx19/configserverdemo/ConfigServerApplication.java ================================================ package cn.iocoder.springcloud.labx19.configserverdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.config.server.EnableConfigServer; @SpringBootApplication @EnableConfigServer public class ConfigServerApplication { public static void main(String[] args) { SpringApplication.run(ConfigServerApplication.class, args); } } ================================================ FILE: labx-19/labx-19-sc-config-server-git-auto-refresh-by-bus/src/main/resources/application.yml ================================================ server: port: 8888 spring: application: name: demo-config-server profiles: active: git # 使用的 Spring Cloud Config Server 的存储器方案 # Spring Cloud Config 相关配置项 cloud: config: server: # Spring Cloud Config Server 的 Git 存储器的配置项,对应 MultipleJGitEnvironmentProperties 类 git: uri: https://github.com/YunaiV/demo-config-server.git # Git 仓库地址 search-paths: / # 读取文件的根地址 default-label: master # 使用的默认分支,默认为 master # username: ${CODING_USERNAME} # 账号 # password: ${CODING_PASSWORD} # 密码 # Kafka 配置项,对应 KafkaProperties 配置类 kafka: bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 ================================================ FILE: labx-19/labx-19-sc-config-user-application-auto-refresh-by-bus/pom.xml ================================================ labx-12 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-19-sc-config-user-application-auto-refresh-by-bus 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-config org.springframework.cloud spring-cloud-starter-bus-kafka ================================================ FILE: labx-19/labx-19-sc-config-user-application-auto-refresh-by-bus/src/main/java/cn/iocoder/springcloud/labx19/userapplication/UserApplication.java ================================================ package cn.iocoder.springcloud.labx19.userapplication; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class UserApplication { public static void main(String[] args) { SpringApplication.run(UserApplication.class, args); } } ================================================ FILE: labx-19/labx-19-sc-config-user-application-auto-refresh-by-bus/src/main/java/cn/iocoder/springcloud/labx19/userapplication/config/OrderProperties.java ================================================ package cn.iocoder.springcloud.labx19.userapplication.config; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component @ConfigurationProperties(prefix = "order") public class OrderProperties { /** * 订单支付超时时长,单位:秒。 */ private Integer payTimeoutSeconds; /** * 订单创建频率,单位:秒 */ private Integer createFrequencySeconds; public Integer getPayTimeoutSeconds() { return payTimeoutSeconds; } public OrderProperties setPayTimeoutSeconds(Integer payTimeoutSeconds) { this.payTimeoutSeconds = payTimeoutSeconds; return this; } public Integer getCreateFrequencySeconds() { return createFrequencySeconds; } public OrderProperties setCreateFrequencySeconds(Integer createFrequencySeconds) { this.createFrequencySeconds = createFrequencySeconds; return this; } } ================================================ FILE: labx-19/labx-19-sc-config-user-application-auto-refresh-by-bus/src/main/java/cn/iocoder/springcloud/labx19/userapplication/controller/DemoController.java ================================================ package cn.iocoder.springcloud.labx19.userapplication.controller; import cn.iocoder.springcloud.labx19.userapplication.config.OrderProperties; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.Map; @RestController @RequestMapping("/demo") @RefreshScope public class DemoController { @Autowired private OrderProperties orderProperties; /** * 测试 @ConfigurationProperties 注解的配置属性类 */ @GetMapping("/test01") public OrderProperties test01() { return orderProperties; } @Value(value = "${order.pay-timeout-seconds}") private Integer payTimeoutSeconds; @Value(value = "${order.create-frequency-seconds}") private Integer createFrequencySeconds; /** * 测试 @Value 注解的属性 */ @GetMapping("/test02") public Map test02() { Map result = new HashMap<>(); result.put("payTimeoutSeconds", payTimeoutSeconds); result.put("createFrequencySeconds", createFrequencySeconds); return result; } } ================================================ FILE: labx-19/labx-19-sc-config-user-application-auto-refresh-by-bus/src/main/resources/application.yml ================================================ # Kafka 配置项,对应 KafkaProperties 配置类 kafka: bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 ================================================ FILE: labx-19/labx-19-sc-config-user-application-auto-refresh-by-bus/src/main/resources/bootstrap.yml ================================================ spring: application: name: user-application cloud: # Spring Cloud Config Client 配置项,对应 ConfigClientProperties 类 config: uri: http://127.0.0.1:8888 # Spring Cloud Config Server 的地址 name: ${spring.application.name} # 读取的配置文件的名字,默认为 ${spring.application.name} ================================================ FILE: labx-19/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-19 pom labx-19-sc-bus-kafka-demo-publisher labx-19-sc-bus-kafka-demo-listener labx-19-sc-bus-kafka-demo-listener-actuator labx-19-sc-bus-kafka-demo-publisher-actuator labx-19-sc-config-server-git-auto-refresh-by-bus labx-19-sc-config-user-application-auto-refresh-by-bus ================================================ FILE: labx-19/《芋道 Spring Cloud 事件总线 Kafka 入门》.md ================================================ ================================================ FILE: labx-20/labx-20-sc-config-server-git-auto-refresh-by-bus/pom.xml ================================================ labx-12 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-20-sc-config-server-git-auto-refresh-by-bus 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-config-server com.alibaba.cloud spring-cloud-starter-bus-rocketmq org.springframework.cloud spring-cloud-config-monitor ================================================ FILE: labx-20/labx-20-sc-config-server-git-auto-refresh-by-bus/src/main/java/cn/iocoder/springcloud/labx20/configserverdemo/ConfigServerApplication.java ================================================ package cn.iocoder.springcloud.labx20.configserverdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.config.server.EnableConfigServer; @SpringBootApplication @EnableConfigServer public class ConfigServerApplication { public static void main(String[] args) { SpringApplication.run(ConfigServerApplication.class, args); } } ================================================ FILE: labx-20/labx-20-sc-config-server-git-auto-refresh-by-bus/src/main/resources/application.yml ================================================ server: port: 8888 spring: application: name: demo-config-server profiles: active: git # 使用的 Spring Cloud Config Server 的存储器方案 # Spring Cloud Config 相关配置项 cloud: config: server: # Spring Cloud Config Server 的 Git 存储器的配置项,对应 MultipleJGitEnvironmentProperties 类 git: uri: https://github.com/YunaiV/demo-config-server.git # Git 仓库地址 search-paths: / # 读取文件的根地址 default-label: master # 使用的默认分支,默认为 master # username: ${CODING_USERNAME} # 账号 # password: ${CODING_PASSWORD} # 密码 # rocketmq 配置项,对应 RocketMQProperties 配置类 rocketmq: name-server: 127.0.0.1:9876 # RocketMQ Namesrv ================================================ FILE: labx-20/labx-20-sc-config-user-application-auto-refresh-by-bus/pom.xml ================================================ labx-12 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-20-sc-config-user-application-auto-refresh-by-bus 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-config com.alibaba.cloud spring-cloud-starter-bus-rocketmq ================================================ FILE: labx-20/labx-20-sc-config-user-application-auto-refresh-by-bus/src/main/java/cn/iocoder/springcloud/labx20/userapplication/UserApplication.java ================================================ package cn.iocoder.springcloud.labx20.userapplication; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class UserApplication { public static void main(String[] args) { SpringApplication.run(UserApplication.class, args); } } ================================================ FILE: labx-20/labx-20-sc-config-user-application-auto-refresh-by-bus/src/main/java/cn/iocoder/springcloud/labx20/userapplication/config/OrderProperties.java ================================================ package cn.iocoder.springcloud.labx20.userapplication.config; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component @ConfigurationProperties(prefix = "order") public class OrderProperties { /** * 订单支付超时时长,单位:秒。 */ private Integer payTimeoutSeconds; /** * 订单创建频率,单位:秒 */ private Integer createFrequencySeconds; public Integer getPayTimeoutSeconds() { return payTimeoutSeconds; } public OrderProperties setPayTimeoutSeconds(Integer payTimeoutSeconds) { this.payTimeoutSeconds = payTimeoutSeconds; return this; } public Integer getCreateFrequencySeconds() { return createFrequencySeconds; } public OrderProperties setCreateFrequencySeconds(Integer createFrequencySeconds) { this.createFrequencySeconds = createFrequencySeconds; return this; } } ================================================ FILE: labx-20/labx-20-sc-config-user-application-auto-refresh-by-bus/src/main/java/cn/iocoder/springcloud/labx20/userapplication/controller/DemoController.java ================================================ package cn.iocoder.springcloud.labx20.userapplication.controller; import cn.iocoder.springcloud.labx20.userapplication.config.OrderProperties; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.Map; @RestController @RequestMapping("/demo") @RefreshScope public class DemoController { @Autowired private OrderProperties orderProperties; /** * 测试 @ConfigurationProperties 注解的配置属性类 */ @GetMapping("/test01") public OrderProperties test01() { return orderProperties; } @Value(value = "${order.pay-timeout-seconds}") private Integer payTimeoutSeconds; @Value(value = "${order.create-frequency-seconds}") private Integer createFrequencySeconds; /** * 测试 @Value 注解的属性 */ @GetMapping("/test02") public Map test02() { Map result = new HashMap<>(); result.put("payTimeoutSeconds", payTimeoutSeconds); result.put("createFrequencySeconds", createFrequencySeconds); return result; } } ================================================ FILE: labx-20/labx-20-sc-config-user-application-auto-refresh-by-bus/src/main/resources/application.yml ================================================ # rocketmq 配置项,对应 RocketMQProperties 配置类 rocketmq: name-server: 127.0.0.1:9876 # RocketMQ Namesrv server: port: 8081 ================================================ FILE: labx-20/labx-20-sc-config-user-application-auto-refresh-by-bus/src/main/resources/bootstrap.yml ================================================ spring: application: name: user-application cloud: # Spring Cloud Config Client 配置项,对应 ConfigClientProperties 类 config: uri: http://127.0.0.1:8888 # Spring Cloud Config Server 的地址 name: ${spring.application.name} # 读取的配置文件的名字,默认为 ${spring.application.name} ================================================ FILE: labx-20/labx-20-sca-bus-rocketmq-demo-listener/pom.xml ================================================ labx-20 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-20-sca-bus-rocketmq-demo-listener 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-bus-rocketmq ================================================ FILE: labx-20/labx-20-sca-bus-rocketmq-demo-listener/src/main/java/cn/iocoder/springcloudalibaba/labx20/listenerdemo/ListenerDemoApplication.java ================================================ package cn.iocoder.springcloudalibaba.labx20.listenerdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.bus.jackson.RemoteApplicationEventScan; @SpringBootApplication @RemoteApplicationEventScan public class ListenerDemoApplication { public static void main(String[] args) { SpringApplication.run(ListenerDemoApplication.class, args); } } ================================================ FILE: labx-20/labx-20-sca-bus-rocketmq-demo-listener/src/main/java/cn/iocoder/springcloudalibaba/labx20/listenerdemo/event/UserRegisterEvent.java ================================================ package cn.iocoder.springcloudalibaba.labx20.listenerdemo.event; import org.springframework.cloud.bus.event.RemoteApplicationEvent; /** * 用户注册事件 */ public class UserRegisterEvent extends RemoteApplicationEvent { /** * 用户名 */ private String username; public UserRegisterEvent() { // 序列化 } public UserRegisterEvent(Object source, String originService, String destinationService, String username) { super(source, originService); this.username = username; } public String getUsername() { return username; } } ================================================ FILE: labx-20/labx-20-sca-bus-rocketmq-demo-listener/src/main/java/cn/iocoder/springcloudalibaba/labx20/listenerdemo/listener/UserRegisterListener.java ================================================ package cn.iocoder.springcloudalibaba.labx20.listenerdemo.listener; import cn.iocoder.springcloudalibaba.labx20.listenerdemo.event.UserRegisterEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; /** * 用户注册事件的监听器 */ @Component public class UserRegisterListener implements ApplicationListener { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public void onApplicationEvent(UserRegisterEvent event) { logger.info("[onApplicationEvent][监听到用户({}) 注册]", event.getUsername()); } } ================================================ FILE: labx-20/labx-20-sca-bus-rocketmq-demo-listener/src/main/resources/application.yml ================================================ spring: application: name: listener-demo server: port: ${random.int[10000,19999]} # 随机端口,方便启动多个消费者 # rocketmq 配置项,对应 RocketMQProperties 配置类 rocketmq: name-server: 127.0.0.1:9876 # RocketMQ Namesrv ================================================ FILE: labx-20/labx-20-sca-bus-rocketmq-demo-listener-actuator/pom.xml ================================================ labx-20 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-20-sca-bus-rocketmq-demo-listener-actuator 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-bus-rocketmq org.springframework.boot spring-boot-starter-actuator ================================================ FILE: labx-20/labx-20-sca-bus-rocketmq-demo-listener-actuator/src/main/java/cn/iocoder/springcloudalibaba/labx20/listenerdemo/ListenerDemoApplication.java ================================================ package cn.iocoder.springcloudalibaba.labx20.listenerdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.bus.jackson.RemoteApplicationEventScan; @SpringBootApplication @RemoteApplicationEventScan public class ListenerDemoApplication { public static void main(String[] args) { SpringApplication.run(ListenerDemoApplication.class, args); } } ================================================ FILE: labx-20/labx-20-sca-bus-rocketmq-demo-listener-actuator/src/main/java/cn/iocoder/springcloudalibaba/labx20/listenerdemo/event/UserRegisterEvent.java ================================================ package cn.iocoder.springcloudalibaba.labx20.listenerdemo.event; import org.springframework.cloud.bus.event.RemoteApplicationEvent; /** * 用户注册事件 */ public class UserRegisterEvent extends RemoteApplicationEvent { /** * 用户名 */ private String username; public UserRegisterEvent() { // 序列化 } public UserRegisterEvent(Object source, String originService, String destinationService, String username) { super(source, originService); this.username = username; } public String getUsername() { return username; } } ================================================ FILE: labx-20/labx-20-sca-bus-rocketmq-demo-listener-actuator/src/main/java/cn/iocoder/springcloudalibaba/labx20/listenerdemo/listener/UserRegisterListener.java ================================================ package cn.iocoder.springcloudalibaba.labx20.listenerdemo.listener; import cn.iocoder.springcloudalibaba.labx20.listenerdemo.event.UserRegisterEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; /** * 用户注册事件的监听器 */ @Component public class UserRegisterListener implements ApplicationListener { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public void onApplicationEvent(UserRegisterEvent event) { logger.info("[onApplicationEvent][监听到用户({}) 注册]", event.getUsername()); } } ================================================ FILE: labx-20/labx-20-sca-bus-rocketmq-demo-listener-actuator/src/main/resources/application.yml ================================================ spring: application: name: listener-demo server: port: 18080 # 随机端口,方便启动多个消费者 # rocketmq 配置项,对应 RocketMQProperties 配置类 rocketmq: name-server: 127.0.0.1:9876 # RocketMQ Namesrv management: endpoints: # Actuator HTTP 配置项,对应 WebEndpointProperties 配置类 web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 ================================================ FILE: labx-20/labx-20-sca-bus-rocketmq-demo-publisher/pom.xml ================================================ labx-20 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-20-sca-bus-rocketmq-demo-publisher 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-bus-rocketmq ================================================ FILE: labx-20/labx-20-sca-bus-rocketmq-demo-publisher/src/main/java/cn/iocoder/springcloudalibaba/labx20/publisherdemo/PublisherDemoApplication.java ================================================ package cn.iocoder.springcloudalibaba.labx20.publisherdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication //@RemoteApplicationEventScan public class PublisherDemoApplication { public static void main(String[] args) { SpringApplication.run(PublisherDemoApplication.class, args); } } ================================================ FILE: labx-20/labx-20-sca-bus-rocketmq-demo-publisher/src/main/java/cn/iocoder/springcloudalibaba/labx20/publisherdemo/controller/DemoController.java ================================================ package cn.iocoder.springcloudalibaba.labx20.publisherdemo.controller; import cn.iocoder.springcloudalibaba.labx20.publisherdemo.event.UserRegisterEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.bus.ServiceMatcher; import org.springframework.context.ApplicationEventPublisher; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private ApplicationEventPublisher applicationEventPublisher; @Autowired private ServiceMatcher busServiceMatcher; @GetMapping("/register") public String register(String username) { // ... 执行注册逻辑 logger.info("[register][执行用户({}) 的注册逻辑]", username); // ... 发布 applicationEventPublisher.publishEvent(new UserRegisterEvent(this, busServiceMatcher.getServiceId(), null, username)); return "success"; } } ================================================ FILE: labx-20/labx-20-sca-bus-rocketmq-demo-publisher/src/main/java/cn/iocoder/springcloudalibaba/labx20/publisherdemo/event/UserRegisterEvent.java ================================================ package cn.iocoder.springcloudalibaba.labx20.publisherdemo.event; import org.springframework.cloud.bus.event.RemoteApplicationEvent; /** * 用户注册事件 */ public class UserRegisterEvent extends RemoteApplicationEvent { /** * 用户名 */ private String username; public UserRegisterEvent() { // 序列化 } public UserRegisterEvent(Object source, String originService, String destinationService, String username) { super(source, originService); this.username = username; } public String getUsername() { return username; } } ================================================ FILE: labx-20/labx-20-sca-bus-rocketmq-demo-publisher/src/main/resources/application.yml ================================================ server: port: 8081 spring: application: name: publisher-demo # Bus 相关配置项,对应 BusProperties cloud: bus: enabled: true # 是否开启,默认为 true destination: springCloudBus # 目标消息队列,默认为 springCloudBus # rocketmq 配置项,对应 RocketMQProperties 配置类 rocketmq: name-server: 127.0.0.1:9876 # RocketMQ Namesrv ================================================ FILE: labx-20/labx-20-sca-bus-rocketmq-demo-publisher-actuator/pom.xml ================================================ labx-20 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-20-sca-bus-rocketmq-demo-publisher-actuator 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-bus-rocketmq org.springframework.boot spring-boot-starter-actuator ================================================ FILE: labx-20/labx-20-sca-bus-rocketmq-demo-publisher-actuator/src/main/java/cn/iocoder/springcloudalibaba/labx20/publisherdemo/PublisherDemoApplication.java ================================================ package cn.iocoder.springcloudalibaba.labx20.publisherdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication //@RemoteApplicationEventScan public class PublisherDemoApplication { public static void main(String[] args) { SpringApplication.run(PublisherDemoApplication.class, args); } } ================================================ FILE: labx-20/labx-20-sca-bus-rocketmq-demo-publisher-actuator/src/main/java/cn/iocoder/springcloudalibaba/labx20/publisherdemo/controller/DemoController.java ================================================ package cn.iocoder.springcloudalibaba.labx20.publisherdemo.controller; import cn.iocoder.springcloudalibaba.labx20.publisherdemo.event.UserRegisterEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.bus.ServiceMatcher; import org.springframework.context.ApplicationEventPublisher; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private ApplicationEventPublisher applicationEventPublisher; @Autowired private ServiceMatcher busServiceMatcher; @GetMapping("/register") public String register(String username) { // ... 执行注册逻辑 logger.info("[register][执行用户({}) 的注册逻辑]", username); // ... 发布 applicationEventPublisher.publishEvent(new UserRegisterEvent(this, busServiceMatcher.getServiceId(), null, username)); return "success"; } } ================================================ FILE: labx-20/labx-20-sca-bus-rocketmq-demo-publisher-actuator/src/main/java/cn/iocoder/springcloudalibaba/labx20/publisherdemo/event/UserRegisterEvent.java ================================================ package cn.iocoder.springcloudalibaba.labx20.publisherdemo.event; import org.springframework.cloud.bus.event.RemoteApplicationEvent; /** * 用户注册事件 */ public class UserRegisterEvent extends RemoteApplicationEvent { /** * 用户名 */ private String username; public UserRegisterEvent() { // 序列化 } public UserRegisterEvent(Object source, String originService, String destinationService, String username) { super(source, originService); this.username = username; } public String getUsername() { return username; } } ================================================ FILE: labx-20/labx-20-sca-bus-rocketmq-demo-publisher-actuator/src/main/resources/application.yml ================================================ server: port: 8081 spring: application: name: publisher-demo # rocketmq 配置项,对应 RocketMQProperties 配置类 rocketmq: name-server: 127.0.0.1:9876 # RocketMQ Namesrv management: endpoints: # Actuator HTTP 配置项,对应 WebEndpointProperties 配置类 web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 ================================================ FILE: labx-20/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-20 pom labx-20-sca-bus-rocketmq-demo-publisher labx-20-sca-bus-rocketmq-demo-listener labx-20-sca-bus-rocketmq-demo-publisher-actuator labx-20-sca-bus-rocketmq-demo-listener-actuator labx-20-sc-config-server-git-auto-refresh-by-bus labx-20-sc-config-user-application-auto-refresh-by-bus ================================================ FILE: labx-20/《芋道 Spring Cloud Alibaba 事件总线 Bus RocketMQ 入门》.md ================================================ ================================================ FILE: labx-21/labx-21-sc-user-service/pom.xml ================================================ labx-08 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-21-sc-user-service 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery ================================================ FILE: labx-21/labx-21-sc-user-service/src/main/java/cn/iocoder/springcloud/labx21/userservice/UserServiceApplication.java ================================================ package cn.iocoder.springcloud.labx21.userservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class UserServiceApplication { public static void main(String[] args) { SpringApplication.run(UserServiceApplication.class, args); } } ================================================ FILE: labx-21/labx-21-sc-user-service/src/main/java/cn/iocoder/springcloud/labx21/userservice/controller/UserController.java ================================================ package cn.iocoder.springcloud.labx21.userservice.controller; import cn.iocoder.springcloud.labx21.userservice.dto.UserAddDTO; import cn.iocoder.springcloud.labx21.userservice.dto.UserDTO; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/user") public class UserController { @GetMapping("/get") public UserDTO get(@RequestParam("id") Integer id) { return new UserDTO().setId(id) .setName("没有昵称:" + id) .setGender(id % 2 + 1); // 1 - 男;2 - 女 } @PostMapping("/add") public Integer add(UserAddDTO addDTO) { return (int) (System.currentTimeMillis() / 1000); // 嘿嘿,随便返回一个 id } @GetMapping("/sleep") public void sleep() throws InterruptedException { Thread.sleep(10 * 1000L); } } ================================================ FILE: labx-21/labx-21-sc-user-service/src/main/java/cn/iocoder/springcloud/labx21/userservice/dto/UserAddDTO.java ================================================ package cn.iocoder.springcloud.labx21.userservice.dto; import java.io.Serializable; /** * 用户添加 DTO */ public class UserAddDTO implements Serializable { /** * 昵称 */ private String name; /** * 性别 */ private Integer gender; public String getName() { return name; } public UserAddDTO setName(String name) { this.name = name; return this; } public Integer getGender() { return gender; } public UserAddDTO setGender(Integer gender) { this.gender = gender; return this; } } ================================================ FILE: labx-21/labx-21-sc-user-service/src/main/java/cn/iocoder/springcloud/labx21/userservice/dto/UserDTO.java ================================================ package cn.iocoder.springcloud.labx21.userservice.dto; import java.io.Serializable; /** * 用户信息 DTO */ public class UserDTO implements Serializable { /** * 用户编号 */ private Integer id; /** * 昵称 */ private String name; /** * 性别 */ private Integer gender; public Integer getId() { return id; } public UserDTO setId(Integer id) { this.id = id; return this; } public String getName() { return name; } public UserDTO setName(String name) { this.name = name; return this; } public Integer getGender() { return gender; } public UserDTO setGender(Integer gender) { this.gender = gender; return this; } } ================================================ FILE: labx-21/labx-21-sc-user-service/src/main/resources/application.yaml ================================================ spring: application: name: user-service # Spring 应用名 cloud: nacos: # Nacos 作为注册中心的配置项 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 server: port: ${random.int[10000,19999]} # 服务器端口。默认为 8080 # port: 18080 # 服务器端口。默认为 8080 ================================================ FILE: labx-21/labx-21-sc-zuul-demo01/pom.xml ================================================ labx-21 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-21-sc-zuul-demo01 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.cloud spring-cloud-starter-netflix-zuul ================================================ FILE: labx-21/labx-21-sc-zuul-demo01/src/main/java/cn/iocoder/springcloud/labx21/zuuldemo/ZuulApplication.java ================================================ package cn.iocoder.springcloud.labx21.zuuldemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.zuul.EnableZuulProxy; @SpringBootApplication @EnableZuulProxy // 开启 Zuul 网关 public class ZuulApplication { public static void main(String[] args) { SpringApplication.run(ZuulApplication.class, args); } } ================================================ FILE: labx-21/labx-21-sc-zuul-demo01/src/main/resources/application.yaml ================================================ server: port: 8888 spring: application: name: zuul-application # Zuul 配置项,对应 ZuulProperties 配置类 zuul: servlet-path: / # ZuulServlet 匹配的路径,默认为 /zuul # 路由配置项,对应 ZuulRoute Map routes: route_yudaoyuanma: path: /blog/** url: http://www.iocoder.cn route_oschina: path: /oschina/** url: https://www.oschina.net ================================================ FILE: labx-21/labx-21-sc-zuul-demo02-registry/pom.xml ================================================ labx-21 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-21-sc-zuul-demo02-registry 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.cloud spring-cloud-starter-netflix-zuul com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery ================================================ FILE: labx-21/labx-21-sc-zuul-demo02-registry/src/main/java/cn/iocoder/springcloud/labx21/zuuldemo/ZuulApplication.java ================================================ package cn.iocoder.springcloud.labx21.zuuldemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.zuul.EnableZuulProxy; @SpringBootApplication @EnableZuulProxy // 开启 Zuul 网关 public class ZuulApplication { public static void main(String[] args) { SpringApplication.run(ZuulApplication.class, args); } } ================================================ FILE: labx-21/labx-21-sc-zuul-demo02-registry/src/main/resources/application.yaml ================================================ server: port: 8888 spring: application: name: zuul-application cloud: nacos: # Nacos 作为注册中心的配置项 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 # Zuul 配置项,对应 ZuulProperties 配置类 zuul: servlet-path: / # ZuulServlet 匹配的路径,默认为 /zuul # 路由配置项,对应 ZuulRoute Map routes: route_yudaoyuanma: path: /blog/** url: http://www.iocoder.cn route_oschina: path: /oschina/** url: https://www.oschina.net route_users: path: /users/** service-id: user-service ================================================ FILE: labx-21/labx-21-sc-zuul-demo03-config-apollo/pom.xml ================================================ labx-21 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-21-sc-zuul-demo03-config-apollo 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.cloud spring-cloud-starter-netflix-zuul com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery com.ctrip.framework.apollo apollo-client 1.5.1 ================================================ FILE: labx-21/labx-21-sc-zuul-demo03-config-apollo/src/main/java/cn/iocoder/springcloud/labx21/zuuldemo/ZuulApplication.java ================================================ package cn.iocoder.springcloud.labx21.zuuldemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.zuul.EnableZuulProxy; @SpringBootApplication @EnableZuulProxy // 开启 Zuul 网关 public class ZuulApplication { public static void main(String[] args) { SpringApplication.run(ZuulApplication.class, args); } } ================================================ FILE: labx-21/labx-21-sc-zuul-demo03-config-apollo/src/main/java/cn/iocoder/springcloud/labx21/zuuldemo/ZuulPropertiesRefresher.java ================================================ package cn.iocoder.springcloud.labx21.zuuldemo; import com.ctrip.framework.apollo.model.ConfigChangeEvent; import com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.context.environment.EnvironmentChangeEvent; import org.springframework.cloud.netflix.zuul.RoutesRefreshedEvent; import org.springframework.cloud.netflix.zuul.filters.RouteLocator; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; /** * 由 https://github.com/ctripcorp/apollo-use-cases/blob/master/spring-cloud-zuul 提供代码,感谢~ */ @Component public class ZuulPropertiesRefresher { private static final Logger logger = LoggerFactory.getLogger(ZuulPropertiesRefresher.class); @Autowired private ApplicationContext applicationContext; @Autowired private RouteLocator routeLocator; @ApolloConfigChangeListener(interestedKeyPrefixes = "zuul.") public void onChange(ConfigChangeEvent changeEvent) { refreshZuulProperties(changeEvent); } private void refreshZuulProperties(ConfigChangeEvent changeEvent) { logger.info("Refreshing zuul properties!"); /* * rebind configuration beans, e.g. ZuulProperties * @see org.springframework.cloud.context.properties.ConfigurationPropertiesRebinder#onApplicationEvent */ this.applicationContext.publishEvent(new EnvironmentChangeEvent(changeEvent.changedKeys())); /* * refresh routes * @see org.springframework.cloud.netflix.zuul.ZuulServerAutoConfiguration.ZuulRefreshListener#onApplicationEvent */ this.applicationContext.publishEvent(new RoutesRefreshedEvent(routeLocator)); logger.info("Zuul properties refreshed!"); } } ================================================ FILE: labx-21/labx-21-sc-zuul-demo03-config-apollo/src/main/resources/application.yaml ================================================ server: port: 8888 spring: application: name: zuul-application cloud: nacos: # Nacos 作为注册中心的配置项 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 # Apollo 相关配置项 app: id: ${spring.application.name} # 使用的 Apollo 的项目(应用)编号 apollo: meta: http://127.0.0.1:8080 # Apollo Meta Server 地址 bootstrap: enabled: true # 是否开启 Apollo 配置预加载功能。默认为 false。 eagerLoad: enable: true # 是否开启 Apollo 支持日志级别的加载时机。默认为 false。 namespaces: application # 使用的 Apollo 的命名空间,默认为 application。 ================================================ FILE: labx-21/labx-21-sc-zuul-demo03-config-nacos/pom.xml ================================================ labx-21 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-21-sc-zuul-demo03-config-nacos 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.cloud spring-cloud-starter-netflix-zuul com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery com.alibaba.cloud spring-cloud-starter-alibaba-nacos-config ================================================ FILE: labx-21/labx-21-sc-zuul-demo03-config-nacos/src/main/java/cn/iocoder/springcloud/labx21/zuuldemo/ZuulApplication.java ================================================ package cn.iocoder.springcloud.labx21.zuuldemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.zuul.EnableZuulProxy; @SpringBootApplication @EnableZuulProxy // 开启 Zuul 网关 public class ZuulApplication { public static void main(String[] args) { SpringApplication.run(ZuulApplication.class, args); } } ================================================ FILE: labx-21/labx-21-sc-zuul-demo03-config-nacos/src/main/java/cn/iocoder/springcloud/labx21/zuuldemo/ZuulRouteRefreshListener.java ================================================ package cn.iocoder.springcloud.labx21.zuuldemo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.context.environment.EnvironmentChangeEvent; import org.springframework.cloud.netflix.zuul.RoutesRefreshedEvent; import org.springframework.cloud.netflix.zuul.filters.RouteLocator; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; @Component public class ZuulRouteRefreshListener implements ApplicationListener { private static final Logger logger = LoggerFactory.getLogger(ZuulRouteRefreshListener.class); @Autowired private ApplicationEventPublisher publisher; @Autowired private RouteLocator routeLocator; @Override public void onApplicationEvent(EnvironmentChangeEvent event) { // 判断是否有 `zuul.` 配置变化 boolean zuulConfigUpdated = false; for (String key : event.getKeys()) { if (key.startsWith("zuul.")) { zuulConfigUpdated = true; break; } } if (!zuulConfigUpdated) { return; } // 发布 RoutesRefreshedEvent 事件 this.publisher.publishEvent(new RoutesRefreshedEvent(routeLocator)); logger.info("发布 RoutesRefreshedEvent 事件完成,刷新 Zuul 路由"); } } ================================================ FILE: labx-21/labx-21-sc-zuul-demo03-config-nacos/src/main/resources/application.yaml ================================================ server: port: 8888 spring: cloud: nacos: # Nacos 作为注册中心的配置项 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 ================================================ FILE: labx-21/labx-21-sc-zuul-demo03-config-nacos/src/main/resources/bootstrap.yaml ================================================ spring: application: name: zuul-application cloud: nacos: # Nacos Config 配置项,对应 NacosConfigProperties 配置属性类 config: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 namespace: # 使用的 Nacos 的命名空间,默认为 null group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP name: # 使用的 Nacos 配置集的 dataId,默认为 spring.application.name file-extension: yaml # 使用的 Nacos 配置集的 dataId 的文件拓展名,同时也是 Nacos 配置集的配置格式,默认为 properties ================================================ FILE: labx-21/labx-21-sc-zuul-demo05-custom-zuul-filter/pom.xml ================================================ labx-21 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-21-sc-zuul-demo05-custom-zuul-filter 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.cloud spring-cloud-starter-netflix-zuul ================================================ FILE: labx-21/labx-21-sc-zuul-demo05-custom-zuul-filter/src/main/java/cn/iocoder/springcloud/labx21/zuuldemo/ZuulApplication.java ================================================ package cn.iocoder.springcloud.labx21.zuuldemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.zuul.EnableZuulProxy; @SpringBootApplication @EnableZuulProxy // 开启 Zuul 网关 public class ZuulApplication { public static void main(String[] args) { SpringApplication.run(ZuulApplication.class, args); } } ================================================ FILE: labx-21/labx-21-sc-zuul-demo05-custom-zuul-filter/src/main/java/cn/iocoder/springcloud/labx21/zuuldemo/filter/AuthZuulFilter.java ================================================ package cn.iocoder.springcloud.labx21.zuuldemo.filter; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import com.netflix.zuul.exception.ZuulException; import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import javax.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.Map; @Component public class AuthZuulFilter extends ZuulFilter { /** * 外部请求 Header - token 认证令牌 */ private static final String DEFAULT_TOKEN_HEADER_NAME = "token"; /** * 转发请求 Header - userId 用户编号 */ private static final String DEFAULT_HEADER_NAME = "user-id"; /** * token 和 userId 的映射 */ private static Map TOKENS = new HashMap(); static { TOKENS.put("yunai", 1); } public String filterType() { return FilterConstants.PRE_TYPE; // 前置过滤器 } public int filterOrder() { return 0; } public boolean shouldFilter() { return true; // 需要过滤 } public Object run() throws ZuulException { // 获取当前请求上下文 RequestContext ctx = RequestContext.getCurrentContext(); // 获得 token HttpServletRequest request = ctx.getRequest(); String token = request.getHeader(DEFAULT_TOKEN_HEADER_NAME); // 如果没有 token,则不进行认证。因为可能是无需认证的 API 接口 if (!StringUtils.hasText(token)) { return null; } // 进行认证 Integer userId = TOKENS.get(token); // 通过 token 获取不到 userId,说明认证不通过 if (userId == null) { ctx.setSendZuulResponse(false); ctx.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value()); // 响应 401 状态码 return null; } // 认证通过,将 userId 添加到 Header 中 ctx.getZuulRequestHeaders().put(DEFAULT_HEADER_NAME, String.valueOf(userId)); return null; } } ================================================ FILE: labx-21/labx-21-sc-zuul-demo05-custom-zuul-filter/src/main/resources/application.yaml ================================================ server: port: 8888 spring: application: name: zuul-application # Zuul 配置项,对应 ZuulProperties 配置类 zuul: servlet-path: / # ZuulServlet 匹配的路径,默认为 /zuul # 路由配置项,对应 ZuulRoute Map routes: route_yudaoyuanma: path: /blog/** url: http://www.iocoder.cn route_oschina: path: /oschina/** url: https://www.oschina.net ================================================ FILE: labx-21/labx-21-sc-zuul-demo07-hystrix/pom.xml ================================================ labx-21 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-21-sc-zuul-demo07-hystrix 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.cloud spring-cloud-starter-netflix-zuul com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery ================================================ FILE: labx-21/labx-21-sc-zuul-demo07-hystrix/src/main/java/cn/iocoder/springcloud/labx21/zuuldemo/ZuulApplication.java ================================================ package cn.iocoder.springcloud.labx21.zuuldemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.zuul.EnableZuulProxy; @SpringBootApplication @EnableZuulProxy // 开启 Zuul 网关 public class ZuulApplication { public static void main(String[] args) { SpringApplication.run(ZuulApplication.class, args); } } ================================================ FILE: labx-21/labx-21-sc-zuul-demo07-hystrix/src/main/java/cn/iocoder/springcloud/labx21/zuuldemo/fallback/ApiFallbackProvider.java ================================================ package cn.iocoder.springcloud.labx21.zuuldemo.fallback; import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.client.ClientHttpResponse; import org.springframework.stereotype.Component; import java.io.ByteArrayInputStream; import java.io.InputStream; @Component public class ApiFallbackProvider implements FallbackProvider { public String getRoute() { return "*"; } public ClientHttpResponse fallbackResponse(String route, final Throwable cause) { return new ClientHttpResponse() { public HttpStatus getStatusCode() { return HttpStatus.OK; } public int getRawStatusCode() { return HttpStatus.OK.value(); } public String getStatusText() { return HttpStatus.OK.getReasonPhrase(); } public void close() {} public InputStream getBody() { // 响应内容 String bodyText = String.format("{\"code\": 500,\"message\": \"Service unavailable:%s\"}", cause.getMessage()); return new ByteArrayInputStream(bodyText.getBytes()); } public HttpHeaders getHeaders() { // 响应头 HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); // json 返回 return headers; } }; } } ================================================ FILE: labx-21/labx-21-sc-zuul-demo07-hystrix/src/main/resources/application.yaml ================================================ server: port: 8888 spring: application: name: zuul-application cloud: nacos: # Nacos 作为注册中心的配置项 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 # Zuul 配置项,对应 ZuulProperties 配置类 zuul: servlet-path: / # ZuulServlet 匹配的路径,默认为 /zuul # 路由配置项,对应 ZuulRoute Map routes: route_yudaoyuanma: path: /blog/** url: http://www.iocoder.cn route_oschina: path: /oschina/** url: https://www.oschina.net route_users: path: /users/** service-id: user-service ================================================ FILE: labx-21/labx-21-sc-zuul-demo07-sentinel/pom.xml ================================================ labx-21 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-21-sc-zuul-demo07-sentinel 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.cloud spring-cloud-starter-netflix-zuul com.alibaba.cloud spring-cloud-starter-alibaba-sentinel com.alibaba.cloud spring-cloud-alibaba-sentinel-gateway ================================================ FILE: labx-21/labx-21-sc-zuul-demo07-sentinel/src/main/java/cn/iocoder/springcloud/labx21/zuuldemo/CustomBlockFallbackProvider.java ================================================ package cn.iocoder.springcloud.labx21.zuuldemo; import com.alibaba.csp.sentinel.adapter.gateway.zuul.fallback.BlockResponse; import com.alibaba.csp.sentinel.adapter.gateway.zuul.fallback.ZuulBlockFallbackManager; import com.alibaba.csp.sentinel.adapter.gateway.zuul.fallback.ZuulBlockFallbackProvider; import com.alibaba.csp.sentinel.slots.block.BlockException; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; @Component public class CustomBlockFallbackProvider implements ZuulBlockFallbackProvider { @PostConstruct public void init() { ZuulBlockFallbackManager.registerProvider(this); // 注册到 ZuulBlockFallbackManager } public String getRoute() { return "*"; } public BlockResponse fallbackResponse(String route, Throwable cause) { if (cause instanceof BlockException) { return new BlockResponse(429, "你被 Block 啦!", route); } else { return new BlockResponse(500, "系统异常", route); } } } ================================================ FILE: labx-21/labx-21-sc-zuul-demo07-sentinel/src/main/java/cn/iocoder/springcloud/labx21/zuuldemo/ZuulApplication.java ================================================ package cn.iocoder.springcloud.labx21.zuuldemo; import com.alibaba.cloud.sentinel.gateway.ConfigConstants; import com.alibaba.csp.sentinel.config.SentinelConfig; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.zuul.EnableZuulProxy; @SpringBootApplication @EnableZuulProxy // 开启 Zuul 网关 public class ZuulApplication { public static void main(String[] args) { System.setProperty(SentinelConfig.APP_TYPE, ConfigConstants.APP_TYPE_ZUUL_GATEWAY); // 【重点】设置应用类型为 Zuul SpringApplication.run(ZuulApplication.class, args); } } ================================================ FILE: labx-21/labx-21-sc-zuul-demo07-sentinel/src/main/resources/application.yaml ================================================ server: port: 8888 spring: application: name: zuul-application cloud: nacos: # Nacos 作为注册中心的配置项 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 sentinel: eager: true # 是否饥饿加载。默认为 false 关闭 transport: dashboard: localhost:7070 # 是否饥饿加载。 # # 数据源的配置项 # datasource: # ds1.file: # file: "classpath: sentinel-gw-flow.json" # ruleType: gw-flow # ds2.file: # file: "classpath: sentinel-gw-api-group.json" # ruleType: gw-api-group # Sentinel 对 Zuul 的专属配置项,对应 SentinelZuulProperties 类 zuul: order: pre: 10000 # 前置过滤器 SentinelZuulPreFilter 的顺序 post: 1000 # 后置过滤器 SentinelZuulPostFilter 的顺序 error: -1 # 错误过滤器 SentinelZuulErrorFilter 的顺序 # Zuul 配置项,对应 ZuulProperties 配置类 zuul: servlet-path: / # ZuulServlet 匹配的路径,默认为 /zuul # 路由配置项,对应 ZuulRoute Map routes: yudaoyuanma: # 这是一个 Route 编号 path: /** url: http://www.iocoder.cn ================================================ FILE: labx-21/labx-21-sc-zuul-demo07-sentinel/src/main/resources/sentinel-gw-api-group.json ================================================ [ { "apiName": "yudaoyuanma_customized_api", "predicateItems": [ { "pattern": "/categories/**", "matchStrategy": 1 }, { "items": [ { "pattern": "/Dubbo/good-collection/", "matchStrategy": 0 }, { "pattern": "/SkyWalking/**", "matchStrategy": 1 } ] } ] } ] ================================================ FILE: labx-21/labx-21-sc-zuul-demo07-sentinel/src/main/resources/sentinel-gw-flow.json ================================================ [ { "resource": "yudaoyuanma", "count": 3 }, { "resource": "yudaoyuanma_customized_api", "count": 1 } ] ================================================ FILE: labx-21/labx-21-sc-zuul-demo09-actuator/pom.xml ================================================ labx-21 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-21-sc-zuul-demo09-actuator 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.springframework.cloud spring-cloud-starter-netflix-zuul ================================================ FILE: labx-21/labx-21-sc-zuul-demo09-actuator/src/main/java/cn/iocoder/springcloud/labx21/zuuldemo/ZuulApplication.java ================================================ package cn.iocoder.springcloud.labx21.zuuldemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.zuul.EnableZuulProxy; @SpringBootApplication @EnableZuulProxy // 开启 Zuul 网关 public class ZuulApplication { public static void main(String[] args) { SpringApplication.run(ZuulApplication.class, args); } } ================================================ FILE: labx-21/labx-21-sc-zuul-demo09-actuator/src/main/resources/application.yaml ================================================ server: port: 8888 spring: application: name: zuul-application # Zuul 配置项,对应 ZuulProperties 配置类 zuul: servlet-path: / # ZuulServlet 匹配的路径,默认为 /zuul # 路由配置项,对应 ZuulRoute Map routes: route_yudaoyuanma: path: /blog/** url: http://www.iocoder.cn route_oschina: path: /oschina/** url: https://www.oschina.net management: endpoints: web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 endpoint: # Health 端点配置项,对应 HealthProperties 配置类 health: enabled: true # 是否开启。默认为 true 开启。 show-details: ALWAYS # 何时显示完整的健康信息。默认为 NEVER 都不展示。可选 WHEN_AUTHORIZED 当经过授权的用户;可选 ALWAYS 总是展示。 server: port: 18888 # 单独设置端口,因为 8888 端口全部给 Zuul 了 ================================================ FILE: labx-21/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-21 pom labx-21-sc-zuul-demo01 labx-21-sc-zuul-demo02-registry labx-21-sc-zuul-demo03-config-apollo labx-21-sc-zuul-demo03-config-nacos labx-21-sc-zuul-demo05-custom-zuul-filter labx-21-sc-zuul-demo07-hystrix labx-21-sc-zuul-demo07-sentinel labx-21-sc-zuul-demo09-actuator labx-21-sc-user-service ================================================ FILE: labx-21/《芋道 Spring Cloud 网关 Zuul 入门》.md ================================================ ================================================ FILE: labx-22/labx-22-scn-eureka-demo01-consumer/pom.xml ================================================ labx-22 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-22-scn-eureka-demo01-consumer 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-netflix-eureka-client ================================================ FILE: labx-22/labx-22-scn-eureka-demo01-consumer/src/main/java/cn/iocoder/springcloudalibaba/labx22/consumerdemo/DemoConsumerApplication.java ================================================ package cn.iocoder.springcloudalibaba.labx22.consumerdemo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import java.util.List; @SpringBootApplication // @EnableDiscoveryClient public class DemoConsumerApplication { public static void main(String[] args) { SpringApplication.run(DemoConsumerApplication.class, args); } @Configuration public class RestTemplateConfiguration { @Bean public RestTemplate restTemplate() { return new RestTemplate(); } } @RestController static class TestController { @Autowired private DiscoveryClient discoveryClient; @Autowired private RestTemplate restTemplate; @Autowired private LoadBalancerClient loadBalancerClient; @GetMapping("/hello") public String hello(String name) { // 获得服务 `demo-provider` 的一个实例 ServiceInstance instance; if (true) { // 获取服务 `demo-provider` 对应的实例列表 List instances = discoveryClient.getInstances("demo-provider"); // 选择第一个 instance = instances.size() > 0 ? instances.get(0) : null; } else { instance = loadBalancerClient.choose("demo-provider"); } // 发起调用 if (instance == null) { throw new IllegalStateException("获取不到实例"); } String targetUrl = instance.getUri() + "/echo?name=" + name; String response = restTemplate.getForObject(targetUrl, String.class); // 返回结果 return "consumer:" + response; } } } ================================================ FILE: labx-22/labx-22-scn-eureka-demo01-consumer/src/main/resources/application.yaml ================================================ spring: application: name: demo-consumer # Spring 应用名 server: port: 28080 # 服务器端口。默认为 8080 eureka: client: register-with-eureka: true fetch-registry: true service-url: defaultZone: http://127.0.0.1:8761/eureka/ ================================================ FILE: labx-22/labx-22-scn-eureka-demo01-provider/pom.xml ================================================ labx-22 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-22-scn-eureka-demo01-provider 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-netflix-eureka-client ================================================ FILE: labx-22/labx-22-scn-eureka-demo01-provider/src/main/java/cn/iocoder/springcloudalibaba/labx22/providerdemo/DemoProviderApplication.java ================================================ package cn.iocoder.springcloudalibaba.labx22.providerdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @SpringBootApplication @EnableDiscoveryClient public class DemoProviderApplication { public static void main(String[] args) { SpringApplication.run(DemoProviderApplication.class, args); } @RestController static class TestController { @GetMapping("/echo") public String echo(String name) { return "provider:" + name; } } } ================================================ FILE: labx-22/labx-22-scn-eureka-demo01-provider/src/main/resources/application.yaml ================================================ spring: application: name: demo-provider # Spring 应用名 server: port: 18080 # 服务器端口。默认为 8080 eureka: client: register-with-eureka: true # 注册到 Eureka-Server,默认为 true fetch-registry: true # 从 Eureka-Server 获取注册表,默认为 true service-url: defaultZone: http://127.0.0.1:8761/eureka/ # Eureka-Server 地址 ================================================ FILE: labx-22/labx-22-scn-eureka-demo02-consumer/pom.xml ================================================ labx-22 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-22-scn-eureka-demo02-consumer 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-netflix-eureka-client ================================================ FILE: labx-22/labx-22-scn-eureka-demo02-consumer/src/main/java/cn/iocoder/springcloudalibaba/labx22/consumerdemo/DemoConsumerApplication.java ================================================ package cn.iocoder.springcloudalibaba.labx22.consumerdemo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import java.util.List; @SpringBootApplication // @EnableDiscoveryClient public class DemoConsumerApplication { public static void main(String[] args) { SpringApplication.run(DemoConsumerApplication.class, args); } @Configuration public class RestTemplateConfiguration { @Bean public RestTemplate restTemplate() { return new RestTemplate(); } } @RestController static class TestController { @Autowired private DiscoveryClient discoveryClient; @Autowired private RestTemplate restTemplate; @Autowired private LoadBalancerClient loadBalancerClient; @GetMapping("/hello") public String hello(String name) { // 获得服务 `demo-provider` 的一个实例 ServiceInstance instance; if (true) { // 获取服务 `demo-provider` 对应的实例列表 List instances = discoveryClient.getInstances("demo-provider"); // 选择第一个 instance = instances.size() > 0 ? instances.get(0) : null; } else { instance = loadBalancerClient.choose("demo-provider"); } // 发起调用 if (instance == null) { throw new IllegalStateException("获取不到实例"); } String targetUrl = instance.getUri() + "/echo?name=" + name; String response = restTemplate.getForObject(targetUrl, String.class); // 返回结果 return "consumer:" + response; } } } ================================================ FILE: labx-22/labx-22-scn-eureka-demo02-consumer/src/main/resources/application.yaml ================================================ spring: application: name: demo-consumer # Spring 应用名 server: port: 28080 # 服务器端口。默认为 8080 eureka: client: register-with-eureka: true # 注册到 Eureka-Server,默认为 true fetch-registry: true # 从 Eureka-Server 获取注册表,默认为 true service-url: defaultZone: http://eureka-node01:18761/eureka/, http://eureka-node02:28761/eureka/ ================================================ FILE: labx-22/labx-22-scn-eureka-demo02-provider/pom.xml ================================================ labx-22 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-22-scn-eureka-demo02-provider 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-netflix-eureka-client ================================================ FILE: labx-22/labx-22-scn-eureka-demo02-provider/src/main/java/cn/iocoder/springcloudalibaba/labx22/providerdemo/DemoProviderApplication.java ================================================ package cn.iocoder.springcloudalibaba.labx22.providerdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @SpringBootApplication @EnableDiscoveryClient public class DemoProviderApplication { public static void main(String[] args) { SpringApplication.run(DemoProviderApplication.class, args); } @RestController static class TestController { @GetMapping("/echo") public String echo(String name) { return "provider:" + name; } } } ================================================ FILE: labx-22/labx-22-scn-eureka-demo02-provider/src/main/resources/application.yaml ================================================ spring: application: name: demo-provider # Spring 应用名 server: port: 18080 # 服务器端口。默认为 8080 eureka: client: register-with-eureka: true # 注册到 Eureka-Server,默认为 true fetch-registry: true # 从 Eureka-Server 获取注册表,默认为 true service-url: defaultZone: http://eureka-node01:18761/eureka/, http://eureka-node02:28761/eureka/ ================================================ FILE: labx-22/labx-22-scn-eureka-demo03-consumer/pom.xml ================================================ labx-22 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-22-scn-eureka-demo03-consumer 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-netflix-eureka-client ================================================ FILE: labx-22/labx-22-scn-eureka-demo03-consumer/src/main/java/cn/iocoder/springcloudalibaba/labx22/consumerdemo/DemoConsumerApplication.java ================================================ package cn.iocoder.springcloudalibaba.labx22.consumerdemo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import java.util.List; @SpringBootApplication // @EnableDiscoveryClient public class DemoConsumerApplication { public static void main(String[] args) { SpringApplication.run(DemoConsumerApplication.class, args); } @Configuration public class RestTemplateConfiguration { @Bean public RestTemplate restTemplate() { return new RestTemplate(); } } @RestController static class TestController { @Autowired private DiscoveryClient discoveryClient; @Autowired private RestTemplate restTemplate; @Autowired private LoadBalancerClient loadBalancerClient; @GetMapping("/hello") public String hello(String name) { // 获得服务 `demo-provider` 的一个实例 ServiceInstance instance; if (true) { // 获取服务 `demo-provider` 对应的实例列表 List instances = discoveryClient.getInstances("demo-provider"); // 选择第一个 instance = instances.size() > 0 ? instances.get(0) : null; } else { instance = loadBalancerClient.choose("demo-provider"); } // 发起调用 if (instance == null) { throw new IllegalStateException("获取不到实例"); } String targetUrl = instance.getUri() + "/echo?name=" + name; String response = restTemplate.getForObject(targetUrl, String.class); // 返回结果 return "consumer:" + response; } } } ================================================ FILE: labx-22/labx-22-scn-eureka-demo03-consumer/src/main/resources/application.yaml ================================================ spring: application: name: demo-consumer # Spring 应用名 server: port: 28080 # 服务器端口。默认为 8080 eureka: client: register-with-eureka: true fetch-registry: true service-url: defaultZone: http://eureka:woshimima@127.0.0.1:8761/eureka/ # Eureka-Server 地址 ================================================ FILE: labx-22/labx-22-scn-eureka-demo03-provider/pom.xml ================================================ labx-22 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-22-scn-eureka-demo03-provider 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-netflix-eureka-client ================================================ FILE: labx-22/labx-22-scn-eureka-demo03-provider/src/main/java/cn/iocoder/springcloudalibaba/labx22/providerdemo/DemoProviderApplication.java ================================================ package cn.iocoder.springcloudalibaba.labx22.providerdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @SpringBootApplication @EnableDiscoveryClient public class DemoProviderApplication { public static void main(String[] args) { SpringApplication.run(DemoProviderApplication.class, args); } @RestController static class TestController { @GetMapping("/echo") public String echo(String name) { return "provider:" + name; } } } ================================================ FILE: labx-22/labx-22-scn-eureka-demo03-provider/src/main/resources/application.yaml ================================================ spring: application: name: demo-provider # Spring 应用名 server: port: 18080 # 服务器端口。默认为 8080 eureka: client: register-with-eureka: true # 注册到 Eureka-Server,默认为 true fetch-registry: true # 从 Eureka-Server 获取注册表,默认为 true service-url: defaultZone: http://eureka:woshimima@127.0.0.1:8761/eureka/ # Eureka-Server 地址 ================================================ FILE: labx-22/labx-22-scn-eureka-server-cluster/pom.xml ================================================ labx-22 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-22-scn-eureka-server-cluster 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.cloud spring-cloud-starter-netflix-eureka-server ================================================ FILE: labx-22/labx-22-scn-eureka-server-cluster/src/main/java/cn/iocoder/springcloudalibaba/labx22/eurekaserverdemo/EurekaServerApplication.java ================================================ package cn.iocoder.springcloudalibaba.labx22.eurekaserverdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; @SpringBootApplication @EnableEurekaServer public class EurekaServerApplication { public static void main(String[] args) { SpringApplication.run(EurekaServerApplication.class,args); } } ================================================ FILE: labx-22/labx-22-scn-eureka-server-cluster/src/main/resources/application-node01.yaml ================================================ server: port: 18761 spring: application: name: eureka-server eureka: instance: hostname: eureka-node01 client: register-with-eureka: true fetch-registry: true service-url: defaultZone: http://eureka-node02:28761/eureka/ ================================================ FILE: labx-22/labx-22-scn-eureka-server-cluster/src/main/resources/application-node02.yaml ================================================ server: port: 28761 spring: application: name: eureka-server eureka: instance: hostname: eureka-node02 client: register-with-eureka: true fetch-registry: true service-url: defaultZone: http://eureka-node01:18761/eureka/ ================================================ FILE: labx-22/labx-22-scn-eureka-server-security/pom.xml ================================================ labx-22 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-22-scn-eureka-server-security 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.cloud spring-cloud-starter-netflix-eureka-server org.springframework.boot spring-boot-starter-security ================================================ FILE: labx-22/labx-22-scn-eureka-server-security/src/main/java/cn/iocoder/springcloudalibaba/labx22/eurekaserverdemo/EurekaServerApplication.java ================================================ package cn.iocoder.springcloudalibaba.labx22.eurekaserverdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; @SpringBootApplication @EnableEurekaServer public class EurekaServerApplication { public static void main(String[] args) { SpringApplication.run(EurekaServerApplication.class,args); } } ================================================ FILE: labx-22/labx-22-scn-eureka-server-security/src/main/java/cn/iocoder/springcloudalibaba/labx22/eurekaserverdemo/config/WebSecurityConfig.java ================================================ package cn.iocoder.springcloudalibaba.labx22.eurekaserverdemo.config; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().ignoringAntMatchers("/eureka/**"); super.configure(http); } } ================================================ FILE: labx-22/labx-22-scn-eureka-server-security/src/main/resources/application.yaml ================================================ server: port: 8761 # 设置 Eureka-Server 的端口 spring: application: name: eureka-server # 使用 Spring Security 创建默认认证账号 security: user: name: eureka password: woshimima eureka: client: register-with-eureka: false # 不注册到 Eureka-Server,默认为 true fetch-registry: false # 不从 Eureka-Server 获取注册表,默认为 true ================================================ FILE: labx-22/labx-22-scn-eureka-server-standalone/pom.xml ================================================ labx-22 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-22-scn-eureka-server-standalone 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.cloud spring-cloud-starter-netflix-eureka-server ================================================ FILE: labx-22/labx-22-scn-eureka-server-standalone/src/main/java/cn/iocoder/springcloudalibaba/labx22/eurekaserverdemo/EurekaServerApplication.java ================================================ package cn.iocoder.springcloudalibaba.labx22.eurekaserverdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; @SpringBootApplication @EnableEurekaServer public class EurekaServerApplication { public static void main(String[] args) { SpringApplication.run(EurekaServerApplication.class,args); } } ================================================ FILE: labx-22/labx-22-scn-eureka-server-standalone/src/main/resources/application.yaml ================================================ server: port: 8761 # 设置 Eureka-Server 的端口 spring: application: name: eureka-server eureka: client: register-with-eureka: false # 不注册到 Eureka-Server,默认为 true fetch-registry: false # 不从 Eureka-Server 获取注册表,默认为 true ================================================ FILE: labx-22/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-22 pom labx-22-scn-eureka-server-standalone labx-22-scn-eureka-demo01-provider labx-22-scn-eureka-demo01-consumer labx-22-scn-eureka-server-cluster labx-22-scn-eureka-demo02-provider labx-22-scn-eureka-demo02-consumer labx-22-scn-eureka-server-security labx-22-scn-eureka-demo03-provider labx-22-scn-eureka-demo03-consumer ================================================ FILE: labx-22/《芋道 Spring Cloud Netflix 注册中心 Eureka 入门》.md ================================================ ================================================ FILE: labx-23/labx-23-scn-hystrix-actuator/pom.xml ================================================ labx-23 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-23-scn-hystrix-actuator 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-netflix-hystrix org.springframework.boot spring-boot-starter-actuator ================================================ FILE: labx-23/labx-23-scn-hystrix-actuator/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/DemoApplication.java ================================================ package cn.iocoder.springcloud.labx23.hystrixdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @SpringBootApplication @EnableCircuitBreaker // 声明开启断路器 public class DemoApplication { @Bean public RestTemplate restTemplate() { return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } ================================================ FILE: labx-23/labx-23-scn-hystrix-actuator/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/controller/CacheDemoController.java ================================================ package cn.iocoder.springcloud.labx23.hystrixdemo.controller; import cn.iocoder.springcloud.labx23.hystrixdemo.service.CacheDemoService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/cache-demo") public class CacheDemoController { @Autowired private CacheDemoService cacheDemoService; @GetMapping("/get_user") public String getUser(@RequestParam("id") Integer id) { String userA = cacheDemoService.getUser(id); String userB = cacheDemoService.getUser(id); String userC = cacheDemoService.getUser(id); return userC; } @GetMapping("/update_user") public String updateUser(@RequestParam("id") Integer id) { String userA = cacheDemoService.getUser(id); cacheDemoService.updateUser(id); String userC = cacheDemoService.getUser(id); return userC; } } ================================================ FILE: labx-23/labx-23-scn-hystrix-actuator/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/controller/CollapserDemoController.java ================================================ package cn.iocoder.springcloud.labx23.hystrixdemo.controller; import cn.iocoder.springcloud.labx23.hystrixdemo.service.CollapserDemoService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; @RestController @RequestMapping("/collapser-demo") public class CollapserDemoController { private Logger logger = LoggerFactory.getLogger(CollapserDemoController.class); @Autowired private CollapserDemoService collapserDemoService; @GetMapping("/test") public void test() throws ExecutionException, InterruptedException { logger.info("[test][准备获取用户信息]"); Future user01 = collapserDemoService.getUserFuture(1); Future user02 = collapserDemoService.getUserFuture(2); logger.info("[test][提交获取用户信息]"); logger.info("[test][user({}) 的结果为({})]", 1, user01.get()); logger.info("[test][user({}) 的结果为({})]", 2, user02.get()); } @GetMapping("/test_02") public void test02() throws ExecutionException, InterruptedException { logger.info("[test][准备获取用户信息]"); Future user01 = collapserDemoService.getUserFuture(2); Future user02 = collapserDemoService.getUserFuture(1); logger.info("[test][提交获取用户信息]"); logger.info("[test][user({}) 的结果为({})]", 1, user01.get()); logger.info("[test][user({}) 的结果为({})]", 2, user02.get()); } } ================================================ FILE: labx-23/labx-23-scn-hystrix-actuator/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/controller/DemoController.java ================================================ package cn.iocoder.springcloud.labx23.hystrixdemo.controller; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import org.apache.commons.lang.exception.ExceptionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @RestController @RequestMapping("/demo") public class DemoController { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private RestTemplate restTemplate; @GetMapping("/get_user") @HystrixCommand(fallbackMethod = "getUserFallback") public String getUser(@RequestParam("id") Integer id) { logger.info("[getUser][准备调用 user-service 获取用户({})详情]", id); return restTemplate.getForEntity("http://127.0.0.1:18080/user/get?id=" + id, String.class).getBody(); } public String getUserFallback(Integer id, Throwable throwable) { logger.info("[getUserFallback][id({}) exception({})]", id, ExceptionUtils.getRootCauseMessage(throwable)); return "mock:User:" + id; } } ================================================ FILE: labx-23/labx-23-scn-hystrix-actuator/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/filter/HystrixRequestContextFilter.java ================================================ package cn.iocoder.springcloud.labx23.hystrixdemo.filter; import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext; import org.springframework.stereotype.Component; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import java.io.IOException; @Component @WebFilter(urlPatterns = "/") public class HystrixRequestContextFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // 初始化 HystrixRequestContext HystrixRequestContext context = HystrixRequestContext.initializeContext(); // 继续过滤器 try { chain.doFilter(request, response); } finally { // 销毁 HystrixRequestContext context.close(); } } } ================================================ FILE: labx-23/labx-23-scn-hystrix-actuator/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/service/CacheDemoService.java ================================================ package cn.iocoder.springcloud.labx23.hystrixdemo.service; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheRemove; import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheResult; import org.apache.commons.lang.exception.ExceptionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; @Service public class CacheDemoService { private Logger logger = LoggerFactory.getLogger(CacheDemoService.class); @Autowired private RestTemplate restTemplate; @HystrixCommand(fallbackMethod = "getUserFallback") @CacheResult(cacheKeyMethod = "genGetUserCacheKey") public String getUser(Integer id) { logger.info("[getUser][准备调用 user-service 获取用户({})详情]", id); return restTemplate.getForEntity("http://127.0.0.1:18080/user/get?id=" + id, String.class).getBody(); } @HystrixCommand @CacheRemove(commandKey = "getUser", cacheKeyMethod = "genGetUserCacheKey") public void updateUser(Integer id) { logger.info("[updateUser][更新用户({})详情]", id); } public String getUserFallback(Integer id, Throwable throwable) { logger.info("[getUserFallback][id({}) exception({})]", id, ExceptionUtils.getRootCauseMessage(throwable)); return "mock:User:" + id; } public String genGetUserCacheKey(Integer id) { return "USER_" + id; } } ================================================ FILE: labx-23/labx-23-scn-hystrix-actuator/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/service/CollapserDemoService.java ================================================ package cn.iocoder.springcloud.labx23.hystrixdemo.service; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.concurrent.Future; @Service public class CollapserDemoService { private Logger logger = LoggerFactory.getLogger(CollapserDemoService.class); @Autowired private RestTemplate restTemplate; @HystrixCollapser( batchMethod = "getUsers", collapserProperties = { @HystrixProperty(name = "timerDelayInMilliseconds", value = "10000") // 演示,所以设置的时间较长 } ) public Future getUserFuture(Integer id) { throw new RuntimeException("This method body should not be executed"); } @HystrixCommand public List getUsers(List ids) { logger.info("[getUsers][准备调用 user-service 获取多个用户({})详情]", ids); String[] users = restTemplate.getForEntity("http://127.0.0.1:18080/user/batch_get?ids=" + StringUtils.join(ids, ',') , String[].class).getBody(); return users == null || users.length == 0 ? Collections.emptyList() : Arrays.asList(users); } } ================================================ FILE: labx-23/labx-23-scn-hystrix-actuator/src/main/resources/application.yml ================================================ management: endpoints: web: exposure: include: 'hystrix.stream' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 ================================================ FILE: labx-23/labx-23-scn-hystrix-dashboard/pom.xml ================================================ labx-23 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-23-scn-hystrix-dashboard 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-netflix-hystrix-dashboard ================================================ FILE: labx-23/labx-23-scn-hystrix-dashboard/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/HystrixDashboardApplication.java ================================================ package cn.iocoder.springcloud.labx23.hystrixdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard; @SpringBootApplication @EnableHystrixDashboard // 声明开启 Hystrix Dashboard 功能 public class HystrixDashboardApplication { public static void main(String[] args) { SpringApplication.run(HystrixDashboardApplication.class, args); } } ================================================ FILE: labx-23/labx-23-scn-hystrix-dashboard/src/main/resources/application.yml ================================================ server: port: 9090 ================================================ FILE: labx-23/labx-23-scn-hystrix-dashboard-turbine/pom.xml ================================================ labx-23 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-23-scn-hystrix-dashboard-turbine 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-netflix-hystrix-dashboard org.springframework.cloud spring-cloud-starter-netflix-turbine org.springframework.cloud spring-cloud-starter-netflix-eureka-client ================================================ FILE: labx-23/labx-23-scn-hystrix-dashboard-turbine/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/HystrixDashboardApplication.java ================================================ package cn.iocoder.springcloud.labx23.hystrixdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard; import org.springframework.cloud.netflix.turbine.EnableTurbine; @SpringBootApplication @EnableHystrixDashboard // 声明开启 Hystrix Dashboard 功能 @EnableTurbine // 声明开启 Turbine 功能 public class HystrixDashboardApplication { public static void main(String[] args) { SpringApplication.run(HystrixDashboardApplication.class, args); } } ================================================ FILE: labx-23/labx-23-scn-hystrix-dashboard-turbine/src/main/resources/application.yml ================================================ server: port: 9090 spring: application: name: hystrix-dashboard eureka: client: service-url: defaultZone: http://127.0.0.1:8761/eureka/ # Eureka-Server 地址 # Turbine 配置项,对应 TurbineProperties 配置类 turbine: app-config: hystrix-demo # 配置需要 Turbine 聚合的服务名;如果有多个,使用逗号分隔。 combine-host-port: true # 服务是否以 host + port 进行区分,默认为 true。如果设置为 false,则只以 host 进行区分,这样会导致相同主机部署了相同服务的多个实例,会被认为是一个 cluster-name-expression: new String('default') # 指定集群名,设置为 `default` 表示默认集群。 ================================================ FILE: labx-23/labx-23-scn-hystrix-demo01/pom.xml ================================================ labx-23 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-23-scn-hystrix-demo01 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-netflix-hystrix ================================================ FILE: labx-23/labx-23-scn-hystrix-demo01/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/DemoApplication.java ================================================ package cn.iocoder.springcloud.labx23.hystrixdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @SpringBootApplication @EnableCircuitBreaker // 声明开启断路器 public class DemoApplication { @Bean public RestTemplate restTemplate() { return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } ================================================ FILE: labx-23/labx-23-scn-hystrix-demo01/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/controller/CacheDemoController.java ================================================ package cn.iocoder.springcloud.labx23.hystrixdemo.controller; import cn.iocoder.springcloud.labx23.hystrixdemo.service.CacheDemoService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/cache-demo") public class CacheDemoController { @Autowired private CacheDemoService cacheDemoService; @GetMapping("/get_user") public String getUser(@RequestParam("id") Integer id) { String userA = cacheDemoService.getUser(id); String userB = cacheDemoService.getUser(id); String userC = cacheDemoService.getUser(id); return userC; } @GetMapping("/update_user") public String updateUser(@RequestParam("id") Integer id) { String userA = cacheDemoService.getUser(id); cacheDemoService.updateUser(id); String userC = cacheDemoService.getUser(id); return userC; } } ================================================ FILE: labx-23/labx-23-scn-hystrix-demo01/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/controller/CollapserDemoController.java ================================================ package cn.iocoder.springcloud.labx23.hystrixdemo.controller; import cn.iocoder.springcloud.labx23.hystrixdemo.service.CollapserDemoService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; @RestController @RequestMapping("/collapser-demo") public class CollapserDemoController { private Logger logger = LoggerFactory.getLogger(CollapserDemoController.class); @Autowired private CollapserDemoService collapserDemoService; @GetMapping("/test") public void test() throws ExecutionException, InterruptedException { logger.info("[test][准备获取用户信息]"); Future user01 = collapserDemoService.getUserFuture(1); Future user02 = collapserDemoService.getUserFuture(2); logger.info("[test][提交获取用户信息]"); logger.info("[test][user({}) 的结果为({})]", 1, user01.get()); logger.info("[test][user({}) 的结果为({})]", 2, user02.get()); } @GetMapping("/test_02") public void test02() throws ExecutionException, InterruptedException { logger.info("[test][准备获取用户信息]"); Future user01 = collapserDemoService.getUserFuture(2); Future user02 = collapserDemoService.getUserFuture(1); logger.info("[test][提交获取用户信息]"); logger.info("[test][user({}) 的结果为({})]", 1, user01.get()); logger.info("[test][user({}) 的结果为({})]", 2, user02.get()); } } ================================================ FILE: labx-23/labx-23-scn-hystrix-demo01/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/controller/DemoController.java ================================================ package cn.iocoder.springcloud.labx23.hystrixdemo.controller; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import org.apache.commons.lang.exception.ExceptionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @RestController @RequestMapping("/demo") public class DemoController { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private RestTemplate restTemplate; @GetMapping("/get_user") @HystrixCommand(fallbackMethod = "getUserFallback") public String getUser(@RequestParam("id") Integer id) { logger.info("[getUser][准备调用 user-service 获取用户({})详情]", id); return restTemplate.getForEntity("http://127.0.0.1:18080/user/get?id=" + id, String.class).getBody(); } public String getUserFallback(Integer id, Throwable throwable) { logger.info("[getUserFallback][id({}) exception({})]", id, ExceptionUtils.getRootCauseMessage(throwable)); return "mock:User:" + id; } } ================================================ FILE: labx-23/labx-23-scn-hystrix-demo01/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/filter/HystrixRequestContextFilter.java ================================================ package cn.iocoder.springcloud.labx23.hystrixdemo.filter; import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext; import org.springframework.stereotype.Component; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import java.io.IOException; @Component @WebFilter(urlPatterns = "/") public class HystrixRequestContextFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // 初始化 HystrixRequestContext HystrixRequestContext context = HystrixRequestContext.initializeContext(); // 继续过滤器 try { chain.doFilter(request, response); } finally { // 销毁 HystrixRequestContext context.close(); } } } ================================================ FILE: labx-23/labx-23-scn-hystrix-demo01/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/service/CacheDemoService.java ================================================ package cn.iocoder.springcloud.labx23.hystrixdemo.service; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheRemove; import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheResult; import org.apache.commons.lang.exception.ExceptionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; @Service public class CacheDemoService { private Logger logger = LoggerFactory.getLogger(CacheDemoService.class); @Autowired private RestTemplate restTemplate; @HystrixCommand(fallbackMethod = "getUserFallback") @CacheResult(cacheKeyMethod = "genGetUserCacheKey") public String getUser(Integer id) { logger.info("[getUser][准备调用 user-service 获取用户({})详情]", id); return restTemplate.getForEntity("http://127.0.0.1:18080/user/get?id=" + id, String.class).getBody(); } @HystrixCommand @CacheRemove(commandKey = "getUser", cacheKeyMethod = "genGetUserCacheKey") public void updateUser(Integer id) { logger.info("[updateUser][更新用户({})详情]", id); } public String getUserFallback(Integer id, Throwable throwable) { logger.info("[getUserFallback][id({}) exception({})]", id, ExceptionUtils.getRootCauseMessage(throwable)); return "mock:User:" + id; } public String genGetUserCacheKey(Integer id) { return "USER_" + id; } } ================================================ FILE: labx-23/labx-23-scn-hystrix-demo01/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/service/CollapserDemoService.java ================================================ package cn.iocoder.springcloud.labx23.hystrixdemo.service; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.concurrent.Future; @Service public class CollapserDemoService { private Logger logger = LoggerFactory.getLogger(CollapserDemoService.class); @Autowired private RestTemplate restTemplate; @HystrixCollapser( batchMethod = "getUsers", collapserProperties = { @HystrixProperty(name = "timerDelayInMilliseconds", value = "10000") // 演示,所以设置的时间较长 } ) public Future getUserFuture(Integer id) { throw new RuntimeException("This method body should not be executed"); } @HystrixCommand public List getUsers(List ids) { logger.info("[getUsers][准备调用 user-service 获取多个用户({})详情]", ids); String[] users = restTemplate.getForEntity("http://127.0.0.1:18080/user/batch_get?ids=" + StringUtils.join(ids, ',') , String[].class).getBody(); return users == null || users.length == 0 ? Collections.emptyList() : Arrays.asList(users); } } ================================================ FILE: labx-23/labx-23-scn-hystrix-demo01-cluster/pom.xml ================================================ labx-23 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-23-scn-hystrix-demo01-cluster 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-netflix-hystrix org.springframework.boot spring-boot-starter-actuator org.springframework.cloud spring-cloud-starter-netflix-eureka-client ================================================ FILE: labx-23/labx-23-scn-hystrix-demo01-cluster/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/DemoApplication.java ================================================ package cn.iocoder.springcloud.labx23.hystrixdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @SpringBootApplication @EnableCircuitBreaker // 声明开启断路器 public class DemoApplication { @Bean public RestTemplate restTemplate() { return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } ================================================ FILE: labx-23/labx-23-scn-hystrix-demo01-cluster/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/controller/CacheDemoController.java ================================================ package cn.iocoder.springcloud.labx23.hystrixdemo.controller; import cn.iocoder.springcloud.labx23.hystrixdemo.service.CacheDemoService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/cache-demo") public class CacheDemoController { @Autowired private CacheDemoService cacheDemoService; @GetMapping("/get_user") public String getUser(@RequestParam("id") Integer id) { String userA = cacheDemoService.getUser(id); String userB = cacheDemoService.getUser(id); String userC = cacheDemoService.getUser(id); return userC; } @GetMapping("/update_user") public String updateUser(@RequestParam("id") Integer id) { String userA = cacheDemoService.getUser(id); cacheDemoService.updateUser(id); String userC = cacheDemoService.getUser(id); return userC; } } ================================================ FILE: labx-23/labx-23-scn-hystrix-demo01-cluster/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/controller/CollapserDemoController.java ================================================ package cn.iocoder.springcloud.labx23.hystrixdemo.controller; import cn.iocoder.springcloud.labx23.hystrixdemo.service.CollapserDemoService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; @RestController @RequestMapping("/collapser-demo") public class CollapserDemoController { private Logger logger = LoggerFactory.getLogger(CollapserDemoController.class); @Autowired private CollapserDemoService collapserDemoService; @GetMapping("/test") public void test() throws ExecutionException, InterruptedException { logger.info("[test][准备获取用户信息]"); Future user01 = collapserDemoService.getUserFuture(1); Future user02 = collapserDemoService.getUserFuture(2); logger.info("[test][提交获取用户信息]"); logger.info("[test][user({}) 的结果为({})]", 1, user01.get()); logger.info("[test][user({}) 的结果为({})]", 2, user02.get()); } @GetMapping("/test_02") public void test02() throws ExecutionException, InterruptedException { logger.info("[test][准备获取用户信息]"); Future user01 = collapserDemoService.getUserFuture(2); Future user02 = collapserDemoService.getUserFuture(1); logger.info("[test][提交获取用户信息]"); logger.info("[test][user({}) 的结果为({})]", 1, user01.get()); logger.info("[test][user({}) 的结果为({})]", 2, user02.get()); } } ================================================ FILE: labx-23/labx-23-scn-hystrix-demo01-cluster/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/controller/DemoController.java ================================================ package cn.iocoder.springcloud.labx23.hystrixdemo.controller; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import org.apache.commons.lang.exception.ExceptionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @RestController @RequestMapping("/demo") public class DemoController { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private RestTemplate restTemplate; @GetMapping("/get_user") @HystrixCommand(fallbackMethod = "getUserFallback") public String getUser(@RequestParam("id") Integer id) { logger.info("[getUser][准备调用 user-service 获取用户({})详情]", id); return restTemplate.getForEntity("http://127.0.0.1:18080/user/get?id=" + id, String.class).getBody(); } public String getUserFallback(Integer id, Throwable throwable) { logger.info("[getUserFallback][id({}) exception({})]", id, ExceptionUtils.getRootCauseMessage(throwable)); return "mock:User:" + id; } } ================================================ FILE: labx-23/labx-23-scn-hystrix-demo01-cluster/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/filter/HystrixRequestContextFilter.java ================================================ package cn.iocoder.springcloud.labx23.hystrixdemo.filter; import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext; import org.springframework.stereotype.Component; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import java.io.IOException; @Component @WebFilter(urlPatterns = "/") public class HystrixRequestContextFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // 初始化 HystrixRequestContext HystrixRequestContext context = HystrixRequestContext.initializeContext(); // 继续过滤器 try { chain.doFilter(request, response); } finally { // 销毁 HystrixRequestContext context.close(); } } } ================================================ FILE: labx-23/labx-23-scn-hystrix-demo01-cluster/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/service/CacheDemoService.java ================================================ package cn.iocoder.springcloud.labx23.hystrixdemo.service; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheRemove; import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheResult; import org.apache.commons.lang.exception.ExceptionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; @Service public class CacheDemoService { private Logger logger = LoggerFactory.getLogger(CacheDemoService.class); @Autowired private RestTemplate restTemplate; @HystrixCommand(fallbackMethod = "getUserFallback") @CacheResult(cacheKeyMethod = "genGetUserCacheKey") public String getUser(Integer id) { logger.info("[getUser][准备调用 user-service 获取用户({})详情]", id); return restTemplate.getForEntity("http://127.0.0.1:18080/user/get?id=" + id, String.class).getBody(); } @HystrixCommand @CacheRemove(commandKey = "getUser", cacheKeyMethod = "genGetUserCacheKey") public void updateUser(Integer id) { logger.info("[updateUser][更新用户({})详情]", id); } public String getUserFallback(Integer id, Throwable throwable) { logger.info("[getUserFallback][id({}) exception({})]", id, ExceptionUtils.getRootCauseMessage(throwable)); return "mock:User:" + id; } public String genGetUserCacheKey(Integer id) { return "USER_" + id; } } ================================================ FILE: labx-23/labx-23-scn-hystrix-demo01-cluster/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/service/CollapserDemoService.java ================================================ package cn.iocoder.springcloud.labx23.hystrixdemo.service; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.concurrent.Future; @Service public class CollapserDemoService { private Logger logger = LoggerFactory.getLogger(CollapserDemoService.class); @Autowired private RestTemplate restTemplate; @HystrixCollapser( batchMethod = "getUsers", collapserProperties = { @HystrixProperty(name = "timerDelayInMilliseconds", value = "10000") // 演示,所以设置的时间较长 } ) public Future getUserFuture(Integer id) { throw new RuntimeException("This method body should not be executed"); } @HystrixCommand public List getUsers(List ids) { logger.info("[getUsers][准备调用 user-service 获取多个用户({})详情]", ids); String[] users = restTemplate.getForEntity("http://127.0.0.1:18080/user/batch_get?ids=" + StringUtils.join(ids, ',') , String[].class).getBody(); return users == null || users.length == 0 ? Collections.emptyList() : Arrays.asList(users); } } ================================================ FILE: labx-23/labx-23-scn-hystrix-demo01-cluster/src/main/resources/application.yml ================================================ management: endpoints: web: exposure: include: 'hystrix.stream' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 spring: application: name: hystrix-demo # 应用名 eureka: client: service-url: defaultZone: http://127.0.0.1:8761/eureka/ # Eureka-Server 地址 ================================================ FILE: labx-23/labx-23-scn-hystrix-dubbo-demo/labx-23-scn-hystrix-dubbo-demo-application/pom.xml ================================================ labx-23-scn-hystrix-dubbo-demo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-23-scn-hystrix-dubbo-demo-application 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import cn.iocoder.springboot.labs labx-23-scn-hystrix-dubbo-demo-user-service-api 1.0-SNAPSHOT org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery com.alibaba.cloud spring-cloud-starter-dubbo org.springframework.cloud spring-cloud-starter-netflix-hystrix ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx23.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; @SpringBootApplication @EnableCircuitBreaker // 声明开启断路器 public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx23.demo.controller; import cn.iocoder.springcloud.labx23.userservice.api.UserService; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import org.apache.commons.lang.exception.ExceptionUtils; import org.apache.dubbo.config.annotation.Reference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { private Logger logger = LoggerFactory.getLogger(getClass()); @Reference(protocol = "dubbo", version = "1.0.0") private UserService userService; @GetMapping("/get_user") @HystrixCommand(fallbackMethod = "getUserFallback") public String getUser(@RequestParam("id") Integer id) { logger.info("[getUser][准备调用 user-service 获取用户({})详情]", id); return userService.getUser(id); } public String getUserFallback(Integer id, Throwable throwable) { logger.info("[getUserFallback][id({}) exception({})]", id, ExceptionUtils.getRootCauseMessage(throwable)); return "mock:User:" + id; } } ================================================ FILE: labx-23/labx-23-scn-hystrix-dubbo-demo/labx-23-scn-hystrix-dubbo-demo-application/src/main/resources/application.yaml ================================================ spring: application: name: demo-consumer cloud: # Nacos 作为注册中心的配置项 nacos: discovery: server-addr: 127.0.0.1:8848 # Dubbo 配置项,对应 DubboConfigurationProperties 类 dubbo: # Dubbo 服务注册中心配置,对应 RegistryConfig 类 registry: address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址 # Spring Cloud Alibaba Dubbo 专属配置项,对应 DubboCloudProperties 类 cloud: subscribed-services: user-service # 设置订阅的应用列表,默认为 * 订阅所有应用。 ================================================ FILE: labx-23/labx-23-scn-hystrix-dubbo-demo/labx-23-scn-hystrix-dubbo-demo-user-service/pom.xml ================================================ labx-23-scn-hystrix-dubbo-demo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-23-scn-hystrix-dubbo-demo-user-service 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import cn.iocoder.springboot.labs labx-23-scn-hystrix-dubbo-demo-user-service-api 1.0-SNAPSHOT com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery com.alibaba.cloud spring-cloud-starter-dubbo ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx23.userservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class UserServiceApplication { public static void main(String[] args) { SpringApplication.run(UserServiceApplication.class); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx23.userservice.service; import cn.iocoder.springcloud.labx23.userservice.api.UserService; @org.apache.dubbo.config.annotation.Service(protocol = "dubbo", version = "1.0.0") public class UserServiceImpl implements UserService { @Override public String getUser(Integer id) { return "User:" + id; } } ================================================ FILE: labx-23/labx-23-scn-hystrix-dubbo-demo/labx-23-scn-hystrix-dubbo-demo-user-service/src/main/resources/application.yaml ================================================ spring: application: name: user-service cloud: # Nacos 作为注册中心的配置项 nacos: discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 # Dubbo 配置项,对应 DubboConfigurationProperties 类 dubbo: scan: base-packages: cn.iocoder.springcloud.labx23.userservice.service # 指定 Dubbo 服务实现类的扫描基准包 # Dubbo 服务暴露的协议配置,对应 ProtocolConfig Map protocols: dubbo: name: dubbo # 协议名称 port: -1 # 协议端口,-1 表示自增端口,从 20880 开始 # Dubbo 服务注册中心配置,对应 RegistryConfig 类 registry: address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址 # Spring Cloud Alibaba Dubbo 专属配置项,对应 DubboCloudProperties 类 cloud: subscribed-services: '' # 设置订阅的应用列表,默认为 * 订阅所有应用。 ================================================ FILE: labx-23/labx-23-scn-hystrix-dubbo-demo/labx-23-scn-hystrix-dubbo-demo-user-service-api/pom.xml ================================================ labx-23-scn-hystrix-dubbo-demo cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-23-scn-hystrix-dubbo-demo-user-service-api ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx23.userservice.api; public interface UserService { String getUser(Integer id); } ================================================ FILE: labx-23/labx-23-scn-hystrix-dubbo-demo/pom.xml ================================================ labx-23 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-23-scn-hystrix-dubbo-demo pom labx-23-scn-hystrix-dubbo-demo-user-service labx-23-scn-hystrix-dubbo-demo-user-service-api labx-23-scn-hystrix-dubbo-demo-application ================================================ FILE: labx-23/labx-23-scn-hystrix-feign/pom.xml ================================================ labx-23 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-23-scn-hystrix-feign 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-netflix-hystrix org.springframework.cloud spring-cloud-starter-openfeign ================================================ FILE: labx-23/labx-23-scn-hystrix-feign/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/DemoApplication.java ================================================ package cn.iocoder.springcloud.labx23.hystrixdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @SpringBootApplication @EnableCircuitBreaker // 声明开启断路器 @EnableFeignClients // 开启 Feign Client 功能 public class DemoApplication { @Bean public RestTemplate restTemplate() { return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } ================================================ FILE: labx-23/labx-23-scn-hystrix-feign/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/controller/FeignDemoController.java ================================================ package cn.iocoder.springcloud.labx23.hystrixdemo.controller; import cn.iocoder.springcloud.labx23.hystrixdemo.feign.UserServiceFeignClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/feign-demo") public class FeignDemoController { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private UserServiceFeignClient userServiceFeignClient; @GetMapping("/get_user") public String getUser(@RequestParam("id") Integer id) { logger.info("[getUser][准备调用 user-service 获取用户({})详情]", id); return userServiceFeignClient.getUser(id); } } ================================================ FILE: labx-23/labx-23-scn-hystrix-feign/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/fallback/UserServiceFeignClientFallbackFactory.java ================================================ package cn.iocoder.springcloud.labx23.hystrixdemo.fallback; import cn.iocoder.springcloud.labx23.hystrixdemo.feign.UserServiceFeignClient; import feign.hystrix.FallbackFactory; import org.apache.commons.lang.exception.ExceptionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component public class UserServiceFeignClientFallbackFactory implements FallbackFactory { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public UserServiceFeignClient create(Throwable cause) { return new UserServiceFeignClient() { @Override public String getUser(Integer id) { logger.info("[getUserFallback][id({}) exception({})]", id, ExceptionUtils.getRootCauseMessage(cause)); return "mock:User:" + id; } }; } } ================================================ FILE: labx-23/labx-23-scn-hystrix-feign/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/feign/UserServiceFeignClient.java ================================================ package cn.iocoder.springcloud.labx23.hystrixdemo.feign; import cn.iocoder.springcloud.labx23.hystrixdemo.fallback.UserServiceFeignClientFallbackFactory; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; @FeignClient(name = "user-service", url = "http://127.0.0.1:18080", fallbackFactory = UserServiceFeignClientFallbackFactory.class) public interface UserServiceFeignClient { @GetMapping("/user/get") String getUser(@RequestParam("id") Integer id); } ================================================ FILE: labx-23/labx-23-scn-hystrix-feign/src/main/resources/application.yaml ================================================ feign: hystrix: enabled: true # 开启 Hystrix 对 Feign 的支持,默认为 false 关闭。 ================================================ FILE: labx-23/labx-23-user-service/pom.xml ================================================ labx-23 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-23-user-service 1.8 1.8 2.2.4.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.boot spring-boot-starter-web ================================================ FILE: labx-23/labx-23-user-service/src/main/java/cn/iocoder/springcloud/labx23/userservice/UserServiceApplication.java ================================================ package cn.iocoder.springcloud.labx23.userservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.List; import java.util.stream.Collectors; @SpringBootApplication public class UserServiceApplication { @RestController @RequestMapping("/user") public class UserController { @GetMapping("/get") public String get(@RequestParam("id") Integer id) { return "User:" + id; } @GetMapping("/batch_get") public List batchGet(@RequestParam("ids") List ids) { return ids.stream().map(id -> "User:" + id).collect(Collectors.toList()); } } public static void main(String[] args) { // 设置端口 System.setProperty("server.port", "18080"); // 应用启动 SpringApplication.run(UserServiceApplication.class, args); } } ================================================ FILE: labx-23/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-23 pom labx-23-user-service labx-23-scn-hystrix-demo01 labx-23-scn-hystrix-actuator labx-23-scn-hystrix-dashboard labx-23-scn-hystrix-demo01-cluster labx-23-scn-hystrix-dashboard-turbine labx-23-scn-hystrix-feign labx-23-scn-hystrix-dubbo-demo ================================================ FILE: labx-23/《芋道 Spring Cloud Netflix 服务容错 Hystrix 入门》.md ================================================ ================================================ FILE: labx-24/labx-24-resilience4j-demo01/pom.xml ================================================ lab-59 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-24-resilience4j-demo01 1.8 1.8 2.2.4.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.boot spring-boot-starter-web io.github.resilience4j resilience4j-spring-cloud2 1.4.0 org.aspectj aspectjrt 1.9.5 org.aspectj aspectjweaver 1.9.5 ================================================ FILE: labx-24/labx-24-resilience4j-demo01/src/main/java/cn/iocoder/springcloud/labx24/resilience4jdemo/DemoApplication.java ================================================ package cn.iocoder.springcloud.labx24.resilience4jdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @SpringBootApplication public class DemoApplication { @Bean public RestTemplate restTemplate() { return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } ================================================ FILE: labx-24/labx-24-resilience4j-demo01/src/main/java/cn/iocoder/springcloud/labx24/resilience4jdemo/controller/BulkheadDemoController.java ================================================ package cn.iocoder.springcloud.labx24.resilience4jdemo.controller; import io.github.resilience4j.bulkhead.annotation.Bulkhead; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/bulkhead-demo") public class BulkheadDemoController { private Logger logger = LoggerFactory.getLogger(getClass()); @GetMapping("/get_user") @Bulkhead(name = "backendC", fallbackMethod = "getUserFallback", type = Bulkhead.Type.SEMAPHORE) public String getUser(@RequestParam("id") Integer id) throws InterruptedException { logger.info("[getUser][id({})]", id); Thread.sleep(10 * 1000L); // sleep 10 秒 return "User:" + id; } public String getUserFallback(Integer id, Throwable throwable) { logger.info("[getUserFallback][id({}) exception({})]", id, throwable.getClass().getSimpleName()); return "mock:User:" + id; } } ================================================ FILE: labx-24/labx-24-resilience4j-demo01/src/main/java/cn/iocoder/springcloud/labx24/resilience4jdemo/controller/DemoController.java ================================================ package cn.iocoder.springcloud.labx24.resilience4jdemo.controller; import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @RestController @RequestMapping("/demo") public class DemoController { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private RestTemplate restTemplate; @GetMapping("/get_user") @CircuitBreaker(name = "backendA", fallbackMethod = "getUserFallback") public String getUser(@RequestParam("id") Integer id) { logger.info("[getUser][准备调用 user-service 获取用户({})详情]", id); return restTemplate.getForEntity("http://127.0.0.1:18080/user/get?id=" + id, String.class).getBody(); } public String getUserFallback(Integer id, Throwable throwable) { logger.info("[getUserFallback][id({}) exception({})]", id, throwable.getClass().getSimpleName()); return "mock:User:" + id; } } ================================================ FILE: labx-24/labx-24-resilience4j-demo01/src/main/java/cn/iocoder/springcloud/labx24/resilience4jdemo/controller/RateLimiterDemoController.java ================================================ package cn.iocoder.springcloud.labx24.resilience4jdemo.controller; import io.github.resilience4j.ratelimiter.annotation.RateLimiter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/rate-limiter-demo") public class RateLimiterDemoController { private Logger logger = LoggerFactory.getLogger(getClass()); @GetMapping("/get_user") @RateLimiter(name = "backendB", fallbackMethod = "getUserFallback") public String getUser(@RequestParam("id") Integer id) { return "User:" + id; } public String getUserFallback(Integer id, Throwable throwable) { logger.info("[getUserFallback][id({}) exception({})]", id, throwable.getClass().getSimpleName()); return "mock:User:" + id; } } ================================================ FILE: labx-24/labx-24-resilience4j-demo01/src/main/java/cn/iocoder/springcloud/labx24/resilience4jdemo/controller/RetryDemoController.java ================================================ package cn.iocoder.springcloud.labx24.resilience4jdemo.controller; import io.github.resilience4j.retry.annotation.Retry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @RestController @RequestMapping("/retry-demo") public class RetryDemoController { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private RestTemplate restTemplate; @GetMapping("/get_user") @Retry(name = "backendE", fallbackMethod = "getUserFallback") public String getUser(@RequestParam("id") Integer id) { logger.info("[getUser][准备调用 user-service 获取用户({})详情]", id); return restTemplate.getForEntity("http://127.0.0.1:18080/user/get?id=" + id, String.class).getBody(); } public String getUserFallback(Integer id, Throwable throwable) { logger.info("[getUserFallback][id({}) exception({})]", id, throwable.getClass().getSimpleName()); return "mock:User:" + id; } } ================================================ FILE: labx-24/labx-24-resilience4j-demo01/src/main/java/cn/iocoder/springcloud/labx24/resilience4jdemo/controller/ThreadPoolBulkheadDemoController.java ================================================ package cn.iocoder.springcloud.labx24.resilience4jdemo.controller; import io.github.resilience4j.bulkhead.annotation.Bulkhead; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @RestController @RequestMapping("/thread-pool-bulkhead-demo") public class ThreadPoolBulkheadDemoController { @Autowired private ThreadPoolBulkheadService threadPoolBulkheadService; @GetMapping("/get_user") public String getUser(@RequestParam("id") Integer id) throws ExecutionException, InterruptedException { threadPoolBulkheadService.getUser0(id); return threadPoolBulkheadService.getUser0(id).get(); } @Service public static class ThreadPoolBulkheadService { private Logger logger = LoggerFactory.getLogger(ThreadPoolBulkheadService.class); @Bulkhead(name = "backendD", fallbackMethod = "getUserFallback", type = Bulkhead.Type.THREADPOOL) public CompletableFuture getUser0(Integer id) throws InterruptedException { logger.info("[getUser][id({})]", id); Thread.sleep(10 * 1000L); // sleep 10 秒 return CompletableFuture.completedFuture("User:" + id); } public CompletableFuture getUserFallback(Integer id, Throwable throwable) { logger.info("[getUserFallback][id({}) exception({})]", id, throwable.getClass().getSimpleName()); return CompletableFuture.completedFuture("mock:User:" + id); } } } ================================================ FILE: labx-24/labx-24-resilience4j-demo01/src/main/java/cn/iocoder/springcloud/labx24/resilience4jdemo/controller/TimeLimiterDemoController.java ================================================ package cn.iocoder.springcloud.labx24.resilience4jdemo.controller; import io.github.resilience4j.bulkhead.annotation.Bulkhead; import io.github.resilience4j.timelimiter.annotation.TimeLimiter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @RestController @RequestMapping("/time-limiter-demo") public class TimeLimiterDemoController { @Autowired private TimeLimiterService timeLimiterService; @GetMapping("/get_user") public String getUser(@RequestParam("id") Integer id) throws ExecutionException, InterruptedException { return timeLimiterService.getUser0(id).get(); } @Service public static class TimeLimiterService { private Logger logger = LoggerFactory.getLogger(TimeLimiterService.class); @Bulkhead(name = "backendD", type = Bulkhead.Type.THREADPOOL) @TimeLimiter(name = "backendF", fallbackMethod = "getUserFallback") public CompletableFuture getUser0(Integer id) throws InterruptedException { logger.info("[getUser][id({})]", id); Thread.sleep(10 * 1000L); // sleep 10 秒 return CompletableFuture.completedFuture("User:" + id); } public CompletableFuture getUserFallback(Integer id, Throwable throwable) { logger.info("[getUserFallback][id({}) exception({})]", id, throwable.getClass().getSimpleName()); return CompletableFuture.completedFuture("mock:User:" + id); } } } ================================================ FILE: labx-24/labx-24-resilience4j-demo01/src/main/resources/application.yml ================================================ resilience4j: # Resilience4j 的断路器配置项,对应 CircuitBreakerProperties 属性类 circuitbreaker: instances: backendA: failure-rate-threshold: 50 # 熔断器关闭状态和半开状态使用的同一个失败率阈值,单位:百分比。默认为 50 ring-buffer-size-in-closed-state: 5 # 熔断器关闭状态的缓冲区大小,不会限制线程的并发量,在熔断器发生状态转换前所有请求都会调用后端服务。默认为 100 ring-buffer-size-in-half-open-state: 5 # 熔断器半开状态的缓冲区大小,会限制线程的并发量。例如,缓冲区为 10 则每次只会允许 10 个请求调用后端服务。默认为 10 wait-duration-in-open-state : 5000 # 熔断器从打开状态转变为半开状态等待的时间,单位:微秒 automatic-transition-from-open-to-half-open-enabled: true # 如果置为 true,当等待时间结束会自动由打开变为半开;若置为 false,则需要一个请求进入来触发熔断器状态转换。默认为 true register-health-indicator: true # 是否注册到健康监测 # Resilience4j 的限流器配置项,对应 RateLimiterProperties 属性类 ratelimiter: instances: backendB: limit-for-period: 1 # 每个周期内,允许的请求数。默认为 50 limit-refresh-period: 10s # 每个周期的时长,单位:微秒。默认为 500 timeout-duration: 5s # 被限流时,阻塞等待的时长,单位:微秒。默认为 5s register-health-indicator: true # 是否注册到健康监测 # Resilience4j 的信号量 Bulkhead 配置项,对应 BulkheadConfigurationProperties 属性类 bulkhead: instances: backendC: max-concurrent-calls: 1 # 并发调用数。默认为 25 max-wait-duration: 5s # 并发调用到达上限时,阻塞等待的时长,单位:微秒。默认为 0 # Resilience4j 的线程池 Bulkhead 配置项,对应 ThreadPoolBulkheadProperties 属性类 thread-pool-bulkhead: instances: backendD: max-thread-pool-size: 1 # 线程池的最大大小。默认为 Runtime.getRuntime().availableProcessors() core-thread-pool-size: 1 # 线程池的核心大小。默认为 Runtime.getRuntime().availableProcessors() - 1 queue-capacity: 200 # 线程池的队列大小。默认为 100 keep-alive-duration: 100s # 超过核心大小的线程,空闲存活时间。默认为 20 毫秒 # Resilience4j 的重试 Retry 配置项,对应 RetryProperties 属性类 retry: instances: backendE: max-retry-Attempts: 3 # 最大重试次数。默认为 3 wait-duration: 5s # 下次重试的间隔,单位:微秒。默认为 500 毫秒 retry-exceptions: # 需要重试的异常列表。默认为空 ingore-exceptions: # 需要忽略的异常列表。默认为空 # Resilience4j 的超时限制器 TimeLimiter 配置项,对应 TimeLimiterProperties 属性类 timelimiter: instances: backendF: timeout-duration: 1s # 等待超时时间,单位:微秒。默认为 1 秒 cancel-running-future: true # 当等待超时时,是否关闭取消线程。默认为 true ================================================ FILE: labx-24/labx-24-resilience4j-demo02/pom.xml ================================================ lab-59 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-24-resilience4j-demo02 1.8 1.8 2.2.4.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-circuitbreaker-resilience4j 1.0.2.RELEASE ================================================ FILE: labx-24/labx-24-resilience4j-demo02/src/main/java/cn/iocoder/springcloud/labx24/resilience4jdemo/DemoApplication.java ================================================ package cn.iocoder.springcloud.labx24.resilience4jdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @SpringBootApplication public class DemoApplication { @Bean public RestTemplate restTemplate() { return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } ================================================ FILE: labx-24/labx-24-resilience4j-demo02/src/main/java/cn/iocoder/springcloud/labx24/resilience4jdemo/config/Resilience4jConfig.java ================================================ package cn.iocoder.springcloud.labx24.resilience4jdemo.config; import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig; import io.github.resilience4j.timelimiter.TimeLimiterConfig; import org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JCircuitBreakerFactory; import org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JConfigBuilder; import org.springframework.cloud.client.circuitbreaker.Customizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.time.Duration; import java.util.function.Consumer; import java.util.function.Function; @Configuration public class Resilience4jConfig { @Bean public Customizer resilience4JCircuitBreakerFactoryCustomizer() { return new Customizer() { @Override public void customize(Resilience4JCircuitBreakerFactory resilience4JCircuitBreakerFactory) { // 设置默认的配置 resilience4JCircuitBreakerFactory.configureDefault(new Function() { @Override public Resilience4JConfigBuilder.Resilience4JCircuitBreakerConfiguration apply(String id) { // 创建 TimeLimiterConfig 对象 TimeLimiterConfig timeLimiterConfig = TimeLimiterConfig.ofDefaults(); // 默认 // 创建 CircuitBreakerConfig 对象 CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.ofDefaults(); // 默认 // 创建 Resilience4JCircuitBreakerConfiguration 对象 return new Resilience4JConfigBuilder(id) .timeLimiterConfig(timeLimiterConfig) .circuitBreakerConfig(circuitBreakerConfig) .build(); } }); // 设置编号为 "slow" 的自定义配置 resilience4JCircuitBreakerFactory.configure(new Consumer() { @Override public void accept(Resilience4JConfigBuilder resilience4JConfigBuilder) { // 创建 TimeLimiterConfig 对象 TimeLimiterConfig timeLimiterConfig = TimeLimiterConfig.custom().timeoutDuration(Duration.ofSeconds(4)) // 自定义 .build(); // 创建 CircuitBreakerConfig 对象 CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom() // 自定义 .slidingWindow(5, 5, CircuitBreakerConfig.SlidingWindowType.COUNT_BASED) .build(); // 设置 Resilience4JCircuitBreakerConfiguration 对象 resilience4JConfigBuilder .timeLimiterConfig(timeLimiterConfig) .circuitBreakerConfig(circuitBreakerConfig); } }, "slow"); } }; } // @Bean // public Customizer defaultCustomizer() { // return factory -> factory.configureDefault( // id -> new Resilience4JConfigBuilder(id) // .timeLimiterConfig(TimeLimiterConfig.custom().timeoutDuration(Duration.ofSeconds(4)).build()) // .circuitBreakerConfig(CircuitBreakerConfig.ofDefaults()) // .build()); // } // @Bean // public Customizer slowCustomizer() { // return factory -> factory.configure(builder -> builder // .timeLimiterConfig(TimeLimiterConfig.custom().timeoutDuration(Duration.ofSeconds(2)).build()) // .circuitBreakerConfig(CircuitBreakerConfig.ofDefaults()), // "slow"); // } } ================================================ FILE: labx-24/labx-24-resilience4j-demo02/src/main/java/cn/iocoder/springcloud/labx24/resilience4jdemo/controller/DemoController.java ================================================ package cn.iocoder.springcloud.labx24.resilience4jdemo.controller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import java.util.function.Function; import java.util.function.Supplier; @RestController @RequestMapping("/demo") public class DemoController { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private RestTemplate restTemplate; @Autowired private CircuitBreakerFactory circuitBreakerFactory; @GetMapping("/get_user") public String getUser(@RequestParam("id") Integer id) { return circuitBreakerFactory.create("slow").run(new Supplier() { @Override public String get() { logger.info("[getUser][准备调用 user-service 获取用户({})详情]", id); return restTemplate.getForEntity("http://127.0.0.1:18080/user/get?id=" + id, String.class).getBody(); } }, new Function() { @Override public String apply(Throwable throwable) { logger.info("[getUserFallback][id({}) exception({})]", id, throwable.getClass().getSimpleName()); return "mock:User:" + id; } }); } } ================================================ FILE: labx-24/labx-24-user-service/pom.xml ================================================ labx-24 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-24-user-service 1.8 1.8 2.2.4.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.boot spring-boot-starter-web ================================================ FILE: labx-24/labx-24-user-service/src/main/java/cn/iocoder/springcloud/labx24/userservice/UserServiceApplication.java ================================================ package cn.iocoder.springcloud.labx24.userservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.List; import java.util.stream.Collectors; @SpringBootApplication public class UserServiceApplication { @RestController @RequestMapping("/user") public class UserController { @GetMapping("/get") public String get(@RequestParam("id") Integer id) { return "User:" + id; } @GetMapping("/batch_get") public List batchGet(@RequestParam("ids") List ids) { return ids.stream().map(id -> "User:" + id).collect(Collectors.toList()); } } public static void main(String[] args) { // 设置端口 System.setProperty("server.port", "18080"); // 应用启动 SpringApplication.run(UserServiceApplication.class, args); } } ================================================ FILE: labx-24/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-24 labx-24-user-service labx-24-resilience4j-demo01 labx-24-resilience4j-demo02 ================================================ FILE: labx-24/《芋道 Spring Cloud 服务容错 Resilience4j 入门》.md ================================================ ================================================ FILE: labx-25/labx-25-sc-zookeeper-discovery-demo01-consumer/pom.xml ================================================ labx-25 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-25-sc-zookeeper-discovery-demo01-consumer 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-zookeeper-discovery ================================================ FILE: labx-25/labx-25-sc-zookeeper-discovery-demo01-consumer/src/main/java/cn/iocoder/springcloud/labx25/zookeeperdemo/consumer/DemoConsumerApplication.java ================================================ package cn.iocoder.springcloud.labx25.zookeeperdemo.consumer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import java.util.List; @SpringBootApplication // @EnableDiscoveryClient public class DemoConsumerApplication { public static void main(String[] args) { SpringApplication.run(DemoConsumerApplication.class, args); } @Configuration public class RestTemplateConfiguration { @Bean public RestTemplate restTemplate() { return new RestTemplate(); } } @RestController static class TestController { @Autowired private DiscoveryClient discoveryClient; @Autowired private RestTemplate restTemplate; @Autowired private LoadBalancerClient loadBalancerClient; @GetMapping("/hello") public String hello(String name) { // 获得服务 `demo-provider` 的一个实例 ServiceInstance instance; if (true) { // 获取服务 `demo-provider` 对应的实例列表 List instances = discoveryClient.getInstances("demo-provider"); // 选择第一个 instance = instances.size() > 0 ? instances.get(0) : null; } else { instance = loadBalancerClient.choose("demo-provider"); } // 发起调用 if (instance == null) { throw new IllegalStateException("获取不到实例"); } String targetUrl = instance.getUri() + "/echo?name=" + name; String response = restTemplate.getForObject(targetUrl, String.class); // 返回结果 return "consumer:" + response; } } } ================================================ FILE: labx-25/labx-25-sc-zookeeper-discovery-demo01-consumer/src/main/resources/application.yaml ================================================ spring: application: name: demo-consumer # Spring 应用名 cloud: zookeeper: connect-string: 127.0.0.1:2181 # Zookeeper 作为注册中心的配置项,对应 ZookeeperDiscoveryProperties 配置类 discovery: root: /services # Zookeeper 数据存储的根节点,默认为 /services server: port: 28080 # 服务器端口。默认为 8080 ================================================ FILE: labx-25/labx-25-sc-zookeeper-discovery-demo01-provider/pom.xml ================================================ labx-25 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-25-sc-zookeeper-discovery-demo01-provider 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-zookeeper-discovery ================================================ FILE: labx-25/labx-25-sc-zookeeper-discovery-demo01-provider/src/main/java/cn/iocoder/springcloud/labx25/zookeeperdemo/provider/DemoProviderApplication.java ================================================ package cn.iocoder.springcloud.labx25.zookeeperdemo.provider; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @SpringBootApplication @EnableDiscoveryClient public class DemoProviderApplication { public static void main(String[] args) { SpringApplication.run(DemoProviderApplication.class, args); } @RestController static class TestController { @GetMapping("/echo") public String echo(String name) { return "provider:" + name; } } } ================================================ FILE: labx-25/labx-25-sc-zookeeper-discovery-demo01-provider/src/main/resources/application.yaml ================================================ spring: application: name: demo-provider # Spring 应用名 cloud: zookeeper: connect-string: 127.0.0.1:2181 # Zookeeper 作为注册中心的配置项,对应 ZookeeperDiscoveryProperties 配置类 discovery: root: /services # Zookeeper 数据存储的根节点,默认为 /services server: port: 18080 # 服务器端口。默认为 8080 ================================================ FILE: labx-25/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-25 pom labx-25-sc-zookeeper-discovery-demo01-provider labx-25-sc-zookeeper-discovery-demo01-consumer ================================================ FILE: labx-25/《芋道 Spring Cloud 注册中心 ZooKeeper 入门》.md ================================================ ================================================ FILE: labx-26/labx-26-sc-zookeeper-config-auto-refresh/pom.xml ================================================ labx-26 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-26-sc-zookeeper-config-auto-refresh 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-zookeeper-config ================================================ FILE: labx-26/labx-26-sc-zookeeper-config-auto-refresh/src/main/java/cn/iocoder/springcloud/labx26/zookeeperdemo/DemoApplication.java ================================================ package cn.iocoder.springcloud.labx26.zookeeperdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class); } } ================================================ FILE: labx-26/labx-26-sc-zookeeper-config-auto-refresh/src/main/java/cn/iocoder/springcloud/labx26/zookeeperdemo/config/OrderProperties.java ================================================ package cn.iocoder.springcloud.labx26.zookeeperdemo.config; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component @ConfigurationProperties(prefix = "order") public class OrderProperties { /** * 订单支付超时时长,单位:秒。 */ private Integer payTimeoutSeconds; /** * 订单创建频率,单位:秒 */ private Integer createFrequencySeconds; // /** // * 配置描述 // */ // private String desc; public Integer getPayTimeoutSeconds() { return payTimeoutSeconds; } public OrderProperties setPayTimeoutSeconds(Integer payTimeoutSeconds) { this.payTimeoutSeconds = payTimeoutSeconds; return this; } public Integer getCreateFrequencySeconds() { return createFrequencySeconds; } public OrderProperties setCreateFrequencySeconds(Integer createFrequencySeconds) { this.createFrequencySeconds = createFrequencySeconds; return this; } // public String getDesc() { // return desc; // } // // public OrderProperties setDesc(String desc) { // this.desc = desc; // return this; // } } ================================================ FILE: labx-26/labx-26-sc-zookeeper-config-auto-refresh/src/main/java/cn/iocoder/springcloud/labx26/zookeeperdemo/controller/DemoController.java ================================================ package cn.iocoder.springcloud.labx26.zookeeperdemo.controller; import cn.iocoder.springcloud.labx26.zookeeperdemo.config.OrderProperties; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.Map; @RestController @RequestMapping("/demo") @RefreshScope public class DemoController { @Autowired private OrderProperties orderProperties; /** * 测试 @ConfigurationProperties 注解的配置属性类 */ @GetMapping("/test01") public OrderProperties test01() { return orderProperties; } @Value(value = "${order.pay-timeout-seconds}") // @NacosValue(value = "${order.pay-timeout-seconds}") private Integer payTimeoutSeconds; @Value(value = "${order.create-frequency-seconds}") // @NacosValue(value = "${order.create-frequency-seconds}") private Integer createFrequencySeconds; /** * 测试 @Value 注解的属性 */ @GetMapping("/test02") public Map test02() { Map results = new HashMap(); results.put("payTimeoutSeconds", payTimeoutSeconds); results.put("createFrequencySeconds", createFrequencySeconds); return results; } } ================================================ FILE: labx-26/labx-26-sc-zookeeper-config-auto-refresh/src/main/java/cn/iocoder/springcloud/labx26/zookeeperdemo/listener/DemoEnvironmentChangeListener.java ================================================ package cn.iocoder.springcloud.labx26.zookeeperdemo.listener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.context.environment.EnvironmentChangeEvent; import org.springframework.context.ApplicationListener; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.stereotype.Component; @Component public class DemoEnvironmentChangeListener implements ApplicationListener { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private ConfigurableEnvironment environment; @Override public void onApplicationEvent(EnvironmentChangeEvent event) { for (String key : event.getKeys()) { logger.info("[onApplicationEvent][key({}) 最新 value 为 {}]", key, environment.getProperty(key)); } } } ================================================ FILE: labx-26/labx-26-sc-zookeeper-config-auto-refresh/src/main/resources/bootstrap.yaml ================================================ spring: application: name: demo-application cloud: zookeeper: connect-string: 127.0.0.1:2181 # Zookeeper 作为配置中心的配置项,对应 ZookeeperConfigProperties 配置类 config: root: /config # Zookeeper 数据存储的根节点,默认为 /config default-context: application # 读取 Zookeeper 配置的默认目录,默认为 application profile-separator: ',' # 多环境目录分隔符,默认为 , watcher: enabled: true # 是否开启 Watch 功能,监听 Zookeeper 数据的变化。默认为 true,实现实时监听配置的更新 ================================================ FILE: labx-26/labx-26-sc-zookeeper-config-demo/pom.xml ================================================ labx-26 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-26-sc-zookeeper-config-demo 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-zookeeper-config ================================================ FILE: labx-26/labx-26-sc-zookeeper-config-demo/src/main/java/cn/iocoder/springcloud/labx26/zookeeperdemo/DemoApplication.java ================================================ package cn.iocoder.springcloud.labx26.zookeeperdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class); } } ================================================ FILE: labx-26/labx-26-sc-zookeeper-config-demo/src/main/java/cn/iocoder/springcloud/labx26/zookeeperdemo/config/OrderProperties.java ================================================ package cn.iocoder.springcloud.labx26.zookeeperdemo.config; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component @ConfigurationProperties(prefix = "order") public class OrderProperties { /** * 订单支付超时时长,单位:秒。 */ private Integer payTimeoutSeconds; /** * 订单创建频率,单位:秒 */ private Integer createFrequencySeconds; // /** // * 配置描述 // */ // private String desc; public Integer getPayTimeoutSeconds() { return payTimeoutSeconds; } public OrderProperties setPayTimeoutSeconds(Integer payTimeoutSeconds) { this.payTimeoutSeconds = payTimeoutSeconds; return this; } public Integer getCreateFrequencySeconds() { return createFrequencySeconds; } public OrderProperties setCreateFrequencySeconds(Integer createFrequencySeconds) { this.createFrequencySeconds = createFrequencySeconds; return this; } // public String getDesc() { // return desc; // } // // public OrderProperties setDesc(String desc) { // this.desc = desc; // return this; // } } ================================================ FILE: labx-26/labx-26-sc-zookeeper-config-demo/src/main/java/cn/iocoder/springcloud/labx26/zookeeperdemo/controller/DemoController.java ================================================ package cn.iocoder.springcloud.labx26.zookeeperdemo.controller; import cn.iocoder.springcloud.labx26.zookeeperdemo.config.OrderProperties; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.Map; @RestController @RequestMapping("/demo") public class DemoController { @Autowired private OrderProperties orderProperties; /** * 测试 @ConfigurationProperties 注解的配置属性类 */ @GetMapping("/test01") public OrderProperties test01() { return orderProperties; } @Value(value = "${order.pay-timeout-seconds}") // @NacosValue(value = "${order.pay-timeout-seconds}") private Integer payTimeoutSeconds; @Value(value = "${order.create-frequency-seconds}") // @NacosValue(value = "${order.create-frequency-seconds}") private Integer createFrequencySeconds; /** * 测试 @Value 注解的属性 */ @GetMapping("/test02") public Map test02() { Map results = new HashMap(); results.put("payTimeoutSeconds", payTimeoutSeconds); results.put("createFrequencySeconds", createFrequencySeconds); return results; } } ================================================ FILE: labx-26/labx-26-sc-zookeeper-config-demo/src/main/resources/bootstrap.yaml ================================================ spring: application: name: demo-application cloud: zookeeper: connect-string: 127.0.0.1:2181 # Zookeeper 作为配置中心的配置项,对应 ZookeeperConfigProperties 配置类 config: root: /config # Zookeeper 数据存储的根节点,默认为 /config default-context: application # 读取 Zookeeper 配置的默认目录,默认为 application profile-separator: ',' # 多环境目录分隔符,默认为 , watcher: enabled: true # 是否开启 Watch 功能,监听 Zookeeper 数据的变化。默认为 true,实现实时监听配置的更新 ================================================ FILE: labx-26/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-26 labx-26-sc-zookeeper-config-demo labx-26-sc-zookeeper-config-auto-refresh ================================================ FILE: labx-26/《芋道 Spring Cloud 配置中心 ZooKeeper 入门》.md ================================================ ================================================ FILE: labx-27/labx-27-sc-consul-discovery-demo01-consumer/pom.xml ================================================ labx-27 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-27-sc-consul-discovery-demo01-consumer 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-consul-discovery org.springframework.boot spring-boot-starter-actuator ================================================ FILE: labx-27/labx-27-sc-consul-discovery-demo01-consumer/src/main/java/cn/iocoder/springcloud/labx27/consuldemo/consumer/DemoConsumerApplication.java ================================================ package cn.iocoder.springcloud.labx27.consuldemo.consumer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import java.util.List; @SpringBootApplication // @EnableDiscoveryClient public class DemoConsumerApplication { public static void main(String[] args) { SpringApplication.run(DemoConsumerApplication.class, args); } @Configuration public class RestTemplateConfiguration { @Bean public RestTemplate restTemplate() { return new RestTemplate(); } } @RestController static class TestController { @Autowired private DiscoveryClient discoveryClient; @Autowired private RestTemplate restTemplate; @Autowired private LoadBalancerClient loadBalancerClient; @GetMapping("/hello") public String hello(String name) { // 获得服务 `demo-provider` 的一个实例 ServiceInstance instance; if (true) { // 获取服务 `demo-provider` 对应的实例列表 List instances = discoveryClient.getInstances("demo-provider"); // 选择第一个 instance = instances.size() > 0 ? instances.get(0) : null; } else { instance = loadBalancerClient.choose("demo-provider"); } // 发起调用 if (instance == null) { throw new IllegalStateException("获取不到实例"); } String targetUrl = instance.getUri() + "/echo?name=" + name; String response = restTemplate.getForObject(targetUrl, String.class); // 返回结果 return "consumer:" + response; } } } ================================================ FILE: labx-27/labx-27-sc-consul-discovery-demo01-consumer/src/main/resources/application.yaml ================================================ spring: application: name: demo-consumer # Spring 应用名 cloud: # Spring Cloud Consul 通用配置项,对应 ConsulProperties 类 consul: host: 127.0.0.1 # Consul 主机 port: 8500 # Consul 端口 # Spring Cloud Consul Discovery 配置项,对应 ConsulDiscoveryProperties 类 discovery: service-name: ${spring.application.name} # 注册到 Consul 的服务名,默认为 `spring.application.name` 配置项 health-check-path: /actuator/health # 健康检查的接口,默认为 /actuator/health,由 Spring Boot Actuator 提供 server: port: 28080 # 服务器端口。默认为 8080 ================================================ FILE: labx-27/labx-27-sc-consul-discovery-demo01-provider/pom.xml ================================================ labx-27 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-27-sc-consul-discovery-demo01-provider 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-consul-discovery org.springframework.boot spring-boot-starter-actuator ================================================ FILE: labx-27/labx-27-sc-consul-discovery-demo01-provider/src/main/java/cn/iocoder/springcloud/labx27/consuldemo/provider/DemoProviderApplication.java ================================================ package cn.iocoder.springcloud.labx27.consuldemo.provider; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @SpringBootApplication @EnableDiscoveryClient public class DemoProviderApplication { public static void main(String[] args) { SpringApplication.run(DemoProviderApplication.class, args); } @RestController static class TestController { @GetMapping("/echo") public String echo(String name) { return "provider:" + name; } } } ================================================ FILE: labx-27/labx-27-sc-consul-discovery-demo01-provider/src/main/resources/application.yaml ================================================ spring: application: name: demo-provider # Spring 应用名 cloud: # Spring Cloud Consul 通用配置项,对应 ConsulProperties 类 consul: host: 127.0.0.1 # Consul 主机 port: 8500 # Consul 端口 # Spring Cloud Consul Discovery 配置项,对应 ConsulDiscoveryProperties 类 discovery: service-name: ${spring.application.name} # 注册到 Consul 的服务名,默认为 `spring.application.name` 配置项 health-check-path: /actuator/health # 健康检查的接口,默认为 /actuator/health,由 Spring Boot Actuator 提供 server: port: 18080 # 服务器端口。默认为 8080 ================================================ FILE: labx-27/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-27 pom labx-27-sc-consul-discovery-demo01-provider labx-27-sc-consul-discovery-demo01-consumer ================================================ FILE: labx-27/《芋道 Spring Cloud 注册中心 Consul 入门》.md ================================================ ================================================ FILE: labx-28/labx-28-sc-consul-config-auto-refresh/pom.xml ================================================ labx-28 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-28-sc-consul-config-auto-refresh 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-consul-config ================================================ FILE: labx-28/labx-28-sc-consul-config-auto-refresh/src/main/java/cn/iocoder/springcloud/labx28/consuldemo/DemoApplication.java ================================================ package cn.iocoder.springcloud.labx28.consuldemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class); } } ================================================ FILE: labx-28/labx-28-sc-consul-config-auto-refresh/src/main/java/cn/iocoder/springcloud/labx28/consuldemo/config/OrderProperties.java ================================================ package cn.iocoder.springcloud.labx28.consuldemo.config; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component @ConfigurationProperties(prefix = "order") public class OrderProperties { /** * 订单支付超时时长,单位:秒。 */ private Integer payTimeoutSeconds; /** * 订单创建频率,单位:秒 */ private Integer createFrequencySeconds; // /** // * 配置描述 // */ // private String desc; public Integer getPayTimeoutSeconds() { return payTimeoutSeconds; } public OrderProperties setPayTimeoutSeconds(Integer payTimeoutSeconds) { this.payTimeoutSeconds = payTimeoutSeconds; return this; } public Integer getCreateFrequencySeconds() { return createFrequencySeconds; } public OrderProperties setCreateFrequencySeconds(Integer createFrequencySeconds) { this.createFrequencySeconds = createFrequencySeconds; return this; } // public String getDesc() { // return desc; // } // // public OrderProperties setDesc(String desc) { // this.desc = desc; // return this; // } } ================================================ FILE: labx-28/labx-28-sc-consul-config-auto-refresh/src/main/java/cn/iocoder/springcloud/labx28/consuldemo/controller/DemoController.java ================================================ package cn.iocoder.springcloud.labx28.consuldemo.controller; import cn.iocoder.springcloud.labx28.consuldemo.config.OrderProperties; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.Map; @RestController @RequestMapping("/demo") @RefreshScope public class DemoController { @Autowired private OrderProperties orderProperties; /** * 测试 @ConfigurationProperties 注解的配置属性类 */ @GetMapping("/test01") public OrderProperties test01() { return orderProperties; } @Value(value = "${order.pay-timeout-seconds}") // @NacosValue(value = "${order.pay-timeout-seconds}") private Integer payTimeoutSeconds; @Value(value = "${order.create-frequency-seconds}") // @NacosValue(value = "${order.create-frequency-seconds}") private Integer createFrequencySeconds; /** * 测试 @Value 注解的属性 */ @GetMapping("/test02") public Map test02() { Map results = new HashMap(); results.put("payTimeoutSeconds", payTimeoutSeconds); results.put("createFrequencySeconds", createFrequencySeconds); return results; } } ================================================ FILE: labx-28/labx-28-sc-consul-config-auto-refresh/src/main/java/cn/iocoder/springcloud/labx28/consuldemo/listener/DemoEnvironmentChangeListener.java ================================================ package cn.iocoder.springcloud.labx28.consuldemo.listener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.context.environment.EnvironmentChangeEvent; import org.springframework.context.ApplicationListener; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.stereotype.Component; @Component public class DemoEnvironmentChangeListener implements ApplicationListener { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private ConfigurableEnvironment environment; @Override public void onApplicationEvent(EnvironmentChangeEvent event) { for (String key : event.getKeys()) { logger.info("[onApplicationEvent][key({}) 最新 value 为 {}]", key, environment.getProperty(key)); } } } ================================================ FILE: labx-28/labx-28-sc-consul-config-auto-refresh/src/main/resources/bootstrap.yaml ================================================ spring: application: name: demo-application cloud: # Spring Cloud Consul 通用配置项,对应 ConsulProperties 类 consul: host: 127.0.0.1 # Consul 主机 port: 8500 # Consul 端口 # Spring Cloud Consul Config 配置项,对应 ConsulConfigProperties 类 config: format: YAML # Consul 配置的格式 prefix: config # Consul 配置的目录 data-key: data # Consul 配置的文件 profile-separator: ',' # 多环境目录分隔符,默认为 , ================================================ FILE: labx-28/labx-28-sc-consul-config-demo/pom.xml ================================================ labx-28 cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-28-sc-consul-config-demo 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-consul-config ================================================ FILE: labx-28/labx-28-sc-consul-config-demo/src/main/java/cn/iocoder/springcloud/labx28/consuldemo/DemoApplication.java ================================================ package cn.iocoder.springcloud.labx28.consuldemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class); } } ================================================ FILE: labx-28/labx-28-sc-consul-config-demo/src/main/java/cn/iocoder/springcloud/labx28/consuldemo/config/OrderProperties.java ================================================ package cn.iocoder.springcloud.labx28.consuldemo.config; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component @ConfigurationProperties(prefix = "order") public class OrderProperties { /** * 订单支付超时时长,单位:秒。 */ private Integer payTimeoutSeconds; /** * 订单创建频率,单位:秒 */ private Integer createFrequencySeconds; // /** // * 配置描述 // */ // private String desc; public Integer getPayTimeoutSeconds() { return payTimeoutSeconds; } public OrderProperties setPayTimeoutSeconds(Integer payTimeoutSeconds) { this.payTimeoutSeconds = payTimeoutSeconds; return this; } public Integer getCreateFrequencySeconds() { return createFrequencySeconds; } public OrderProperties setCreateFrequencySeconds(Integer createFrequencySeconds) { this.createFrequencySeconds = createFrequencySeconds; return this; } // public String getDesc() { // return desc; // } // // public OrderProperties setDesc(String desc) { // this.desc = desc; // return this; // } } ================================================ FILE: labx-28/labx-28-sc-consul-config-demo/src/main/java/cn/iocoder/springcloud/labx28/consuldemo/controller/DemoController.java ================================================ package cn.iocoder.springcloud.labx28.consuldemo.controller; import cn.iocoder.springcloud.labx28.consuldemo.config.OrderProperties; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.Map; @RestController @RequestMapping("/demo") public class DemoController { @Autowired private OrderProperties orderProperties; /** * 测试 @ConfigurationProperties 注解的配置属性类 */ @GetMapping("/test01") public OrderProperties test01() { return orderProperties; } @Value(value = "${order.pay-timeout-seconds}") // @NacosValue(value = "${order.pay-timeout-seconds}") private Integer payTimeoutSeconds; @Value(value = "${order.create-frequency-seconds}") // @NacosValue(value = "${order.create-frequency-seconds}") private Integer createFrequencySeconds; /** * 测试 @Value 注解的属性 */ @GetMapping("/test02") public Map test02() { Map results = new HashMap(); results.put("payTimeoutSeconds", payTimeoutSeconds); results.put("createFrequencySeconds", createFrequencySeconds); return results; } } ================================================ FILE: labx-28/labx-28-sc-consul-config-demo/src/main/resources/bootstrap.yaml ================================================ spring: application: name: demo-application cloud: # Spring Cloud Consul 通用配置项,对应 ConsulProperties 类 consul: host: 127.0.0.1 # Consul 主机 port: 8500 # Consul 端口 # Spring Cloud Consul Config 配置项,对应 ConsulConfigProperties 类 config: format: YAML # Consul 配置的格式 prefix: config # Consul 配置的目录 data-key: data # Consul 配置的文件 profile-separator: ',' # 多环境目录分隔符,默认为 , ================================================ FILE: labx-28/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-28 pom labx-28-sc-consul-config-demo labx-28-sc-consul-config-auto-refresh ================================================ FILE: labx-28/《芋道 Spring Cloud 配置中心 Consul 入门》.md ================================================ ================================================ FILE: labx-29-spring-cloud-consul-bus/labx-29-sc-bus-consul-demo-listener/pom.xml ================================================ labx-29-spring-cloud-consul-bus cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-29-sc-bus-consul-demo-listener 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-consul-bus ================================================ FILE: labx-29-spring-cloud-consul-bus/labx-29-sc-bus-consul-demo-listener/src/main/java/cn/iocoder/springcloud/labx29/listenerdemo/ListenerDemoApplication.java ================================================ package cn.iocoder.springcloud.labx29.listenerdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.bus.jackson.RemoteApplicationEventScan; @SpringBootApplication @RemoteApplicationEventScan public class ListenerDemoApplication { public static void main(String[] args) { SpringApplication.run(ListenerDemoApplication.class, args); } } ================================================ FILE: labx-29-spring-cloud-consul-bus/labx-29-sc-bus-consul-demo-listener/src/main/java/cn/iocoder/springcloud/labx29/listenerdemo/event/UserRegisterEvent.java ================================================ package cn.iocoder.springcloud.labx29.listenerdemo.event; import org.springframework.cloud.bus.event.RemoteApplicationEvent; /** * 用户注册事件 */ public class UserRegisterEvent extends RemoteApplicationEvent { /** * 用户名 */ private String username; public UserRegisterEvent() { // 序列化 } public UserRegisterEvent(Object source, String originService, String destinationService, String username) { super(source, originService); this.username = username; } public String getUsername() { return username; } } ================================================ FILE: labx-29-spring-cloud-consul-bus/labx-29-sc-bus-consul-demo-listener/src/main/java/cn/iocoder/springcloud/labx29/listenerdemo/listener/UserRegisterListener.java ================================================ package cn.iocoder.springcloud.labx29.listenerdemo.listener; import cn.iocoder.springcloud.labx29.listenerdemo.event.UserRegisterEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; /** * 用户注册事件的监听器 */ @Component public class UserRegisterListener implements ApplicationListener { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public void onApplicationEvent(UserRegisterEvent event) { logger.info("[onApplicationEvent][监听到用户({}) 注册]", event.getUsername()); } } ================================================ FILE: labx-29-spring-cloud-consul-bus/labx-29-sc-bus-consul-demo-listener/src/main/resources/application.yml ================================================ spring: application: name: listener-demo cloud: # Consul consul: host: 127.0.0.1 port: 8500 server: port: ${random.int[10000,19999]} # 随机端口,方便启动多个消费者 ================================================ FILE: labx-29-spring-cloud-consul-bus/labx-29-sc-bus-consul-demo-listener-actuator/pom.xml ================================================ labx-29-spring-cloud-consul-bus cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-29-sc-bus-consul-demo-listener-actuator 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-consul-bus org.springframework.boot spring-boot-starter-actuator ================================================ FILE: labx-29-spring-cloud-consul-bus/labx-29-sc-bus-consul-demo-listener-actuator/src/main/java/cn/iocoder/springcloud/labx29/listenerdemo/ListenerDemoApplication.java ================================================ package cn.iocoder.springcloud.labx29.listenerdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.bus.jackson.RemoteApplicationEventScan; @SpringBootApplication @RemoteApplicationEventScan public class ListenerDemoApplication { public static void main(String[] args) { SpringApplication.run(ListenerDemoApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx29.listenerdemo.event; import org.springframework.cloud.bus.event.RemoteApplicationEvent; /** * 用户注册事件 */ public class UserRegisterEvent extends RemoteApplicationEvent { /** * 用户名 */ private String username; public UserRegisterEvent() { // 序列化 } public UserRegisterEvent(Object source, String originService, String destinationService, String username) { super(source, originService); this.username = username; } public String getUsername() { return username; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx29.listenerdemo.listener; import cn.iocoder.springcloud.labx29.listenerdemo.event.UserRegisterEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; /** * 用户注册事件的监听器 */ @Component public class UserRegisterListener implements ApplicationListener { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public void onApplicationEvent(UserRegisterEvent event) { logger.info("[onApplicationEvent][监听到用户({}) 注册]", event.getUsername()); } } ================================================ FILE: labx-29-spring-cloud-consul-bus/labx-29-sc-bus-consul-demo-listener-actuator/src/main/java/org/springframework/cloud/consul/binder/ConsulSendingHandler.java ================================================ package org.springframework.cloud.consul.binder; import com.ecwid.consul.v1.ConsulClient; import com.ecwid.consul.v1.QueryParams; import com.ecwid.consul.v1.Response; import com.ecwid.consul.v1.event.model.Event; import com.ecwid.consul.v1.event.model.EventParams; import org.springframework.integration.handler.AbstractMessageHandler; import org.springframework.messaging.Message; /** * Adapter that converts and sends Messages as Consul events. * * @author Spencer Gibb */ public class ConsulSendingHandler extends AbstractMessageHandler { private final ConsulClient consul; private final String eventName; public ConsulSendingHandler(ConsulClient consul, String eventName) { this.consul = consul; this.eventName = eventName; } @Override protected void handleMessageInternal(Message message) { if (this.logger.isTraceEnabled()) { this.logger.trace("Publishing message" + message); } // 转换成 String Object payload = message.getPayload(); if (payload instanceof byte[]) { payload = new String((byte[]) payload); } // TODO: support headers // TODO: support consul event filters: NodeFilter, ServiceFilter, TagFilter Response event = this.consul.eventFire(this.eventName, (String) payload, new EventParams(), QueryParams.DEFAULT); // TODO: return event? } } ================================================ FILE: labx-29-spring-cloud-consul-bus/labx-29-sc-bus-consul-demo-listener-actuator/src/main/resources/application.yml ================================================ spring: application: name: listener-demo cloud: # Consul consul: host: 127.0.0.1 port: 8500 server: port: 18080 # 随机端口,方便启动多个消费者 management: endpoints: # Actuator HTTP 配置项,对应 WebEndpointProperties 配置类 web: exposure: include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 ================================================ FILE: labx-29-spring-cloud-consul-bus/labx-29-sc-bus-consul-demo-publisher/pom.xml ================================================ labx-29-spring-cloud-consul-bus cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-29-sc-bus-consul-demo-publisher 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-consul-bus ================================================ FILE: labx-29-spring-cloud-consul-bus/labx-29-sc-bus-consul-demo-publisher/src/main/java/cn/iocoder/springcloud/labx29/publisherdemo/PublisherDemoApplication.java ================================================ package cn.iocoder.springcloud.labx29.publisherdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication //@RemoteApplicationEventScan public class PublisherDemoApplication { public static void main(String[] args) { SpringApplication.run(PublisherDemoApplication.class, args); } } ================================================ FILE: labx-29-spring-cloud-consul-bus/labx-29-sc-bus-consul-demo-publisher/src/main/java/cn/iocoder/springcloud/labx29/publisherdemo/controller/DemoController.java ================================================ package cn.iocoder.springcloud.labx29.publisherdemo.controller; import cn.iocoder.springcloud.labx29.publisherdemo.event.UserRegisterEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.bus.ServiceMatcher; import org.springframework.context.ApplicationEventPublisher; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private ApplicationEventPublisher applicationEventPublisher; @Autowired private ServiceMatcher busServiceMatcher; @GetMapping("/register") public String register(String username) { // ... 执行注册逻辑 logger.info("[register][执行用户({}) 的注册逻辑]", username); // ... 发布 applicationEventPublisher.publishEvent(new UserRegisterEvent(this, busServiceMatcher.getServiceId(), null, username)); return "success"; } } ================================================ FILE: labx-29-spring-cloud-consul-bus/labx-29-sc-bus-consul-demo-publisher/src/main/java/cn/iocoder/springcloud/labx29/publisherdemo/event/UserRegisterEvent.java ================================================ package cn.iocoder.springcloud.labx29.publisherdemo.event; import org.springframework.cloud.bus.event.RemoteApplicationEvent; /** * 用户注册事件 */ public class UserRegisterEvent extends RemoteApplicationEvent { /** * 用户名 */ private String username; public UserRegisterEvent() { // 序列化 } public UserRegisterEvent(Object source, String originService, String destinationService, String username) { super(source, originService); this.username = username; } public String getUsername() { return username; } } ================================================ FILE: labx-29-spring-cloud-consul-bus/labx-29-sc-bus-consul-demo-publisher/src/main/java/org/springframework/cloud/consul/binder/ConsulSendingHandler.java ================================================ package org.springframework.cloud.consul.binder; import com.ecwid.consul.v1.ConsulClient; import com.ecwid.consul.v1.QueryParams; import com.ecwid.consul.v1.Response; import com.ecwid.consul.v1.event.model.Event; import com.ecwid.consul.v1.event.model.EventParams; import org.springframework.integration.handler.AbstractMessageHandler; import org.springframework.messaging.Message; /** * Adapter that converts and sends Messages as Consul events. * * @author Spencer Gibb */ public class ConsulSendingHandler extends AbstractMessageHandler { private final ConsulClient consul; private final String eventName; public ConsulSendingHandler(ConsulClient consul, String eventName) { this.consul = consul; this.eventName = eventName; } @Override protected void handleMessageInternal(Message message) { if (this.logger.isTraceEnabled()) { this.logger.trace("Publishing message" + message); } // 转换成 String Object payload = message.getPayload(); if (payload instanceof byte[]) { payload = new String((byte[]) payload); } // TODO: support headers // TODO: support consul event filters: NodeFilter, ServiceFilter, TagFilter Response event = this.consul.eventFire(this.eventName, (String) payload, new EventParams(), QueryParams.DEFAULT); // TODO: return event? } } ================================================ FILE: labx-29-spring-cloud-consul-bus/labx-29-sc-bus-consul-demo-publisher/src/main/resources/application.yml ================================================ server: port: 8081 spring: application: name: publisher-demo cloud: # Consul consul: host: 127.0.0.1 port: 8500 # Bus 相关配置项,对应 BusProperties bus: enabled: true # 是否开启,默认为 true destination: springCloudBus # 目标消息队列,默认为 springCloudBus ================================================ FILE: labx-29-spring-cloud-consul-bus/labx-29-sc-config-server-git-auto-refresh-by-bus/pom.xml ================================================ labx-29-spring-cloud-consul-bus cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-29-sc-config-server-git-auto-refresh-by-bus 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-config-server org.springframework.cloud spring-cloud-starter-consul-bus org.springframework.cloud spring-cloud-config-monitor ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx29.configserverdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.config.server.EnableConfigServer; @SpringBootApplication @EnableConfigServer public class ConfigServerApplication { public static void main(String[] args) { SpringApplication.run(ConfigServerApplication.class, args); } } ================================================ FILE: labx-29-spring-cloud-consul-bus/labx-29-sc-config-server-git-auto-refresh-by-bus/src/main/resources/application.yml ================================================ server: port: 8888 spring: application: name: demo-config-server profiles: active: git # 使用的 Spring Cloud Config Server 的存储器方案 # Spring Cloud Config 相关配置项 cloud: config: server: # Spring Cloud Config Server 的 Git 存储器的配置项,对应 MultipleJGitEnvironmentProperties 类 git: uri: https://github.com/YunaiV/demo-config-server.git # Git 仓库地址 search-paths: / # 读取文件的根地址 default-label: master # 使用的默认分支,默认为 master # username: ${CODING_USERNAME} # 账号 # password: ${CODING_PASSWORD} # 密码 # Consul consul: host: 127.0.0.1 port: 8500 ================================================ FILE: labx-29-spring-cloud-consul-bus/labx-29-sc-config-user-application-auto-refresh-by-bus/pom.xml ================================================ labx-29-spring-cloud-consul-bus cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-29-sc-config-user-application-auto-refresh-by-bus 1.8 1.8 2.2.4.RELEASE Hoxton.SR1 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-config org.springframework.cloud spring-cloud-starter-consul-bus ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx29.userapplication; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class UserApplication { public static void main(String[] args) { SpringApplication.run(UserApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx29.userapplication.config; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component @ConfigurationProperties(prefix = "order") public class OrderProperties { /** * 订单支付超时时长,单位:秒。 */ private Integer payTimeoutSeconds; /** * 订单创建频率,单位:秒 */ private Integer createFrequencySeconds; public Integer getPayTimeoutSeconds() { return payTimeoutSeconds; } public OrderProperties setPayTimeoutSeconds(Integer payTimeoutSeconds) { this.payTimeoutSeconds = payTimeoutSeconds; return this; } public Integer getCreateFrequencySeconds() { return createFrequencySeconds; } public OrderProperties setCreateFrequencySeconds(Integer createFrequencySeconds) { this.createFrequencySeconds = createFrequencySeconds; return this; } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx29.userapplication.controller; import cn.iocoder.springcloud.labx29.userapplication.config.OrderProperties; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.Map; @RestController @RequestMapping("/demo") @RefreshScope public class DemoController { @Autowired private OrderProperties orderProperties; /** * 测试 @ConfigurationProperties 注解的配置属性类 */ @GetMapping("/test01") public OrderProperties test01() { return orderProperties; } @Value(value = "${order.pay-timeout-seconds}") private Integer payTimeoutSeconds; @Value(value = "${order.create-frequency-seconds}") private Integer createFrequencySeconds; /** * 测试 @Value 注解的属性 */ @GetMapping("/test02") public Map test02() { Map result = new HashMap<>(); result.put("payTimeoutSeconds", payTimeoutSeconds); result.put("createFrequencySeconds", createFrequencySeconds); return result; } } ================================================ FILE: labx-29-spring-cloud-consul-bus/labx-29-sc-config-user-application-auto-refresh-by-bus/src/main/resources/application.yml ================================================ spring: cloud: # Consul consul: host: 127.0.0.1 port: 8500 server: port: 8081 ================================================ FILE: labx-29-spring-cloud-consul-bus/labx-29-sc-config-user-application-auto-refresh-by-bus/src/main/resources/bootstrap.yml ================================================ spring: application: name: user-application cloud: # Spring Cloud Config Client 配置项,对应 ConfigClientProperties 类 config: uri: http://127.0.0.1:8888 # Spring Cloud Config Server 的地址 name: ${spring.application.name} # 读取的配置文件的名字,默认为 ${spring.application.name} ================================================ FILE: labx-29-spring-cloud-consul-bus/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-29-spring-cloud-consul-bus pom labx-29-sc-bus-consul-demo-publisher labx-29-sc-bus-consul-demo-listener labx-29-sc-bus-consul-demo-listener-actuator labx-29-sc-config-server-git-auto-refresh-by-bus labx-29-sc-config-user-application-auto-refresh-by-bus ================================================ FILE: labx-29-spring-cloud-consul-bus/《芋道 Spring Cloud 消息总线 Bus Consul 入门》.md ================================================ ================================================ FILE: labx-30-spring-cloud-grpc/labx-30-grpc-cloud/labx-30-grpc-cloud-application/pom.xml ================================================ labx-30-grpc-cloud cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-30-grpc-cloud-application 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE 1.8 1.8 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import cn.iocoder.springboot.labs labx-30-grpc-cloud-user-service-api 1.0-SNAPSHOT org.springframework.boot spring-boot-starter-web net.devh grpc-client-spring-boot-starter 2.8.0.RELEASE com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery ================================================ FILE: labx-30-spring-cloud-grpc/labx-30-grpc-cloud/labx-30-grpc-cloud-application/src/main/java/cn/iocoder/springcloud/labx30/demo/DemoApplication.java ================================================ package cn.iocoder.springcloud.labx30.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { // 启动 Spring Boot 应用 SpringApplication.run(DemoApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx30.demo.controller; import cn.iocoder.springcloud.labx30.userservice.api.*; import net.devh.boot.grpc.client.inject.GrpcClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo") public class DemoController { @GrpcClient("user-service") private UserServiceGrpc.UserServiceBlockingStub userServiceGrpc; @GetMapping("/get") public String get(@RequestParam("id") Integer id) { // 创建请求 UserGetRequest request = UserGetRequest.newBuilder().setId(id).build(); // 执行 gRPC 请求 UserGetResponse response = userServiceGrpc.get(request); // 响应 return response.getName(); } @GetMapping("/create") // 为了方便测试,实际使用 @PostMapping public Integer create(@RequestParam("name") String name, @RequestParam("gender") Integer gender) { // 创建请求 UserCreateRequest request = UserCreateRequest.newBuilder() .setName(name).setGender(gender).build(); // 执行 gRPC 请求 UserCreateResponse response = userServiceGrpc.create(request); // 响应 return response.getId(); } } ================================================ FILE: labx-30-spring-cloud-grpc/labx-30-grpc-cloud/labx-30-grpc-cloud-application/src/main/resources/application.yml ================================================ spring: application: name: demo-application # 应用名 cloud: nacos: # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 grpc: # gRPC 客户端配置,对应 GrpcChannelsProperties 配置类的映射 client: user-service: negotiation-type: plaintext ================================================ FILE: labx-30-spring-cloud-grpc/labx-30-grpc-cloud/labx-30-grpc-cloud-user-service/pom.xml ================================================ labx-30-grpc-cloud cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-30-grpc-cloud-user-service 2.2.4.RELEASE Hoxton.SR1 2.2.0.RELEASE 1.8 1.8 org.springframework.boot spring-boot-starter-parent ${spring.boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import cn.iocoder.springboot.labs labx-30-grpc-cloud-user-service-api 1.0-SNAPSHOT org.springframework.boot spring-boot-starter-web net.devh grpc-server-spring-boot-starter 2.8.0.RELEASE com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx30.userservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class UserServiceApplication { public static void main(String[] args) { // 启动 Spring Boot 应用 SpringApplication.run(UserServiceApplication.class, args); } } ================================================ FILE: 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 ================================================ package cn.iocoder.springcloud.labx30.userservice.rpc; import cn.iocoder.springcloud.labx30.userservice.api.*; import io.grpc.stub.StreamObserver; import net.devh.boot.grpc.server.service.GrpcService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @GrpcService public class UserServiceGrpcImpl extends UserServiceGrpc.UserServiceImplBase { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public void get(UserGetRequest request, StreamObserver responseObserver) { logger.info("[get]"); // 创建响应对象 UserGetResponse.Builder builder = UserGetResponse.newBuilder(); builder.setId(request.getId()) .setName("没有昵称:" + request.getId()) .setGender(request.getId() % 2 + 1); // 返回响应 responseObserver.onNext(builder.build()); responseObserver.onCompleted(); } @Override public void create(UserCreateRequest request, StreamObserver responseObserver) { logger.info("[create]"); // 创建响应对象 UserCreateResponse.Builder builder = UserCreateResponse.newBuilder(); builder.setId((int) (System.currentTimeMillis() / 1000)); // 返回响应 responseObserver.onNext(builder.build()); responseObserver.onCompleted(); } } ================================================ FILE: labx-30-spring-cloud-grpc/labx-30-grpc-cloud/labx-30-grpc-cloud-user-service/src/main/resources/application.yml ================================================ spring: application: name: user-service # 应用名 cloud: nacos: # Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类 discovery: server-addr: 127.0.0.1:8848 # Nacos 服务器地址 grpc: # gRPC 服务器配置,对应 GrpcServerProperties 配置类 server: port: 0 # gRPC Server 随机端口 server: port: 0 # Web Server 随机端口 ================================================ FILE: labx-30-spring-cloud-grpc/labx-30-grpc-cloud/labx-30-grpc-cloud-user-service-api/pom.xml ================================================ labx-30-grpc-cloud cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-30-grpc-cloud-user-service-api 1.30.0 1.8 1.8 1.6.2 0.6.1 io.grpc grpc-protobuf ${io.grpc.version} io.grpc grpc-stub ${io.grpc.version} kr.motd.maven os-maven-plugin ${os-maven-plugin.version} org.xolstice.maven.plugins protobuf-maven-plugin ${protobuf-maven-plugin.version} grpc-java com.google.protobuf:protoc:3.9.1:exe:${os.detected.classifier} io.grpc:protoc-gen-grpc-java:${io.grpc.version}:exe:${os.detected.classifier} compile compile-custom ================================================ FILE: labx-30-spring-cloud-grpc/labx-30-grpc-cloud/labx-30-grpc-cloud-user-service-api/src/main/proto/UserService.proto ================================================ syntax = "proto3"; option java_multiple_files = true; package cn.iocoder.springcloud.labx30.userservice.api; message UserGetRequest { int32 id = 1; } message UserGetResponse { int32 id = 1; string name = 2; int32 gender = 3; } message UserCreateRequest { string name = 1; int32 gender = 2; } message UserCreateResponse { int32 id = 1; } service UserService { rpc get(UserGetRequest) returns (UserGetResponse); rpc create(UserCreateRequest) returns (UserCreateResponse); } ================================================ FILE: labx-30-spring-cloud-grpc/labx-30-grpc-cloud/pom.xml ================================================ labx-30-spring-cloud-grpc cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-30-grpc-cloud pom labx-30-grpc-cloud-user-service labx-30-grpc-cloud-user-service-api labx-30-grpc-cloud-application ================================================ FILE: labx-30-spring-cloud-grpc/pom.xml ================================================ labs-parent cn.iocoder.springboot.labs 1.0-SNAPSHOT 4.0.0 labx-30-spring-cloud-grpc pom labx-30-grpc-cloud ================================================ FILE: labx-30-spring-cloud-grpc/《芋道 Spring Cloud gRPC 入门》.md ================================================ ================================================ FILE: pom.xml ================================================ 4.0.0 cn.iocoder.springboot.labs labs-parent pom 1.0-SNAPSHOT lab-72-minio