Spring WebFlux – Spring Reactive Programming春のWebFlux – 春のリアクティブプログラミング

Spring WebFluxは、Spring 5で導入された新しいモジュールです。Spring WebFluxは、Springフレームワークにおける反応型プログラミングモデルへの第一歩です。

Springのリアクティブプログラミング

もし、あなたがリアクティブプログラミングモデルに慣れていないのであれば、リアクティブプログラミングについて学ぶために以下の記事を読むことを強くおすすめします。

  • Reactive Manifesto
  • Reactive Streams
  • Java 9 Reactive Streams
  • RxJava

もしSpring 5に初めて触れる場合は、Spring 5の特徴をご確認ください。

Spring WebFlux(スプリング ウェブフラックス)

spring webflux and spring mvc
    1. Mono: Publisherを実装し、0または1つの要素を返します。

 

    Flux: Publisherを実装し、N個の要素を返します。

Spring WebFluxのHello Worldの例。

spring webflux example

Spring WebFluxのMavenの依存関係

<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.scdev.spring</groupId>
  <artifactId>SpringWebflux</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>Spring WebFlux</name>
  <description>Spring WebFlux Example</description>
  
      <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <jdk.version>1.9</jdk.version>
    </properties>
    
  <parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.1.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>
	<repositories>
		<repository>
			<id>spring-snapshots</id>
			<name>Spring Snapshots</name>
			<url>https://repo.spring.io/snapshot</url>
			<snapshots>
				<enabled>true</enabled>
			</snapshots>
		</repository>
		<repository>
			<id>spring-milestones</id>
			<name>Spring Milestones</name>
			<url>https://repo.spring.io/milestone</url>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</repository>
	</repositories>
	<pluginRepositories>
		<pluginRepository>
			<id>spring-snapshots</id>
			<name>Spring Snapshots</name>
			<url>https://repo.spring.io/snapshot</url>
			<snapshots>
				<enabled>true</enabled>
			</snapshots>
		</pluginRepository>
		<pluginRepository>
			<id>spring-milestones</id>
			<name>Spring Milestones</name>
			<url>https://repo.spring.io/milestone</url>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</pluginRepository>
	</pluginRepositories>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.7.0</version>
                    <configuration>
                        <source>${jdk.version}</source>
                        <target>${jdk.version}</target>
                    </configuration>
                </plugin>
            </plugins>
    </pluginManagement>
    </build>
    
</project>

最も重要な依存関係は、spring-boot-starter-webfluxとspring-boot-starter-parentです。その他の依存関係は、JUnitのテストケースを作成するためのものです。

スプリングのWebFluxハンドラ

Spring WebFluxのハンドラーメソッドがリクエストを処理し、レスポンスとしてMonoまたはFluxを返します。

package com.scdev.spring.component;

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

	public Mono<ServerResponse> helloWorld(ServerRequest request) {
		return ServerResponse.ok().contentType(MediaType.TEXT_PLAIN)
			.body(BodyInserters.fromObject("Hello World!"));
	}
}

リアクティブコンポーネントMonoがServerResponseのボディを保持することに注意してください。また、戻り値のコンテンツタイプ、レスポンスコード、およびボディを設定するための関数チェーンにも注目してください。

Spring WebFluxのルーター

アプリケーションのルートを定義するためにルーターメソッドが使用されます。これらのメソッドはRouterFunctionオブジェクトを返し、ServerResponseの本体も保持します。

package com.scdev.spring.component;

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

	@Bean
	public RouterFunction<ServerResponse> routeHelloWorld(HelloWorldHandler helloWorldHandler) {

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

私たちは/helloWorldに対してGETメソッドを公開しており、クライアントの呼び出しは平文のレスポンスを受け入れる必要があります。

スプリングブートアプリケーション

私たちのシンプルなWebFluxアプリケーションをSpring Bootで設定しましょう。

package com.scdev.spring;

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);
	}
}

上記のコードを見ると、Spring WebFluxに関連するものは何もありません。しかし、spring-boot-starter-webfluxモジュールの依存関係を追加したため、Spring Bootは私たちのアプリケーションをSpring WebFluxとして構成します。

Java 9のモジュールサポート

私たちのアプリケーションはJava 8上で実行する準備ができていますが、Java 9を使用している場合は、module-info.javaクラスを追加する必要があります。

module com.scdev.spring {
    requires reactor.core;
    requires spring.web;
    requires spring.beans;
    requires spring.context;
    requires spring.webflux;
    requires spring.boot;
    requires spring.boot.autoconfigure;
    exports com.scdev.spring;
}

Spring WebFlux Spring Boot アプリケーションを実行しています。

Eclipse run as spring boot app
2018-05-07 15:01:47.893  INFO 25158 --- [           main] o.s.w.r.f.s.s.RouterFunctionMapping      : Mapped ((GET && /helloWorld) && Accept: [text/plain]) -> com.scdev.spring.component.HelloWorldRouter$$Lambda$501/704766954@6eeb5d56
2018-05-07 15:01:48.495  INFO 25158 --- [ctor-http-nio-1] r.ipc.netty.tcp.BlockingNettyContext     : Started HttpServer on /0:0:0:0:0:0:0:0:8080
2018-05-07 15:01:48.495  INFO 25158 --- [           main] o.s.b.web.embedded.netty.NettyWebServer  : Netty started on port(s): 8080
2018-05-07 15:01:48.501  INFO 25158 --- [           main] com.scdev.spring.Application        : Started Application in 1.86 seconds (JVM running for 5.542)

ログからは、私たちのアプリがポート8080上のNettyサーバーで実行されていることが明確です。さあ、アプリケーションをテストしましょう。

春のWebFluxアプリのテスト

私たちはさまざまな方法でアプリをテストすることができます。 (Watashitachi wa samazama na hōhō de apuri o tesuto suru koto ga dekimasu.)

    1. CURLコマンドを使用する

$ curl https://localhost:8080/helloWorld
Hello World!
$

ブラウザでURLを起動する

Spring 5のWebTestClientを使用

以下は、Spring 5のリアクティブウェブを使用して当社のRESTウェブサービスをテストするためのJUnitテストプログラムです。

“`java
package com.scdev.spring;

import org.junit.Test;
import org.junit.runner.RunWith;
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;

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class SpringWebFluxTest {

@Autowired
private WebTestClient webTestClient;

@Test
public void testHelloWorld() {
webTestClient
.get().uri(“/helloWorld”) // GETメソッドとURI
.accept(MediaType.TEXT_PLAIN) // ACCEPT-Contentを設定
.exchange() // 応答にアクセスする
.expectStatus().isOk() // 応答がOKかどうかを確認する
.expectBody(String.class).isEqualTo(“Hello World!”); // 応答のタイプとメッセージを確認する
}

}
“`

これをJUnitテストケースとして実行し、正常にパスするはずです。

Spring Web ReactiveからWebClientを使用する

RESTウェブサービスを呼び出すためにWebClientを使用することもできます。

“`java
package com.scdev.spring.client;

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

public static void main(String args[]) {
WebClient client = WebClient.create(“https://localhost:8080”);

Mono result = client.get()
.uri(“/helloWorld”)
.accept(MediaType.TEXT_PLAIN)
.exchange();

System.out.println(“Result = ” + result.flatMap(res -> res.bodyToMono(String.class)).block());
}

}
“`

単純なJavaアプリケーションとして実行し、デバッグメッセージがたくさん表示された正しい出力が表示されるはずです。

要約。

この投稿では、Spring WebFluxについて学び、リアクティブなRestful Webサービスを作成する方法を学びました。Springなどの人気のあるフレームワークがリアクティブプログラミングモデルをサポートしていることは良いことです。しかし、依存関係がリアクティブで非ブロッキングでない場合、アプリケーションも真にリアクティブではありません。例えば、関係データベースのベンダーはリアクティブなドライバを持っていないため、JDBCに依存しています。したがって、Hibernate APIも非リアクティブです。したがって、関係データベースを使用している場合、本当にリアクティブなアプリケーションを構築することはできません、まだです。私はそれが近いうちに変わることを期待しています。

私のGitHubリポジトリから、プロジェクトのコードをダウンロードすることができます。

参照:公式ドキュメンテーション

コメントを残す 0

Your email address will not be published. Required fields are marked *