響應式編程生活案例:java
傳統形式:react
一羣人去餐廳吃飯,顧客1找服務員點餐,服務員把訂單交給後臺廚師,而後服務員等待,web
當後臺廚師作好飯,交給服務員,通過服務員再交給顧客1,依此類推,該服務員再招待顧客2。redis
服務員能夠理解爲服務器,服務器越多,可處理的顧客請求越多spring
響應式編程:數據庫
服務員記住到顧客1的要求,交給後臺廚師,再記住顧客2的要求,交給後臺廚師,依此類推編程
當廚師作好顧客1的飯,告知服務員,而後服務員把飯送到顧客1;設計模式
當廚師作好顧客2的飯,告知服務員,而後服務員把飯送到顧客2,依此類推數組
一系列的事件稱爲流,異步非阻塞,觀察者的設計模式服務器
代碼案例:
傳統:
int b=2;
int c=3
int a=b+c //a被賦值後,b和c的改變不會影響a
b=5;
響應式編程:
int b=2;
int c=3
int a=b+c
b=5;//此時a變化爲8,a會根據b、c的變化而變化
SpringBoot2.x的響應式編程基於Spring5;
而Spring5的響應式編程又基於Reactor和Netty、Spring WebFlux替代Spring MVC
響應式編程最大的核心是非阻塞,即後臺的每一步每一段都要作到非阻塞
好比使用MySQL做爲數據庫,因爲MySQL不提供響應式編程,因此會阻塞
所以響應式編程不該採用MySQL,應該使用非阻塞的NoSQL
Spring WebFlux有兩種風格:基於功能和基於註解的。基於註解很是接近Spring MVC模型,如如下示例所示:
@RestController @RequestMapping(「/ users」) public class MyRestController { @GetMapping(「/ {user}」) public Mono <User> getUser( @PathVariable Long user){ // ... } @GetMapping(「/ {user} / customers」) public Flux <Customer> getUserCustomers( @PathVariable Long user){ // ... } @DeleteMapping(「/ {user}」) public Mono <User> deleteUser( @PathVariable Long user){ // ... } }
第二種: 路由配置與請求的實際處理分開
@Configuration public class RoutingConfiguration { @Bean public RouterFunction <ServerResponse> monoRouterFunction(UserHandler userHandler){ return route(GET( 「/ {user}」).and(accept(APPLICATION_JSON)),userHandler :: getUser) .andRoute(GET(「/ {user} / customers」).and(accept(APPLICATION_JSON)),userHandler :: getUserCustomers) .andRoute(DELETE(「/ {user}」).and(accept(APPLICATION_JSON)),userHandler :: deleteUser); } } @Component public class UserHandler { public Mono <ServerResponse> getUser(ServerRequest request){ // ... } public Mono <ServerResponse> getUserCustomers(ServerRequest request){ // ... } public Mono <ServerResponse> deleteUser(ServerRequest request){ // ... } }
Spring WebFlux應用程序不嚴格依賴於Servlet API,所以它們不能做爲war文件部署,也不能使用src/main/webapp目錄
能夠整合多個模板引擎,除了REST外,您還可使用Spring WebFlux提供動態HTML內容
Spring WebFlux支持各類模板技術,包括Thymeleaf,FreeMarker
簡單的實戰:
依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency>
自動生成的SpringBoot項目還會有一個test依賴,可選
<dependency> <groupId>io.projectreactor</groupId> <artifactId>reactor-test</artifactId> <scope>test</scope> </dependency>
簡單的Controller:
package org.dreamtech.webflux.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Mono; @RestController public class TestController { @GetMapping("/test") public Mono<String> test() { return Mono.just("hello world"); } }
訪問http://localhost:8080/test,顯示hello world說明成功
這裏使用到了Mono,後邊還會用到Flux,他們的實現很複雜,但能夠簡單地理解:
User、List<User>
1)簡單業務而言:和其餘普通對象差異不大,複雜請求業務,就能夠提高性能
2)通俗理解:
Mono 表示的是包含 0 或者 1 個元素的異步序列
mono->單一對象 User
例如從redis根據用戶ID查到惟一的用戶,而後進行返回Mono<User>
Flux 表示的是包含 0 到 N 個元素的異步序列
flux->數組列表對象 List<User>
例如從redis根據條件:性別爲男性的用戶進行查找,而後返回Flux<User>
3)Flux 和 Mono 之間能夠進行轉換
進一步的使用
對User實體類實現增刪改查功能:
package org.dreamtech.webflux.domain; public class User { private String id; private String name; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public User(String id, String name) { super(); this.id = id; this.name = name; } }
Service:
package org.dreamtech.webflux.service; import java.util.Collection; import java.util.HashMap; import java.util.Map; import org.dreamtech.webflux.domain.User; import org.springframework.stereotype.Service; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @Service public class UserService { // 使用Map模擬數據庫 private static final Map<String, User> dataMap = new HashMap<String, User>(); static { dataMap.put("1", new User("1", "admin")); dataMap.put("2", new User("2", "John")); dataMap.put("3", new User("3", "Rose")); dataMap.put("4", new User("4", "James")); dataMap.put("5", new User("5", "Bryant")); } /** * 返回數據庫的全部用戶信息 * * @return */ public Flux<User> list() { Collection<User> list = UserService.dataMap.values(); return Flux.fromIterable(list); } /** * 根據用戶ID返回用戶信息 * * @param id 用戶ID * @return */ public Mono<User> getById(final String id) { return Mono.justOrEmpty(UserService.dataMap.get(id)); } /** * 根據用戶ID刪除用戶 * * @param id 用戶ID * @return */ public Mono<User> delete(final String id) { return Mono.justOrEmpty(UserService.dataMap.remove(id)); } }
Controller:
package org.dreamtech.webflux.controller; import org.dreamtech.webflux.domain.User; import org.dreamtech.webflux.service.UserService; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @RestController public class UserController { private final UserService userService; public UserController(final UserService userService) { this.userService = userService; } /** * 根據ID查找用戶 * * @param id 用戶ID * @return */ @GetMapping("/find") public Mono<User> findById(final String id) { return userService.getById(id); } /** * 得到用戶列表 * * @return */ @GetMapping("/list") public Flux<User> list() { return userService.list(); } /** * 根據ID刪除用戶 * * @param id 用戶ID * @return */ @GetMapping("/delete") public Mono<User> delete(final String id) { return userService.delete(id); } }
訪問定義的三個API,發現和SpringMVC基本沒有區別
因此,對返回進行延遲處理:
@GetMapping("/list") public Flux<User> list() { return userService.list().delayElements(Duration.ofSeconds(3)); }
只是這些設置的話,等待3*list.size秒後所有返回,要突出流的特色,須要進行配置:
@GetMapping(value = "/list", produces = MediaType.APPLICATION_STREAM_JSON_VALUE) public Flux<User> list() { return userService.list().delayElements(Duration.ofSeconds(3)); }
這時候訪問,能夠發現每過3秒返回一個對象信息
使用WebClient客戶端進行測試:
package org.dreamtech.webflux; import org.junit.Test; import org.springframework.http.MediaType; import org.springframework.web.reactive.function.client.WebClient; import reactor.core.publisher.Mono; public class WebClientTest { @Test public void test() { Mono<String> bodyMono = WebClient.create().get().uri("http://localhost:8080/find?id=3") .accept(MediaType.APPLICATION_JSON).retrieve().bodyToMono(String.class); System.out.println(bodyMono.block()); } }