Spring零基礎入門WebFlux響應式編程
說明:基于atguigu學習筆記。
簡介
Webflux是 Spring5 添加新的模塊,用于 web 開發(fā)的,功能和 SpringMVC 類似的,Webflux 使用當前一種比較流程響應式編程出現的框架。
使用傳統(tǒng) web 框架,比如 SpringMVC,這些基于 Servlet 容器,Webflux 是一種異步非阻
塞的框架,異步非阻塞的框架在 Servlet3.1 以后才支持,核心是基于 Reactor 的相關 API 實現
的。
Webflux 特點:
- 非阻塞式:在有限資源下,提高系統(tǒng)吞吐量和伸縮性,以 Reactor 為基礎實現響應式編程
- 函數式編程:Spring5 框架基于 java8,Webflux 使用 Java8 函數式編程方式實現路由請求
比較 SpringMVC:
第一:兩個框架都可以使用注解方式,都運行在 Tomet 等容器中
第二:SpringMVC 采用命令式編程,Webflux 采用異步響應式編程
響應式編程
響應式編程是一種面向數據流和變化傳播的編程范式。這意味著可以在編程語言中很方便地表達靜態(tài)或動態(tài)的數據流,而相關的計算模型會自動將變化的值通過數據流進行傳播。電子表格程序就是響應式編程的一個例子。單元格可以包含字面值或類似"=B1+C1"的公式,而包含公式的單元格的值會依據其他單元格的值的變化而變化
Reactor
Reactor 框架是 Pivotal 基于 Reactive Programming 思想實現的。
Reactor 有兩個核心類,Mono 和 Flux,這兩個類實現接口 Publisher,提供豐富操作
符。Flux 對象實現發(fā)布者,返回 N 個元素;Mono 實現發(fā)布者,返回 0 或者 1 個元素。
信號
Flux 和 Mono 都是數據流的發(fā)布者,使用 Flux 和 Mono 都可以發(fā)出三種數據信號:
元素值,錯誤信號,完成信號,錯誤信號和完成信號都代表終止信號,終止信號用于告訴
訂閱者數據流結束了,錯誤信號終止數據流同時把錯誤信息傳遞給訂閱者。
示例:
1.引入依賴
<dependency> <groupId>io.projectreactor</groupId> <artifactId>reactor-core</artifactId> <version>3.1.5.RELEASE</version> </dependency>
2.代碼
package com.example.springdemo3; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Test01 { public static void main(String[] args) { //just 方法直接聲明 Flux.just(1,2,3,4); Mono.just(1); //其他的方法 Integer[] array = {1,2,3,4}; Flux.fromArray(array); List<Integer> list = Arrays.asList(array); Flux.fromIterable(list); Stream<Integer> stream = list.stream(); Flux.fromStream(stream); } }
三種信號特點
- 錯誤信號和完成信號都是終止信號,不能共存的
- 如果沒有發(fā)送任何元素值,而是直接發(fā)送錯誤或者完成信號,表示是空數據流
- 如果沒有錯誤信號,沒有完成信號,表示是無限數據流
調用 just 或者其他方法只是聲明數據流,數據流并沒有發(fā)出,只有進行訂閱之后才會觸
發(fā)數據流,不訂閱什么都不會發(fā)生。示例如下:
package com.example.springdemo3; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Test01 { public static void main(String[] args) { //just 方法直接聲明 Flux.just(1,2,3,4).subscribe(System.out::println); Mono.just(1).subscribe(System.out::println);; } }
操作符
操作符對數據流進行一道道操作,成為操作符,比如工廠流水線。有以下兩個操作符:
1.map
將一個數據流里的每個元素映射為新元素,返回一個新的流。
2.flatMap
把每個元素轉換成數據流,把轉換之后多個流合并一個大的數據流
SpringWebflux執(zhí)行流程和API
SpringWebflux 基于 Reactor,默認使用容器是 Netty,Netty 是高性能的 NIO 框架,異步非阻
塞的框架。
SpringWebflux 執(zhí)行過程和 SpringMVC 相似的,SpringWebflux 核心控制器 DispatchHandler,實現了接口 WebHandler。WebHandler的源碼如下:
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package org.springframework.web.server; import reactor.core.publisher.Mono; public interface WebHandler { Mono<Void> handle(ServerWebExchange var1); }
可以看到只有一個方法WebHandler,WebHandler的實現在DispatcherHandler類中,實現邏輯如下:
public Mono<Void> handle(ServerWebExchange exchange) { return this.handlerMappings == null ? this.createNotFoundError() : Flux.fromIterable(this.handlerMappings).concatMap((mapping) -> { return mapping.getHandler(exchange); }).next().switchIfEmpty(this.createNotFoundError()).flatMap((handler) -> { return this.invokeHandler(exchange, handler); }).flatMap((result) -> { return this.handleResult(exchange, result); }); }
其中,參數ServerWebExchange是放http請求和響應信息;getHandler根據請求地址獲取對應的mapping;invokeHandler調用具體的業(yè)務方法;handleResult處理結果返回。
SpringWebflux 里面 的DispatcherHandler,有3個很重要的屬性,如下:
public class DispatcherHandler implements WebHandler, ApplicationContextAware { @Nullable private List<HandlerMapping> handlerMappings; @Nullable private List<HandlerAdapter> handlerAdapters; @Nullable private List<HandlerResultHandler> resultHandlers; }
HandlerMapping:請求查詢到處理的方法; HandlerAdapter:真正負責請求處理;HandlerResultHandler:響應結果處理。
SpringWebflux 實現函數式編程,兩個接口:RouterFunction(路由處理)
和 HandlerFunction(處理函數)。
注解實現SpringWebflux
使用注解編程模型方式,和 SpringMVC 使用相似的,只需要把相關依賴配置到項目中,
SpringBoot 自動配置相關運行容器,默認情況下使用 Netty 服務器。
1.創(chuàng)建項目
創(chuàng)建springboot項目,引入依賴:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>spring-demo3</artifactId> <version>0.0.1-SNAPSHOT</version> <name>spring-demo3</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <spring-boot.version>2.3.7.RELEASE</spring-boot.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>io.projectreactor</groupId> <artifactId>reactor-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>2.3.7.RELEASE</version> <configuration> <mainClass>com.example.springdemo3.SpringDemo3Application</mainClass> </configuration> <executions> <execution> <id>repackage</id> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
2.創(chuàng)建包和相關類
實體類:
package com.example.springdemo3.entity; import lombok.AllArgsConstructor; import lombok.Data; @Data @AllArgsConstructor public class User { private String name; private String gender; private Integer age; }
service接口:
package com.example.springdemo3.service; import com.example.springdemo3.entity.User; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; public interface UserService { //根據 id 查詢用戶 Mono<User> getUserById(int id); //查詢所有用戶 Flux<User> getAllUser(); //添加用戶 Mono<Void> saveUserInfo(Mono<User> user); }
service實現類
package com.example.springdemo3.service.impl; import com.example.springdemo3.entity.User; import com.example.springdemo3.service.UserService; import org.springframework.stereotype.Service; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.util.HashMap; import java.util.Map; @Service public class UserServiceImpl implements UserService { //創(chuàng)建 map 集合存儲數據 private final Map<Integer,User> users = new HashMap<>(); public UserServiceImpl() { this.users.put(1,new User("lucy","nan",20)); this.users.put(2,new User("mary","nv",30)); this.users.put(3,new User("jack","nv",50)); } @Override public Mono<User> getUserById(int id) { return Mono.justOrEmpty(this.users.get(id)); } @Override public Flux<User> getAllUser() { return Flux.fromIterable(this.users.values()); } @Override public Mono<Void> saveUserInfo(Mono<User> userMono) { return userMono.doOnNext(person -> { //向 map 集合里面放值 int id = users.size()+1; users.put(id,person); }).thenEmpty(Mono.empty()); // Mono.empty()是終止信號 } }
注意這里沒有真正和數據庫交互,而是維護了一個數組,模擬數據庫。
controller類:
package com.example.springdemo3.controller; import com.example.springdemo3.entity.User; import com.example.springdemo3.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.*; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @Component @RestController public class UserController { //注入 service @Autowired private UserService userService; //id 查詢 @GetMapping("/user/{id}") public Mono<User> geetUserId(@PathVariable int id) { return userService.getUserById(id); } //查詢所有 @GetMapping("/user") public Flux<User> getUsers() { return userService.getAllUser(); } //添加 @PostMapping("/saveuser") public Mono<Void> saveUser(@RequestBody User user) { Mono<User> userMono = Mono.just(user); return userService.saveUserInfo(userMono); } }
SpringMVC 方式實現,同步阻塞的方式,基于 SpringMVC+Servlet+Tomcat
SpringWebflux 方式實現,異步非阻塞 方式,基于 SpringWebflux+Reactor+Netty
到此這篇關于Spring零基礎入門WebFlux響應式編程的文章就介紹到這了,更多相關Spring WebFlux內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
maven依賴關系中的<scope>provided</scope>使用詳解
這篇文章主要介紹了maven依賴關系中的<scope>provided</scope>使用詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-07-07詳解SpringCloud Ribbon 負載均衡通過服務器名無法連接的神坑
這篇文章主要介紹了詳解SpringCloud Ribbon 負載均衡通過服務器名無法連接的神坑,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-06-06