Spring?Boot?整合?Reactor實例詳解
引言
Reactor 是一個完全非阻塞的 JVM 響應(yīng)式編程基礎(chǔ),有著高效的需求管理(背壓的形式)。它直接整合 Java8 的函數(shù)式 API,尤其是 CompletableFuture, Stream,還有 Duration 。提供了可組合的異步化序列 API — Flux (對于 [N] 個元素) and Mono (對于 [0|1] 元素) — 并廣泛實現(xiàn) 響應(yīng)式Stream 規(guī)范。
這次帶大家從零開始,使用 Spring Boot 框架建立一個 Reactor 響應(yīng)式項目。
1 創(chuàng)建項目
使用 ;https://start.spring.io/ 創(chuàng)建項目。添加依賴項:H2、Lombok、Spring Web、JPA、JDBC

然后導(dǎo)入 Reactor 包
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>2 集成 H2 數(shù)據(jù)庫
application.properties 文件中添加 H2 數(shù)據(jù)連接信息。此外,端口使用 8081(隨意,本地未被使用的端口即可)。
server.port=8081 ################ H2 數(shù)據(jù)庫 基礎(chǔ)配置 ############## spring.datasource.driverClassName=org.h2.Driver spring.datasource.url=jdbc:h2:~/user spring.datasource.username=sa spring.datasource.password= spring.jpa.database=h2 spring.jpa.hibernate.ddl-auto=update spring.h2.console.path=/h2-console spring.h2.console.enable=true
3 創(chuàng)建測試類
3.1 user 實體
建立簡單數(shù)據(jù)操作實體 User。
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
/**
* @Author: prepared
* @Date: 2022/8/29 21:40
*/
@Data
@NoArgsConstructor
@Table(name = "t_user")
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String userName;
private int age;
private String sex;
public User(String userName, int age, String sex) {
this.userName = userName;
this.age = age;
this.sex = sex;
}
}3.2 UserRepository
數(shù)據(jù)模型層使用 JPA 框架。
import com.prepared.user.domain.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
/**
* @Author: prepared
* @Date: 2022/8/29 21:45
*/
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
}3.3 UserService
service 增加兩個方法,add 方法,用來添加數(shù)據(jù);list 方法,用來查詢所有數(shù)據(jù)。所有接口返回 Mono/Flux 對象。
最佳實踐:所有的第三方接口、IO 耗時比較長的操作都可以放在 Mono 對象中。
doOnError 監(jiān)控異常情況;
doFinally 監(jiān)控整體執(zhí)行情況,如:耗時、調(diào)用量監(jiān)控等。
import com.prepared.user.dao.UserRepository;
import com.prepared.user.domain.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;
import javax.annotation.Resource;
import java.util.List;
/**
* @Author: prepared
* @Date: 2022/8/29 21:45
*/
@Service
public class UserService {
private Logger logger = LoggerFactory.getLogger(UserService.class);
@Resource
private UserRepository userRepository;
public Mono<Boolean> save(User user) {
long startTime = System.currentTimeMillis();
return Mono.fromSupplier(() -> {
return userRepository.save(user) != null;
})
.doOnError(e -> {
// 打印異常日志&增加監(jiān)控(自行處理)
logger.error("save.user.error, user={}, e", user, e);
})
.doFinally(e -> {
// 耗時 & 整體健康
logger.info("save.user.time={}, user={}", user, System.currentTimeMillis() - startTime);
});
}
public Mono<User> findById(Long id) {
long startTime = System.currentTimeMillis();
return Mono.fromSupplier(() -> {
return userRepository.getReferenceById(id);
}).doOnError(e -> {
// 打印異常日志&增加監(jiān)控(自行處理)
logger.error("findById.user.error, id={}, e", id, e);
})
.doFinally(e -> {
// 耗時 & 整體健康
logger.info("findById.user.time={}, id={}", id, System.currentTimeMillis() - startTime);
});
}
public Mono<List<User>> list() {
long startTime = System.currentTimeMillis();
return Mono.fromSupplier(() -> {
return userRepository.findAll();
}).doOnError(e -> {
// 打印異常日志&增加監(jiān)控(自行處理)
logger.error("list.user.error, e", e);
})
.doFinally(e -> {
// 耗時 & 整體健康
logger.info("list.user.time={}, ", System.currentTimeMillis() - startTime);
});
}
public Flux<User> listFlux() {
long startTime = System.currentTimeMillis();
return Flux.fromIterable(userRepository.findAll())
.doOnError(e -> {
// 打印異常日志&增加監(jiān)控(自行處理)
logger.error("list.user.error, e", e);
})
.doFinally(e -> {
// 耗時 & 整體健康
logger.info("list.user.time={}, ", System.currentTimeMillis() - startTime);
});
}
}3.4 UserController
controller 增加兩個方法,add 方法,用來添加數(shù)據(jù);list 方法,用來查詢所有數(shù)據(jù)。
list 方法還有另外一種寫法,這就涉及到 Mono 和 Flux 的不同了。
返回List可以使用Mono<List<User>> ,也可以使用 Flux<User>。
Mono<T>是一個特定的Publisher<T>,最多可以發(fā)出一個元素Flux<T>是一個標準的Publisher<T>,表示為發(fā)出 0 到 N 個元素的異步序列
import com.prepared.user.domain.User;
import com.prepared.user.service.UserService;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
/**
* @Author: prepared
* @Date: 2022/8/29 21:47
*/
@RestController
public class UserController {
@Resource
private UserService userService;
@RequestMapping("/add")
public Mono<Boolean> add() {
User user = new User("xiaoming", 10, "F");
return userService.save(user) ;
}
@RequestMapping("/list")
public Mono<List<User>> list() {
return userService.list();
}
}
@RequestMapping("/listFlux")
public Flux<User> listFlux() {
return userService.listFlux();
}3.5 SpringReactorApplication 添加注解支持
Application 啟動類添加注解 @EnableJpaRepositories
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
/**
* Hello world!
*/
@SpringBootApplication
@EnableJpaRepositories
public class SpringReactorApplication {
public static void main(String[] args) {
SpringApplication.run(SpringReactorApplication.class, args);
}
}測試
啟動項目,訪問 localhost:8081/add,正常返回 true。

查詢所有數(shù)據(jù),訪問localhost:8081/list,可以看到插入的數(shù)據(jù),已經(jīng)查詢出來了。PS:我這里執(zhí)行了多次 add,所以有多條記錄。

后臺日志:
2022-09-05 20:13:17.385 INFO 15696 --- [nio-8082-exec-2] com.prepared.user.service.UserService : list.user.time=181,
執(zhí)行了 UserService list() 方法的 doFinnally 代碼塊,打印耗時日志。
總結(jié)
響應(yīng)式編程的優(yōu)勢是不會阻塞。那么正常我們的代碼中有哪些阻塞的操作呢?
Future的get()方法;Reactor中的block()方法,subcribe()方法,所以在使用 Reactor 的時候,除非編寫測試代碼,否則不要直接調(diào)用以上兩個方法;- 同步方法調(diào)用,所以高并發(fā)情況下,會使用異步調(diào)用(如Future)來提升響應(yīng)速度
以上就是Spring Boot 整合 Reactor實例詳解的詳細內(nèi)容,更多關(guān)于Spring Boot 整合 Reactor的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringBoot實現(xiàn)自定義條件注解的代碼示例
在Spring Boot中,條件注解是一種非常強大的工具,它可以根據(jù)特定的條件來選擇是否加載某個類或某個Bean,文將介紹如何在Spring Boot中實現(xiàn)自定義條件注解,并提供一個示例代碼,需要的朋友可以參考下2023-06-06
Spring Boot集成 Spring Boot Admin 監(jiān)控
這篇文章主要介紹了Spring Boot集成 Spring Boot Admin 監(jiān)控,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08
MAC上IntelliJ IDEA的svn無法保存密碼解決方案
今天小編就為大家分享一篇關(guān)于MAC上IntelliJ IDEA的svn無法保存密碼解決方案,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2018-10-10
MyBatis實現(xiàn)簡單的數(shù)據(jù)表分月存儲
本文主要介紹了MyBatis實現(xiàn)簡單的數(shù)據(jù)表分月存儲,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-03-03
SpringBoot如何配置Controller實現(xiàn)Web請求處理
這篇文章主要介紹了SpringBoot如何配置Controller實現(xiàn)Web請求處理,文中通過圖解示例介紹的很詳細,具有有一定的參考價值,需要的小伙伴可以參考一下2023-05-05

