學習Spring5 WebFlux這一篇就夠了
目錄
配套資料,免費下載
連結:https://pan.baidu.com/s/1gsHGUjRI8nPe_Gv1bZ7BMg
提取碼:5la2
複製這段內容後開啟百度網盤手機App,操作更方便哦
第九章 WebFlux
9.1、WebFlux的概述
WebFlux
Spring框架中包含的原始Web框架Spring WebMVC是專門為Servlet API和Servlet容器而構建的。響應式Web框架Spring WebFlux是在Spring 5.0以後新增的新的模組。WebFlux是一種非同步非阻塞的框架,非同步非阻塞的框架在 Servlet3.1 以後才支援,核心是基於 Reactor 的相關API實現的。WebFlux能夠在有限資源下,提高系統吞吐量和伸縮性,並以 Reactor 為基礎實現響應式程式設計,可在Netty,Undertow和Servlet 3.1+容器等伺服器上執行。這兩個Web框架都反映了其源模組的名稱(spring-webmvc和 spring-webflux),並在Spring Framework中並存。每個模組都是可選的,應用程式可以使用一個模組,也可以使用兩個模組。
非同步非阻塞
要想解釋清楚非同步非阻塞,我們就得明白,非同步和同步、阻塞和非阻塞之間的關係。
- 非同步和同步針對呼叫者,呼叫者傳送請求,如果等著對方回應之後才去做其他事情就是同步,如果傳送請求之後不等著對方回應就去做其他事情就是非同步
- 阻塞和非阻塞針對被呼叫者,被呼叫者收到請求之後,做完請求任務之後才給出反饋就是阻塞,收到請求之後馬上給出反饋然後再去做事情就是非阻塞
瞭解了非同步非阻塞,我們來說一說Spring WebMVC 和 Spring WebFlux分別屬於哪種?
【spring-webmvc + Servlet + Tomcat】命令式的、同步阻塞的
【spring-webflux + Reactor + Netty】響應式的、非同步非阻塞的
響應式程式設計
響應式程式設計是一種面向資料流和變化傳播的程式設計正規化。這意味著可以在程式語言中很方便地表達靜態或動態的資料流,而相關的計算模型會自動將變化的值通過資料流進行傳播。電子表格程式就是響應式程式設計的一個例子。單元格可以包含字面值或類似"=B1+C1"的公式,而包含公式的單元格的值會依據其他單元格的值的變化而變化。
Reactive 和 Reactor
Reactor 是基於Reactive Streams 規範的第四代響應庫,用於在JVM上構建非阻塞的應用程式。Reactor是Spring WebFlux的首選響應庫。它提供了 Mono和 Flux API型別,並通過豐富運算子集來處理0…1(Mono)和0…N(Flux)資料序列。因此,我們瞭解到 Reactive 是一種響應式程式設計的規範,而 Reactor 是此規範的一種具體實現,他是支撐 WebFlux 實現響應式程式設計的基礎。
到底用Spring WebMVC還是Spring WebFlux?
一個自然的問題要問,但建立了不合理的二分法。實際上,兩者共同努力擴大了可用選項的範圍。兩者的設計旨在實現彼此的連續性和一致性,它們可以並行使用,並且來自雙方的反饋對雙方都有利。下圖顯示了兩者之間的關係,它們的共同點以及各自的獨特支援:
我們建議您考慮以下幾點:
-
如果您有執行正常的Spring MVC應用程式,則無需更改。指令式程式設計是編寫,理解和除錯程式碼的最簡單方法。您有最大的庫選擇空間,因為從歷史上看,大多數庫都是阻塞的。
-
如果您已經在購買無阻塞的Web堆疊,Spring WebFlux可以提供與該領域其他伺服器相同的執行模型優勢,還可以選擇伺服器(Netty,Tomcat,Jetty,Undertow和Servlet 3.1+容器),選擇程式設計模型(帶註釋的控制器和功能性Web端點),以及選擇反應式庫(Reactor,RxJava或其他)。
-
如果您對與Java 8 lambda或Kotlin一起使用的輕量級功能性Web框架感興趣,則可以使用Spring WebFlux功能性Web端點。對於要求較低複雜性的較小應用程式或微服務(可以受益於更高的透明度和控制)而言,這也是一個不錯的選擇。
-
在微服務架構中,您可以混合使用帶有Spring MVC或Spring WebFlux控制器或帶有Spring WebFlux功能端點的應用程式。在兩個框架中都支援相同的基於註釋的程式設計模型,這使得重用知識變得更加容易,同時還為正確的工作選擇了正確的工具。
-
評估應用程式的一種簡單方法是檢查其依賴關係。如果您要使用阻塞性永續性API(JPA,JDBC)或網路API,則Spring MVC至少是通用體系結構的最佳選擇。使用Reactor和RxJava在單獨的執行緒上執行阻塞呼叫在技術上是可行的,但您不會充分利用非阻塞Web堆疊。
-
如果您的Spring MVC應用程式具有對遠端服務的呼叫,請嘗試使用active WebClient。您可以直接從Spring MVC控制器方法返回反應型別(Reactor,RxJava或其他)。每個呼叫的等待時間或呼叫之間的相互依賴性越大,好處就越明顯。Spring MVC控制器也可以呼叫其他反應式元件。
-
如果您有龐大的團隊,請牢記向無阻塞,功能性和宣告性程式設計的過渡過程中的學習曲線很陡。在沒有完全切換的情況下啟動的一種實用方法是使用WebClient。除此之外,從小處著手並衡量收益。我們希望,對於廣泛的應用程式,這種轉變是不必要的。如果不確定要尋找什麼好處,請先了解無阻塞I / O的工作原理(例如,單執行緒Node.js上的併發性)及其影響。
9.2、WebFlux的基礎
9.2.1、兩個核心類
響應式程式設計操作中,Reactor 是滿足 Reactive 規範的框架,Reactor 有兩個核心類,Mono 和 Flux,這兩個類實現介面 Publisher,提供豐富操作符。Flux 物件實現釋出者,返回 N 個元素;Mono 實現釋出者,返回 0 或者 1 個元素,Flux 和 Mono 都是資料流的釋出者,使用 Flux 和 Mono 都可以發出三種資料訊號: 元素值,錯誤訊號,完成訊號,錯誤訊號和完成訊號都代表終止訊號,終止訊號用於告訴訂閱者資料流結束了,錯誤訊號終止資料流同時把錯誤資訊傳遞給訂閱者。
三種訊號特點:
- 錯誤訊號和完成訊號都是終止訊號,不能共存
- 如果沒有傳送任何元素值,而是直接傳送錯誤或者完成訊號,表示是空資料流
- 如果沒有錯誤訊號,沒有完成訊號,表示是無限資料流
一個入門案例:
專案名稱: reactor-demo
引入依賴:
<!-- https://mvnrepository.com/artifact/io.projectreactor/reactor-core -->
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>3.4.1</version>
</dependency>
宣告資料流:
//第一種形式
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);
訂閱資料流: 呼叫 just 或者其他方法只是宣告資料流,資料流並沒有發出,只有進行訂閱之後才會觸發資料流,不訂閱什麼都不會發生的
//第一種形式
Flux.just(1, 2, 3, 4).subscribe(System.out::println);
Mono.just(1).subscribe(System.out::println);
9.2.2、四種操作符
操作符就是對資料進行一道又一道的操作,就好比生產車間的流水線,常見的操作符有四種:map、flatMap、filter、zipWith
map:將元素對映為一個新元素
flatMap:把每個元素轉換成流,把轉換之後多個流合併成大的流
filter:可以對元素進行篩選
zip:可以對元素進行合併
9.3、WebFlux的註解式程式設計模型
專案名稱: webflux-demo-01
pom.xml
<dependencies>
<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>
</dependency>
</dependencies>
application.properties
server.port=8080
com.caochenlei.webfluxdemo01.entity.User
public class User {
private Integer id;
private String name;
private String gender;
private Integer age;
public User() {
}
public User(Integer id, String name, String gender, Integer age) {
this.id = id;
this.name = name;
this.gender = gender;
this.age = age;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
com.caochenlei.webfluxdemo01.dao.UserDao
public interface UserDao {
//查詢一個使用者
public User findOne(Integer id);
//查詢全部使用者
public Collection<User> findAll();
//新增一個使用者
public void save(User user);
//刪除一個使用者
public void delete(User user);
//更新一個使用者
public void update(User user);
}
com.caochenlei.webfluxdemo01.dao.impl.UserDaoImpl
Repository
public class UserDaoImpl implements UserDao {
//模擬資料庫中的資料
private final Map<Integer, User> users = new ConcurrentHashMap<>();
//呼叫無參構造初始化
public UserDaoImpl() {
users.put(1, new User(1, "張三", "男", 18));
users.put(2, new User(2, "李四", "女", 19));
users.put(3, new User(3, "王五", "男", 20));
}
@Override
public User findOne(Integer id) {
return users.get(id);
}
@Override
public Collection<User> findAll() {
return users.values();
}
@Override
public void save(User user) {
int id = users.size() + 1;
user.setId(id);
users.put(id, user);
}
@Override
public void delete(User user) {
users.remove(user.getId());
}
@Override
public void update(User user) {
int id = user.getId();
users.remove(id);
users.put(id, user);
}
}
com.caochenlei.webfluxdemo01.service.UserService
public interface UserService {
//查詢一個使用者
public Mono<User> findOne(Integer id);
//查詢全部使用者
public Flux<User> findAll();
//新增一個使用者
public Mono<Void> save(Mono<User> userMono);
//刪除一個使用者
public Mono<Void> delete(Mono<User> userMono);
//更新一個使用者
public Mono<Void> update(Mono<User> userMono);
}
com.caochenlei.webfluxdemo01.service.UserServiceImpl
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public Mono<User> findOne(Integer id) {
return Mono.justOrEmpty(userDao.findOne(id));
}
@Override
public Flux<User> findAll() {
return Flux.fromIterable(userDao.findAll());
}
@Override
public Mono<Void> save(Mono<User> userMono) {
return userMono.doOnNext(user -> {
//儲存一個使用者
userDao.save(user);
}).thenEmpty(Mono.empty());
}
@Override
public Mono<Void> delete(Mono<User> userMono) {
return userMono.doOnNext(user -> {
//刪除一個使用者
userDao.delete(user);
}).thenEmpty(Mono.empty());
}
@Override
public Mono<Void> update(Mono<User> userMono) {
return userMono.doOnNext(user -> {
//更新一個使用者
userDao.update(user);
}).thenEmpty(Mono.empty());
}
}
com.caochenlei.webfluxdemo01.controller.UserController
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
//查詢一個使用者
@GetMapping("/findOne/{id}")
public Mono<User> findOne(@PathVariable Integer id) {
return userService.findOne(id);
}
//查詢全部使用者
@GetMapping("/findAll")
public Flux<User> findAll() {
return userService.findAll();
}
//新增一個使用者
@PostMapping("/save")
public Mono<Void> save(@RequestBody User user) {
Mono<User> userMono = Mono.just(user);
return userService.save(userMono);
}
//刪除一個使用者
@PostMapping("/delete")
public Mono<Void> delete(@RequestBody User user) {
Mono<User> userMono = Mono.just(user);
return userService.delete(userMono);
}
//更新一個使用者
@PostMapping("/update")
public Mono<Void> update(@RequestBody User user) {
Mono<User> userMono = Mono.just(user);
return userService.update(userMono);
}
}
com.caochenlei.webfluxdemo01.WebfluxDemo01Application,啟動主函式
@SpringBootApplication
public class WebfluxDemo01Application {
public static void main(String[] args) {
SpringApplication.run(WebfluxDemo01Application.class, args);
}
}
查詢一個:http://localhost:8080/user/findOne/1
查詢全部:http://localhost:8080/user/findAll
新增一個:http://localhost:8080/user/save
刪除一個:http://localhost:8080/user/delete
修改一個:http://localhost:8080/user/update
9.4、WebFlux的函數語言程式設計模型
專案名稱: webflux-demo-02
pom.xml
<dependencies>
<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>
</dependency>
</dependencies>
application.properties
server.port=8080
com.caochenlei.webfluxdemo01.entity.User
public class User {
private Integer id;
private String name;
private String gender;
private Integer age;
public User() {
}
public User(Integer id, String name, String gender, Integer age) {
this.id = id;
this.name = name;
this.gender = gender;
this.age = age;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
com.caochenlei.webfluxdemo01.dao.UserDao
public interface UserDao {
//查詢一個使用者
public User findOne(Integer id);
//查詢全部使用者
public Collection<User> findAll();
//新增一個使用者
public void save(User user);
//刪除一個使用者
public void delete(User user);
//更新一個使用者
public void update(User user);
}
com.caochenlei.webfluxdemo01.dao.impl.UserDaoImpl
Repository
public class UserDaoImpl implements UserDao {
//模擬資料庫中的資料
private final Map<Integer, User> users = new ConcurrentHashMap<>();
//呼叫無參構造初始化
public UserDaoImpl() {
users.put(1, new User(1, "張三", "男", 18));
users.put(2, new User(2, "李四", "女", 19));
users.put(3, new User(3, "王五", "男", 20));
}
@Override
public User findOne(Integer id) {
return users.get(id);
}
@Override
public Collection<User> findAll() {
return users.values();
}
@Override
public void save(User user) {
int id = users.size() + 1;
user.setId(id);
users.put(id, user);
}
@Override
public void delete(User user) {
users.remove(user.getId());
}
@Override
public void update(User user) {
int id = user.getId();
users.remove(id);
users.put(id, user);
}
}
com.caochenlei.webfluxdemo01.service.UserService
public interface UserService {
//查詢一個使用者
public Mono<User> findOne(Integer id);
//查詢全部使用者
public Flux<User> findAll();
//新增一個使用者
public Mono<Void> save(Mono<User> userMono);
//刪除一個使用者
public Mono<Void> delete(Mono<User> userMono);
//更新一個使用者
public Mono<Void> update(Mono<User> userMono);
}
com.caochenlei.webfluxdemo01.service.UserServiceImpl
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public Mono<User> findOne(Integer id) {
return Mono.justOrEmpty(userDao.findOne(id));
}
@Override
public Flux<User> findAll() {
return Flux.fromIterable(userDao.findAll());
}
@Override
public Mono<Void> save(Mono<User> userMono) {
return userMono.doOnNext(user -> {
//儲存一個使用者
userDao.save(user);
}).thenEmpty(Mono.empty());
}
@Override
public Mono<Void> delete(Mono<User> userMono) {
return userMono.doOnNext(user -> {
//刪除一個使用者
userDao.delete(user);
}).thenEmpty(Mono.empty());
}
@Override
public Mono<Void> update(Mono<User> userMono) {
return userMono.doOnNext(user -> {
//更新一個使用者
userDao.update(user);
}).thenEmpty(Mono.empty());
}
}
com.caochenlei.webfluxdemo01.handler.UserHandler
@Component
public class UserHandler {
@Autowired
private UserService userService;
//查詢一個使用者
public Mono<ServerResponse> findOne(ServerRequest request) {
//獲取id值
int id = Integer.valueOf(request.pathVariable("id"));
Mono<User> userMono = userService.findOne(id);
return ServerResponse
.ok()
.contentType(MediaType.APPLICATION_JSON)
.body(userMono, User.class);
}
//查詢全部使用者
public Mono<ServerResponse> findAll(ServerRequest request) {
Flux<User> userFlux = userService.findAll();
return ServerResponse
.ok()
.contentType(MediaType.APPLICATION_JSON)
.body(userFlux, User.class);
}
//新增一個使用者
public Mono<ServerResponse> save(ServerRequest request) {
//獲取user值
Mono<User> userMono = request.bodyToMono(User.class);
return ServerResponse.ok().build(userService.save(userMono));
}
//刪除一個使用者
public Mono<ServerResponse> delete(ServerRequest request) {
//獲取user值
Mono<User> userMono = request.bodyToMono(User.class);
return ServerResponse.ok().build(userService.delete(userMono));
}
//更新一個使用者
public Mono<ServerResponse> update(ServerRequest request) {
//獲取user值
Mono<User> userMono = request.bodyToMono(User.class);
return ServerResponse.ok().build(userService.update(userMono));
}
}
com.caochenlei.webfluxdemo01.routers.RouterConfig
@Configuration
public class RouterConfig {
@Bean
public RouterFunction<ServerResponse> userRouter(UserHandler userHandler) {
return RouterFunctions
.route(GET("/user/findOne/{id}").and(accept(MediaType.APPLICATION_JSON)), userHandler::findOne)
.andRoute(GET("/user/findAll").and(accept(MediaType.APPLICATION_JSON)), userHandler::findAll)
.andRoute(POST("/user/save").and(accept(MediaType.APPLICATION_JSON)), userHandler::save)
.andRoute(POST("/user/delete").and(accept(MediaType.APPLICATION_JSON)), userHandler::delete)
.andRoute(POST("/user/update").and(accept(MediaType.APPLICATION_JSON)), userHandler::update);
}
}
com.caochenlei.webfluxdemo01.WebfluxDemo02Application,啟動主函式
@SpringBootApplication
public class WebfluxDemo02Application {
public static void main(String[] args) {
SpringApplication.run(WebfluxDemo02Application.class, args);
}
}
查詢一個:http://localhost:8080/user/findOne/1
查詢全部:http://localhost:8080/user/findAll
新增一個:http://localhost:8080/user/save
刪除一個:http://localhost:8080/user/delete
修改一個:http://localhost:8080/user/update
9.5、WebFlux的WebClient程式設計
本章前提: 需要 webflux-demo-02 處於啟動狀態,我們需要手動程式設計實現訪問路由
專案名稱: webflux-demo-03
pom.xml
<dependencies>
<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>
</dependency>
</dependencies>
com.caochenlei.webfluxdemo03.entity.User
public class User {
private Integer id;
private String name;
private String gender;
private Integer age;
public User() {
}
public User(Integer id, String name, String gender, Integer age) {
this.id = id;
this.name = name;
this.gender = gender;
this.age = age;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", gender='" + gender + '\'' +
", age=" + age +
'}';
}
}
com.caochenlei.webfluxdemo03.WebClientDemo
public class WebClientDemo {
public static void main(String[] args) {
//建立Web客戶端
WebClient webClient = WebClient.create("http://localhost:8080");
//查詢指定使用者
User user = webClient
.get()
.uri("/user/findOne/{id}", 2)
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(User.class)
.block();
System.out.println(user);
//查詢所有使用者
Flux<User> userFlux = webClient
.get()
.uri("/user/findAll")
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToFlux(User.class);
userFlux.buffer().doOnNext(System.out::println).blockFirst();
//新增一個使用者
User user1 = new User(null, "趙六", "男", 16);
String returnValue1 = webClient
.post()
.uri("/user/save")
.body(Mono.just(user1), User.class)
.retrieve()
.bodyToMono(String.class)
.block();
System.out.println(returnValue1);
//刪除一個使用者
User user2 = new User();
user2.setId(4);
String returnValue2 = webClient
.post()
.uri("/user/delete")
.body(Mono.just(user2), User.class)
.retrieve()
.bodyToMono(String.class)
.block();
System.out.println(returnValue2);
//修改一個使用者
User user3 = user;
user3.setName("李思思");
String returnValue3 = webClient
.post()
.uri("/user/update")
.body(Mono.just(user3), User.class)
.retrieve()
.bodyToMono(String.class)
.block();
System.out.println(returnValue3);
}
}
相關文章
- 學習JDBC這一篇就夠了JDBC
- 學習MySQL這一篇就夠了MySql
- 學習Jmeter,這一篇就夠了JMeter
- 學習git這一篇就夠了!!!Git
- 學Nginx,這一篇就夠了Nginx
- 學習Hibernate5這一篇就夠了
- 學習AIDL,這一篇文章就夠了AI
- 學習Hibernate5 JPA這一篇就夠了
- 學習HTML5 Canvas這一篇文章就夠了HTMLCanvas
- iOS 動畫詳解(學習動畫看這一篇就夠了)iOS動畫
- APP瘦身這一篇就夠了APP
- 深入Babel,這一篇就夠了Babel
- Git 看這一篇就夠了Git
- 【詳細圖解】學習佇列,看這一篇就夠了!圖解佇列
- rodert教你學FFmpeg實戰這一篇就夠了
- Ubuntu搭建Pytorch,就這一篇就夠了UbuntuPyTorch
- Flutter DataTable 看這一篇就夠了Flutter
- Git【入門】這一篇就夠了Git
- 代理模式看這一篇就夠了模式
- MySQL查詢這一篇就夠了MySql
- Java 集合看這一篇就夠了Java
- [譯] 理解CORS這一篇就夠了CORS
- AJAX入門這一篇就夠了
- html css js(這一篇就夠了)HTMLCSSJS
- 學習Less-看這篇就夠了
- [收藏]學習sed,看這篇就夠了
- 學Mybatis,入門看這一篇就夠你學的了!MyBatis
- 學Redis這篇就夠了Redis
- c語言入門這一篇就夠了-學習筆記(一萬字)C語言筆記
- 瞭解 MongoDB 看這一篇就夠了MongoDB
- 關於SwiftUI,看這一篇就夠了SwiftUI
- 瞭解HandlerThread這一篇就夠了thread
- 這一篇就夠了——APP瘦身總結APP
- Spring入門這一篇就夠了Spring
- 入門Hbase,看這一篇就夠了
- jQuery入門看這一篇就夠了jQuery
- Elasticsearch入門,看這一篇就夠了Elasticsearch
- 關於MyBatis框架這一篇就夠了MyBatis框架