• Spring webflux 构建响应式 RESTful Web 服务


    本指南将引导您完成创建“Hello, Spring!”使用Spring WebFlux的rest式web服务(Spring Boot 2.0的新版本),然后使用WebClient使用该服务(Spring Boot 2.0的新版本)。

    1、你将建立什么

    您将使用 Spring Webflux 和该服务的 WebClient 使用者构建一个RESTful web 服务。您将能够在两个系统中看到输出。和:

    http://localhost:8080/hello
    
    • 1

    2、你需要什么

    • 大约15分钟
    • 最喜欢的文本编辑器或IDE
    • JDK 1.8及以上版本
    • Gradle 4+或Maven 3.2+
    • 你也可以将代码直接导入到你的IDE中:
      • Spring Tool Suite (STS)
      • IntelliJ IDEA

    3、如何完成本指南

    与大多数 Spring Getting Started 指南一样,您可以从头开始并完成每个步骤,也可以跳过您已经熟悉的基本设置步骤。无论哪种方式,您最终都会得到可工作的代码。

    要从头开始,请转到“ 从 Spring Initializr 开始”。

    要跳过这些基本步骤,请执行以下步骤:

    当您完成时,您可以检查您的结果与代码 gs-reactive-rest-service/complete .

    4、从 Spring Initializr 开始

    您可以使用这个 预先初始化的项目 并单击 Generate 下载ZIP文件。该项目被配置为适合本教程中的示例。

    手动初始化项目:

    • 导航到 https://start.spring.io。这个服务会拉入应用程序所需的所有依赖项,并为你完成大部分设置工作。
    • 选择 Gradle 或 Maven 和你想要使用的语言。本指南假设您选择了Java。
    • 单击 Dependencies 并选择 Spring Reactive Web
    • 点击 Generate
    • 下载得到的ZIP文件,它是用您的选择配置的web应用程序的存档文件。

    5、创建 WebFlux Handler

    我们将从一个 Greeting POJO开始,它将被我们的RESTful服务序列化为JSON:
    src/main/java/hello/Greeting.java

    package hello;
    
    
    public class Greeting {
    
      private String message;
    
      public Greeting() {
      }
    
      public Greeting(String message) {
        this.message = message;
      }
    
      public String getMessage() {
        return this.message;
      }
    
      public void setMessage(String message) {
        this.message = message;
      }
    
      @Override
      public String toString() {
        return "Greeting{" +
            "message='" + message + '\'' +
            '}';
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    在 Spring Reactive 方法中,我们使用一个处理程序来处理请求并创建响应,如下所示:
    src/main/java/hello/GreetingHandler.java

    package hello;
    
    import org.springframework.http.MediaType;
    import org.springframework.stereotype.Component;
    import org.springframework.web.reactive.function.BodyInserters;
    import org.springframework.web.reactive.function.server.ServerRequest;
    import org.springframework.web.reactive.function.server.ServerResponse;
    
    import reactor.core.publisher.Mono;
    
    @Component
    public class GreetingHandler {
    
      public Mono<ServerResponse> hello(ServerRequest request) {
        return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
          .body(BodyInserters.fromValue(new Greeting("Hello, Spring!")));
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    这个简单的响应式类总是返回一个带有 “Hello, Spring!” “问候。它可以返回许多其他内容,包括从数据库中返回的项流、由计算生成的项流等等。注意反应性代码:保存 ServerResponse 主体的 Mono 对象。

    6、创建 Router

    在这个应用程序中,我们使用一个路由器来处理我们暴露的唯一路由(/hello),如下所示:
    src/main/java/hello/GreetingRouter.java

    package hello;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.http.MediaType;
    import org.springframework.web.reactive.function.server.RouterFunction;
    import org.springframework.web.reactive.function.server.RouterFunctions;
    import org.springframework.web.reactive.function.server.ServerResponse;
    
    import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
    import static org.springframework.web.reactive.function.server.RequestPredicates.accept;
    
    @Configuration(proxyBeanMethods = false)
    public class GreetingRouter {
    
      @Bean
      public RouterFunction<ServerResponse> route(GreetingHandler greetingHandler) {
    
        return RouterFunctions
          .route(GET("/hello").and(accept(MediaType.APPLICATION_JSON)), greetingHandler::hello);
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    7、创建 WebClient

    Spring RestTemplate 类本质上是阻塞的。因此,我们不希望在响应式应用程序中使用它。对于响应式应用程序,Spring 提供了 WebClient类,它是非阻塞的。我们使用一个基于 webclient 的实现来消费我们的 RESTful 服务:

    src/main/java/hello/GreetingClient.java

    package hello;
    
    import reactor.core.publisher.Mono;
    
    import org.springframework.http.MediaType;
    import org.springframework.stereotype.Component;
    import org.springframework.web.reactive.function.client.WebClient;
    
    @Component
    public class GreetingClient {
    
      private final WebClient client;
    
      // Spring Boot auto-configures a `WebClient.Builder` instance with nice defaults and customizations.
      // We can use it to create a dedicated `WebClient` for our component.
      public GreetingClient(WebClient.Builder builder) {
        this.client = builder.baseUrl("http://localhost:8080").build();
      }
    
      public Mono<String> getMessage() {
        return this.client.get().uri("/hello").accept(MediaType.APPLICATION_JSON)
            .retrieve()
            .bodyToMono(Greeting.class)
            .map(Greeting::getMessage);
      }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    WebClient 类使用响应式特性,以 Mono 的形式保存消息的内容(由getMessage 方法返回)。这是使用函数 API (而不是命令式 API)来链接反应操作符。

    适应响应式api可能需要一些时间,但是 WebClient有一些有趣的特性,也可以在传统的 Spring MVC 应用程序中使用。

    也可以使用WebClient与非响应式、阻塞式服务通信。

    8、使应用程序可执行

    我们将使用 main() 方法来驱动应用程序,并从端点获得 Greeting 消息。

    src/main/java/hello/Application.java

    package hello;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.ConfigurableApplicationContext;
    
    @SpringBootApplication
    public class Application {
    
      public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
        GreetingClient greetingClient = context.getBean(GreetingClient.class);
        // We need to block for the content here or the JVM might exit before the message is logged
        System.out.println(">> message = " + greetingClient.getMessage().block());
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    @SpringBootApplication 是一个方便的注释,它添加了以下所有内容:

    • @Configuration:将类标记为应用程序上下文的 bean 定义源。
    • @EnableAutoConfiguration:告诉 Spring Boot 根据类路径设置、其他 bean 和各种属性设置开始添加bean。例如,如果 spring-webmvc 在类路径上,这个注释将应用程序标记为 web 应用程序,并激活关键行为,例如设置DispatcherServlet
    • @ComponentScan:告诉 Spring 在 hello 包中查找其他组件、配置和服务,让它找到控制器。

    main() 方法使用 Spring Boo t的 SpringApplication.run() 方法来启动应用程序。您是否注意到没有一行 XML ,也没有 web.xml文件。这个 web 应用程序是 100% 纯 Java,你不需要处理配置任何管道或基础设施。

    9、构建一个可执行的JAR

    您可以使用Gradle或Maven从命令行运行应用程序。您还可以构建一个单独的可执行JAR文件,其中包含所有必要的依赖项、类和资源,并运行它。构建一个可执行的jar使得在整个开发生命周期、跨不同的环境等过程中,将服务作为应用程序来交付、版本和部署变得容易。

    如果你使用Gradle,你可以使用 ./gradlew boorun 来运行应用程序。或者,你也可以使用 ./gradlew build构建JAR文件,然后运行JAR文件,如下所示:

    java -jar build/libs/gs-reactive-rest-service-0.1.0.jar
    
    • 1

    如果你使用Maven,你可以使用 ./mvnw spring-boot:run 来运行这个应用。或者,您也可以使用 ./mvnw clean 包构建JAR文件,然后运行 JAR 文件,如下所示:

    java -jar target/gs-reactive-rest-service-0.1.0.jar
    
    • 1

    显示日志输出。服务应该在几秒钟内启动并运行。

    一旦服务启动,你会看到一行代码:

    >> message = Hello, Spring!
    
    • 1

    这一行来自 WebClient 正在使用的响应式内容。当然,您可以对输出进行更有趣的处理,而不是将其放入 System.out 中。

    10、测试应用程序

    现在应用程序正在运行,您可以对其进行测试。首先,您可以打开浏览器并访问 http://localhost:8080/hello,并看到 “Hello, Spring!” 在本指南中,我们还创建了一个测试类,让您开始使用 WebTestClient 类进行测试。

    src/test/java/hello/GreetingRouterTest.java

    package hello;
    
    import org.junit.jupiter.api.Test;
    import org.junit.jupiter.api.extension.ExtendWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.http.MediaType;
    import org.springframework.test.context.junit.jupiter.SpringExtension;
    import org.springframework.test.web.reactive.server.WebTestClient;
    
    import static org.assertj.core.api.Assertions.assertThat;
    
    @ExtendWith(SpringExtension.class)
    //  We create a `@SpringBootTest`, starting an actual server on a `RANDOM_PORT`
    @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
    public class GreetingRouterTest {
    
      // Spring Boot will create a `WebTestClient` for you,
      // already configure and ready to issue requests against "localhost:RANDOM_PORT"
      @Autowired
      private WebTestClient webTestClient;
    
      @Test
      public void testHello() {
        webTestClient
          // Create a GET request to test an endpoint
          .get().uri("/hello")
          .accept(MediaType.APPLICATION_JSON)
          .exchange()
          // and use the dedicated DSL to test assertions against the response
          .expectStatus().isOk()
          .expectBody(Greeting.class).value(greeting -> {
            assertThat(greeting.getMessage()).isEqualTo("Hello, Spring!");
        });
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    11、总结

    恭喜你! 您已经开发了一个响应式 Spring 应用程序,其中包括一个使用RESTful 服务的 WebClient !

    想要编写新的指南或对现有指南做出贡献吗?请查看我们的 投稿指南

  • 相关阅读:
    DPDK之eventdev_pipeline源码解析
    vscode配置openssl include和lib环境(M1 mac)
    【Spring(六)】使用篇:AOP在开发中的使用
    JAVA通过COM方式(jeasyopc)接入OPC DA
    数据资产管理与数据安全国内外最新趋势
    Python代码中的偏函数
    数据结构--5.0.1图的存储结构
    Transformer3
    Linux 内存top命令详解
    美团一面(手撕)
  • 原文地址:https://blog.csdn.net/u012410733/article/details/126075750