【From】https://spring.io/guides/gs/reactive-rest-service/
Building a Reactive RESTful Web Service —— 用 Spring WebFlux 构建reactive restful web服务
本文转自以上Pivotal公司原文,简要概括翻译。
使用spring framework 5 里面的webflux来构建一个最基本的restful web服务。本人实测使用环境是JDK1.8,springboot 2.2.1.RELEASE,gradle 5.2.1,IntelliJ IDEA 2019。
一、创建项目
在IDEA里创建一个新的gradle项目,例如取名“webflux_greeting”。在IDEA中建好项目会自己构建这个空项目,然后一直显示“configuring build...”,应该是一直连不了maven的官方仓库的问题,我直接关了。下面修改完build.gradle之后加上阿里云的maven仓库,再构建就好。
二、配置build.gradle
使用以下内容替换自动生成的build.gradle文件,buildscript里面的仓库信息是给gradle下载自己的依赖用的,buildscript后面的repositories才是项目构建时查找依赖的仓库地址,两个都加上阿里云仓库作为优先选择。
buildscript { ext { springBootVersion = '2.2.1.RELEASE' } repositories {
maven { url "http://maven.aliyun.com/nexus/content/groups/public/" } mavenCentral() } dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") } } apply plugin: 'java' apply plugin: 'eclipse' apply plugin: 'org.springframework.boot' apply plugin: 'io.spring.dependency-management' bootJar { baseName = 'gs-reactive-rest-service' version = '0.1.0' } sourceCompatibility = 1.8 repositories {
maven { url "http://maven.aliyun.com/nexus/content/groups/public/" } mavenCentral() } dependencies { compile('org.springframework.boot:spring-boot-starter-webflux') testCompile('org.springframework.boot:spring-boot-starter-test') testCompile('io.projectreactor:reactor-test') }
Spring Boot gradle 插件提供了以下好处:
a、会自动收集所有需要的jar,打包成一个uber-jar,即集成所有依赖包。
b、自动识别所有 “public static void main()” 方法,并标记成可执行类。
c、自动解决项目中和springboot的引用相关的版本依赖和冲突,你也可以手动指定依赖版本。
三、编写WebFlux Handler
在IDEA的项目名上右键,选择添加目录,然后会弹出快捷方式。选择创建“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.fromValue("Hello, Spring!")); } }
四、编写router
类似的方式,在hello包中添加新class GreetingRouter:
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); } }
五、编写client (可选)
类似的方式,在hello包中添加新class GreetingWebClient。这个客户端既可以访问reactive的restful endpoint,同样适用于访问non-reactive的阻塞restful服务。
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(); } }
六、添加springboot主类
类似的方式,在hello包中添加新class Application。此“@SpringBootApplication”的注解等效于 @Configuration + @EnableAutoConfiguration + @ComponentScan。
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()); } }
七、构建项目
此时,执行gradle的build任务,项目会编译并运行,控制台会输出 >> result = Hello, Spring!,这是上面所写的客户端访问所得的结果。 如果从浏览器访问本地地址 http://localhost:8080/hello,同样可以得到 “Hello, Spring!” 的结果。
在项目路径下会生成build/libs目录,里面有打包好的用bootJar里设置好的名字和版本所生成的uber-jar “gs-reactive-rest-service-0.1.0.jar”,可以用 java -jar gs-reactive-rest-service-0.1.0.jar 来直接启动。
八、测试类
创建测试类 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!"); } }