├── .gitignore
├── LICENSE
├── README.md
├── pom.xml
└── src
└── main
├── java
└── com
│ └── solarwind
│ └── reactive
│ ├── Application.java
│ ├── controller
│ └── ReactiveController.java
│ ├── dao
│ └── UserRepository.java
│ ├── handler
│ └── ExampleHandler.java
│ ├── model
│ └── User.java
│ └── router
│ └── ExampleRouter.java
└── resources
└── application.yml
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | *.iml
3 | .project
4 | .settings
5 | .classpath
6 | target/
7 | output/
8 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Zhongyang MA
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # webflux-streaming-demo
2 | This project is a tryout of reactive application with Spring 5 WebFlux and mongoDB.
3 | For the purpose of learning, I wrote an overview article on reactive programming, covering from the basic concepts, new version tools, to the use demonstrations.
4 | This article can be regarded as my reading notes, and was posted on the [wiki](https://github.com/ZhongyangMA/webflux-streaming-demo/wiki) of this repository.
5 | - English version: [An Overview of Reactive Programming](https://zhongyangma.github.io/archivers/An-Overview-of-Reactive-Programming)
6 | - 中文版请见:[反应式编程概览(中文版)](https://github.com/ZhongyangMA/webflux-streaming-demo/wiki/%E5%8F%8D%E5%BA%94%E5%BC%8F%E7%BC%96%E7%A8%8B%E6%A6%82%E8%A7%88%EF%BC%88%E4%B8%AD%E6%96%87%E7%89%88%EF%BC%89)
7 |
8 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.solarwind
8 | webflux-streaming-demo
9 | 1.0-SNAPSHOT
10 | jar
11 |
12 | webflux-streaming-demo
13 | webflux streaming demo
14 |
15 |
16 | org.springframework.boot
17 | spring-boot-starter-parent
18 | 2.0.0.RELEASE
19 |
20 |
21 |
22 |
23 | UTF-8
24 | UTF-8
25 | 1.8
26 |
27 |
28 |
29 |
30 | org.springframework.boot
31 | spring-boot-starter-webflux
32 |
33 |
34 | org.springframework.boot
35 | spring-boot-starter-data-mongodb-reactive
36 |
37 |
38 |
39 |
40 |
41 |
42 | org.springframework.boot
43 | spring-boot-maven-plugin
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/src/main/java/com/solarwind/reactive/Application.java:
--------------------------------------------------------------------------------
1 | package com.solarwind.reactive;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | /**
7 | * Created by IntelliJ IDEA.
8 | * User: Zhongyang MA
9 | * Date: 2018/4/4
10 | * Time: 10:42
11 | */
12 | @SpringBootApplication
13 | public class Application {
14 | public static void main(String[] args) {
15 | SpringApplication.run(Application.class, args);
16 | }
17 | }
18 |
19 |
--------------------------------------------------------------------------------
/src/main/java/com/solarwind/reactive/controller/ReactiveController.java:
--------------------------------------------------------------------------------
1 | package com.solarwind.reactive.controller;
2 |
3 | import com.solarwind.reactive.handler.ExampleHandler;
4 | import com.solarwind.reactive.model.User;
5 | import javafx.util.Pair;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.http.MediaType;
8 | import org.springframework.web.bind.annotation.GetMapping;
9 | import org.springframework.web.bind.annotation.PathVariable;
10 | import org.springframework.web.bind.annotation.RequestMapping;
11 | import org.springframework.web.bind.annotation.RestController;
12 | import reactor.core.publisher.Flux;
13 | import reactor.core.publisher.Mono;
14 |
15 | /**
16 | * Created by IntelliJ IDEA.
17 | * User: Zhongyang MA
18 | * Date: 2018/4/4
19 | * Time: 12:07
20 | */
21 | @RestController
22 | @RequestMapping("/annotated")
23 | public class ReactiveController {
24 |
25 | @Autowired
26 | private ExampleHandler exampleHandler;
27 |
28 | // Example of returning a simple string to your browser
29 | @GetMapping("/test1")
30 | public Mono test1() {
31 | return exampleHandler.test1();
32 | }
33 |
34 | // The server will push messages line by line to your browser - scrolling effect on your browser
35 | @GetMapping(value = "/test2", produces = MediaType.APPLICATION_STREAM_JSON_VALUE)
36 | public Flux test2() {
37 | return exampleHandler.test2();
38 | }
39 |
40 | // Get all users from reactive mongoDB
41 | @GetMapping(value = "/user/list", produces = "application/json")
42 | public Flux listAll() {
43 | return exampleHandler.findAll();
44 | }
45 |
46 | // Find items by gender from user collection in reactive mongoDB
47 | @GetMapping(value = "/user/{gender}")
48 | public Flux findByGender(@PathVariable("gender") String gender) {
49 | return exampleHandler.findByGender(gender);
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/java/com/solarwind/reactive/dao/UserRepository.java:
--------------------------------------------------------------------------------
1 | package com.solarwind.reactive.dao;
2 |
3 | import com.solarwind.reactive.model.User;
4 | import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
5 | import reactor.core.publisher.Flux;
6 |
7 | /**
8 | * Created by IntelliJ IDEA.
9 | * User: Zhongyang MA
10 | * Date: 2018/4/13
11 | * Time: 16:03
12 | */
13 | public interface UserRepository extends ReactiveMongoRepository {
14 |
15 | Flux findByGender(String gender); // function name will automatically match the corresponding field in User
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/com/solarwind/reactive/handler/ExampleHandler.java:
--------------------------------------------------------------------------------
1 | package com.solarwind.reactive.handler;
2 |
3 | import com.solarwind.reactive.dao.UserRepository;
4 | import com.solarwind.reactive.model.User;
5 | import javafx.util.Pair;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.http.MediaType;
8 | import org.springframework.stereotype.Component;
9 | import org.springframework.web.reactive.function.BodyInserters;
10 | import org.springframework.web.reactive.function.server.ServerRequest;
11 | import org.springframework.web.reactive.function.server.ServerResponse;
12 | import reactor.core.publisher.Flux;
13 | import reactor.core.publisher.Mono;
14 | import java.text.SimpleDateFormat;
15 | import java.time.Duration;
16 | import java.util.Date;
17 |
18 | /**
19 | * Created by IntelliJ IDEA.
20 | * User: Zhongyang MA
21 | * Date: 2018/4/11
22 | * Time: 17:28
23 | */
24 | @Component
25 | public class ExampleHandler {
26 |
27 | @Autowired
28 | private UserRepository userRepository;
29 |
30 | /**
31 | * for annotated controllers
32 | */
33 | public Mono test1() {
34 | return Mono.just("test1: return a simple string");
35 | }
36 |
37 | public Flux test2() {
38 | Flux flux = Flux
39 | .interval(Duration.ofMillis(1000))
40 | .map(i -> {
41 | String now = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
42 | return new Pair(now, i);
43 | });
44 | return flux;
45 | }
46 |
47 | public Flux findAll() {
48 | return userRepository.findAll();
49 | }
50 |
51 | public Flux findByGender(String gender) {
52 | return userRepository.findByGender(gender);
53 | }
54 |
55 |
56 |
57 |
58 | /**
59 | * below for functional routers
60 | */
61 | public Mono funcTest1(ServerRequest request) {
62 | return ServerResponse.ok()
63 | .contentType(MediaType.TEXT_PLAIN)
64 | .body(BodyInserters.fromObject("funcTest1: WebFlux functional router."));
65 | }
66 |
67 | public Mono funcTest2(ServerRequest request) {
68 | return ServerResponse.ok()
69 | .contentType(MediaType.TEXT_PLAIN)
70 | .body(BodyInserters.fromObject("funcTest2: WebFlux functional router."));
71 | }
72 |
73 | public Mono findAll(ServerRequest request) {
74 | return ServerResponse.ok()
75 | .contentType(MediaType.APPLICATION_JSON)
76 | .body(userRepository.findAll(), User.class);
77 | }
78 |
79 | public Mono findByGender(ServerRequest request) {
80 | return ServerResponse.ok()
81 | .contentType(MediaType.APPLICATION_JSON)
82 | .body(userRepository.findByGender(request.pathVariable("gender")), User.class);
83 | }
84 |
85 | }
86 |
--------------------------------------------------------------------------------
/src/main/java/com/solarwind/reactive/model/User.java:
--------------------------------------------------------------------------------
1 | package com.solarwind.reactive.model;
2 |
3 | import org.springframework.data.annotation.Id;
4 | import org.springframework.data.mongodb.core.mapping.Document;
5 |
6 | /**
7 | * Created by IntelliJ IDEA.
8 | * User: Zhongyang MA
9 | * Date: 2018/4/13
10 | * Time: 15:44
11 | */
12 | @Document(collection = "user") // specify mongodb's "collection", which is equivalent to "table"
13 | public class User {
14 | @Id // define the primary key
15 | private String id;
16 | private String name;
17 | private String gender;
18 | private String phone;
19 |
20 | public User() {}
21 |
22 | public User(String id, String name, String gender, String phone) {
23 | this.id = id;
24 | this.name = name;
25 | this.gender = gender;
26 | this.phone = phone;
27 | }
28 |
29 | public void setId(String id) {
30 | this.id = id;
31 | }
32 |
33 | public String getId() {
34 | return id;
35 | }
36 |
37 | public void setName(String name) {
38 | this.name = name;
39 | }
40 |
41 | public String getName() {
42 | return name;
43 | }
44 |
45 | public void setGender(String gender) {
46 | this.gender = gender;
47 | }
48 |
49 | public String getGender() {
50 | return gender;
51 | }
52 |
53 | public void setPhone(String phone) {
54 | this.phone = phone;
55 | }
56 |
57 | public String getPhone() {
58 | return phone;
59 | }
60 |
61 | @Override
62 | public String toString() {
63 | return "{id:" + id + ", name:" + name + ", gender:" + gender + ", phone:" + phone + "}";
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/src/main/java/com/solarwind/reactive/router/ExampleRouter.java:
--------------------------------------------------------------------------------
1 | package com.solarwind.reactive.router;
2 |
3 | import com.solarwind.reactive.handler.ExampleHandler;
4 | import org.springframework.context.annotation.Bean;
5 | import org.springframework.context.annotation.Configuration;
6 | import org.springframework.http.MediaType;
7 | import org.springframework.web.reactive.function.server.RequestPredicates;
8 | import org.springframework.web.reactive.function.server.RouterFunction;
9 | import org.springframework.web.reactive.function.server.RouterFunctions;
10 | import org.springframework.web.reactive.function.server.ServerResponse;
11 |
12 | /**
13 | * Created by IntelliJ IDEA.
14 | * User: Zhongyang MA
15 | * Date: 2018/4/11
16 | * Time: 17:24
17 | */
18 | @Configuration
19 | public class ExampleRouter {
20 |
21 | @Bean
22 | public RouterFunction route1(ExampleHandler exampleHandler) {
23 | return RouterFunctions.route(RequestPredicates
24 | .GET("/functional/test1")
25 | .and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), exampleHandler::funcTest1);
26 | }
27 |
28 | @Bean
29 | public RouterFunction route2(ExampleHandler exampleHandler) {
30 | return RouterFunctions.route(RequestPredicates
31 | .GET("/functional/test2")
32 | .and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), exampleHandler::funcTest2);
33 | }
34 |
35 | @Bean
36 | public RouterFunction listAll(ExampleHandler exampleHandler) {
37 | return RouterFunctions.route(RequestPredicates
38 | .GET("/functional/user/list")
39 | .and(RequestPredicates.accept(MediaType.APPLICATION_JSON)), exampleHandler::findAll);
40 | }
41 |
42 | @Bean
43 | public RouterFunction findByGender(ExampleHandler exampleHandler) {
44 | return RouterFunctions.route(RequestPredicates
45 | .GET("/functional/user/{gender}")
46 | .and(RequestPredicates.accept(MediaType.APPLICATION_JSON)), exampleHandler::findByGender);
47 | }
48 |
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | server:
2 | port: 8090
3 |
4 | spring:
5 | data:
6 | mongodb:
7 | uri: mongodb://localhost:27017/dbname
8 |
9 |
--------------------------------------------------------------------------------