├── .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 | --------------------------------------------------------------------------------