本指南将引导您使用Spring WebFlux完成创建“Hello, Spring!”的RESTful Web服务的过程,然后使用WebClient调用该服务。你能够在system.out和浏览器访问http://localhost:8080/hello后看到输出。

程序结构

└── src
    └── main
        └── java
            └── hello

pom.xml

<?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>org.springframework</groupId>
    <artifactId>gs-reactive-rest-service</artifactId>
    <version>0.1.0</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <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>

        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

Spring Boot将会你做如下的事:

  • 将 classpath 里面所有用到的jar包构建成一个可执行的 JAR 文件,方便执行你的程序
  • 搜索public static void main()方法并且将它当作可执行类
  • 根据springboot版本,去查找相应的依赖类版本,当然你可以定义其它版本。

建立一个WebFlux处理器
在Spring响应式方法中,我们使用一个处理程序来处理请求并创建响应,如下例所示:

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.TEXT_PLAIN)
            .body(BodyInserters.fromObject("Hello, Spring!"));
    }
}

这个简单的响应类总是返回“Hello, Spring!”.它也可以返回许多其他东西,包括数据库中的数据流、通过计算后生成的一个数据流等等。注意响应式代码返回:一个包含ServerResponse主体的Mono对象。

创建一个路由器类
在这个应用程序中,我们使用路由器类来处理我们公开的唯一路由(“/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.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;

@Configuration
public class GreetingRouter {

    @Bean
    public RouterFunction<ServerResponse> route(GreetingHandler greetingHandler) {

        return RouterFunctions
            .route(RequestPredicates.GET("/hello").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), greetingHandler::hello);
    }
}

路由器类监听/hello路径上的流量,并返回我们的响应式处理程序类提供的值。
创建一个WebClient
Spring MVC RestTemplate类本质上是阻塞的。因此,我们不想在响应式应用程序中使用它。对于响应式应用程序,Spring提供了WebClient类,它是非阻塞的。我们将使用WebClient实现来使用我们的RESTful服务:
src/main/java/hello/GreetingWebClient.java

package hello;

import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient;

import reactor.core.publisher.Mono;

public class GreetingWebClient {
    private WebClient client = WebClient.create("http://localhost:8080");

    private Mono<ClientResponse> result = client.get()
            .uri("/hello")
            .accept(MediaType.TEXT_PLAIN)
            .exchange();

    public String getResult() {
        return ">> result = " + result.flatMap(res -> res.bodyToMono(String.class)).block();
    }
}

WebClient使用Mono类型的响应式来保存我们请求的URI的内容,并使用函数(在getResult 方法中)将该内容转换为字符串。如果我们有不同的需求,我们可以把它转换成字符串以外的东西。因为我们要将结果放入System.out,所以这里需要一个字符串。

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

创建Application
尽管可以将此服务打包为用于部署到外部应用服务器的传统war文件,但下面演示的简单方法是创建了一个独立的应用程序。在一个单一的、可执行的JAR文件中打包所有的东西,由一个好的Java main()方法驱动。在此过程中,您使用响应式Spring的支持将Netty服务器嵌入为HTTP运行时,而不是部署到外部实例。
src/main/java/hello/Application.java

package hello;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);

        GreetingWebClient gwc = new GreetingWebClient();
        System.out.println(gwc.getResult());
    }
}

@SpringBootApplication包含如下注解:
@Configuration 将类标记为应用程序上下文的bean定义源。
@EnableAutoConfiguration 告诉SpringBoot根据类路径设置、其他bean和各种属性设置开始添加bean。
通常,您会为SpringMVC应用程序添加@EnableWebMVC,但当SpringBoot在类路径上看到spring-webmvc 时,它会自动添加。这将应用程序标记为Web应用程序,并激活关键行为,如设置DispatcherServlet。
@ComponentScan 告诉Spring在hello包中查找其他组件、配置和服务。

执行应用程序
在STS下,右键-选择Run as-Spring Boot App ,输出如下:

>> result = Hello, Spring!

测试程序
现在应用程序运行后,您可以测试它。首先,您可以打开一个浏览器并转到http://localhost:8080/hello,然后看到“Hello, Spring!”对于本指南,我们还创建了一个测试类,让您开始使用WebTestClient类进行测试。
src/test/java/hello/GreetingRouterTest.java

package hello;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;

import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(SpringRunner.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.TEXT_PLAIN)
            .exchange()
            // and use the dedicated DSL to test assertions against the response
            .expectStatus().isOk()
            .expectBody(String.class).isEqualTo("Hello, Spring!");
    }
}

祝贺你!您开发了一个响应式Spring应用程序,其中包括一个WebClient来使用RESTful服务!