Spring Webflux 教程

1、概览

Spring 5 引入了 Spring WebFlux 框架,为 Web 应用提供响应式编程支持。

本文将带你了解如何使用响应式 Web 组件 RestControllerWebClient 创建一个小型响应式 REST 应用,以及如何使用 Spring Security 来保护响应式端点。

2、Spring WebFlux 框架

Spring WebFlux 内部使用 Project Reactor 及其 Publisher(发布者)实现、FluxMono

WebFlux 支持两种编程模式:

  • 基于注解的响应式组件
  • 函数式路由和处理

本文重点介绍基于注解的响应式组件。

3、依赖

首先从 spring-boot-starter-webflux 依赖开始。

它会传递依赖其他所有的依赖:

  • spring-bootspring-boot-starter,用于基本的 Spring Boot 应用设置
  • spring-webflux 框架
  • 响应式流(Reactive Stream)所需的 reactor-core 以及 reactor-netty
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
    <version>3.1.2</version>
</dependency>

可从 Maven Central 下载最新的 spring-boot-starter-webflux

4、响应式 REST 应用

现在,使用 Spring WebFlux 构建一个非常简单的响应式 REST EmployeeManagement 应用:

  • 使用 Employee Model - 有 idname 字段。
  • 使用 RestController 构建 REST API,以单个资源和集合的形式发布 Employee 资源。
  • 使用 WebClient 创建客户端,检索相同的资源。
  • 使用 WebFlux 和 Spring Security 创建安全的响应式端点

5、响应式 RestController

Spring WebFlux 与 Spring Web MVC 框架一样支持基于注解的配置。

首先,创建一个基于注解的 Controller EmployeeController,用于发布 Employee 资源的响应式流。

@RestController
@RequestMapping("/employees")
public class EmployeeController {

    private final EmployeeRepository employeeRepository;
    
    // 构造函数
}

EmployeeRepository 可以是任何支持非阻塞响应式流的 Data Repository。

5.1、单个资源

然后,在 Controller 中创建一个端点,用于发布单个 Employee 资源:

@GetMapping("/{id}")
public Mono<Employee> getEmployeeById(@PathVariable String id) {
    return employeeRepository.findEmployeeById(id);
}

如上,使用 Mono 封装了一个 Employee 资源,因为最多只能返回一个 Employee

5.2、资源集合

再添加一个端点,用于发布所有 Employees 的集合资源:

@GetMapping
public Flux<Employee> getAllEmployees() {
    return employeeRepository.findAllEmployees();
}

对于集合资源,使用 Employee 类型的 Flux,因为它是 0...n 个元素的发布者。

6、响应式 Web 客户端

Spring 5 中引入的 WebClient,是一种支持响应式流的非阻塞客户端。

使用 WebClient 创建一个客户端,以从 EmployeeController 提供的端点检索数据。

创建一个 EmployeeWebClient

public class EmployeeWebClient {

    WebClient client = WebClient.create("http://localhost:8080");

    // ...
}

如上,使用其工厂方法 create 创建了一个 WebClient。它将指向 localhost:8080,因此可以使用相对 URL 来调用该客户端实例。

6.1、检索单个资源

从端点 /employee/{id} 获取 Mono 类型的单个资源:

Mono<Employee> employeeMono = client.get()
  .uri("/employees/{id}", "1")
  .retrieve()
  .bodyToMono(Employee.class);

employeeMono.subscribe(System.out::println);

6.2、检索资源集合

同样,从端点 /employees 获取 Flux 类型的集合资源:

Flux<Employee> employeeFlux = client.get()
  .uri("/employees")
  .retrieve()
  .bodyToFlux(Employee.class);
        
employeeFlux.subscribe(System.out::println);

关于 WebClient 的更多详细用法,你可以参考 这篇教程

7、Spring WebFlux 安全设置

可以使用 Spring Security 来保护响应式端点。

接着,我们要在 EmployeeController 中新建了一个端点。这个端点会更新 Employee 的详细信息,并将更新后的 Employee 信息响应回来。

由于这允许用户更改现有的 Employee 数据,所以将此端点限制为只能由 ADMIN 角色用户使用。

EmployeeController 中添加一个新方法:

@PostMapping("/update")
public Mono<Employee> updateEmployee(@RequestBody Employee employee) {
    return employeeRepository.updateEmployee(employee);
}

创建 SecurityConfig 并定义一些基于路径的规则来限制对该方法的访问。

只允许 ADMIN 用户访问 /employees/update 端点:

@EnableWebFluxSecurity
public class EmployeeWebSecurityConfig {

    // ...

    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(
      ServerHttpSecurity http) {
        http.csrf().disable()
          .authorizeExchange()
          .pathMatchers(HttpMethod.POST, "/employees/update").hasRole("ADMIN")
          .pathMatchers("/**").permitAll()
          .and()
          .httpBasic();
        return http.build();
    }
}

此配置会限制对 /employees/update 端点的访问。只有角色为 ADMIN 的用户才能访问该端点并更新现有 Employee

最后,注解 @EnableWebFluxSecurity 通过一些默认配置添加了 Spring Security WebFlux 支持。

关于 Spring Security 的更多用法,你可以参考 Spring Security 中文文档。

8、总结

本文介绍了如何使用 Spring WebFlux 创建响应式 Web 应用。通过实际案例演示了如何使用 RestController 和 WebClient 发布和消费响应式流,以及如何使用 Spring Security 保护响应式端点。


Ref:https://www.baeldung.com/spring-webflux