Spring Web Reactive を試す
Spring 5 で導入される Spring Web Reactive を試してみました。
本来なら Spring Boot で実行する事になると思いますが、今回は Spring Boot を使わずに Undertow で直接実行してみます。
ソースは http://github.com/fits/try_samples/tree/master/blog/20161115/
サンプル作成
ビルド定義
現時点では Spring Web Reactive の正式版はリリースされていないようなのでスナップショット版を使います。
Undertow を実行するために undertow-core
、JSON で結果を返す処理を試すために jackson-databind
を依存関係へ設定しています。
build.gradle
apply plugin: 'application' mainClassName = 'sample.App' repositories { jcenter() maven { url 'http://repo.spring.io/snapshot/' } } dependencies { compile 'org.projectlombok:lombok:1.16.10' // Spring Web Reactive compile 'org.springframework:spring-web-reactive:5.0.0.BUILD-SNAPSHOT' // Undertow compile 'io.undertow:undertow-core:2.0.0.Alpha1' // JSON 用 runtime 'com.fasterxml.jackson.core:jackson-databind:2.8.4' }
設定クラス
@EnableWebReactive
を付与すれば Spring Web Reactive を有効にできるようです。
src/main/java/sample/config/AppConfig.java
package sample.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.web.reactive.config.EnableWebReactive; @EnableWebReactive @ComponentScan("sample.controller") public class AppConfig { }
コントローラークラス
Spring Web Reactive は Spring Web (MVC) のプログラミングスタイルを踏襲しているようです。
Spring Web (MVC) と同じアノテーションでコントローラークラスを定義し、メソッドの戻り値に Reactor の Mono / Flux を使えばよさそうです。
クラス | 概要 |
---|---|
Mono | 単一の結果を返す場合に使用 |
Flux | 複数の結果を返す場合に使用 |
src/main/java/sample/controller/SampleController.java
package sample.controller; import lombok.Value; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @RestController public class SampleController { @RequestMapping("/") public Mono<String> sample() { return Mono.just("sample"); } @RequestMapping(value = "/data/{name}", produces = "application/json") public Flux<Data> dataList(@PathVariable("name") String name) { return Flux.fromArray(new Data[] { new Data(name + "-1", 1), new Data(name + "-2", 2), new Data(name + "-3", 3) }); } @Value class Data { private String name; private int value; } }
実行クラス
まずは、Spring Web Reactive を有効化した ApplicationContext を使って HttpHandler を作成します。 (DispatcherHandler
と WebHttpHandlerBuilder
を使用)
あとは、対象の Web サーバー ※ に合わせて実行します。
※ 今のところ Servlet 3.1 対応コンテナ、Netty、Undertow を サポートしているようです
Undertow の場合、アダプタークラス UndertowHttpHandlerAdapter
で HttpHandler をラッピングして実行するだけです。
src/main/java/sample/App.java
package sample; import io.undertow.Undertow; import lombok.val; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.http.server.reactive.UndertowHttpHandlerAdapter; import org.springframework.web.reactive.DispatcherHandler; import org.springframework.web.server.adapter.WebHttpHandlerBuilder; import sample.config.AppConfig; public class App { public static void main(String... args) { val ctx = new AnnotationConfigApplicationContext(); // @EnableWebReactive を付与したクラスを登録 ctx.register(AppConfig.class); ctx.refresh(); val handler = new DispatcherHandler(); handler.setApplicationContext(ctx); // HttpHandler の作成 val httpHandler = WebHttpHandlerBuilder.webHandler(handler).build(); val server = Undertow.builder() .addHttpListener(8080, "localhost") // Undertow 用アダプターでラッピングして設定 .setHandler(new UndertowHttpHandlerAdapter(httpHandler)) .build(); server.start(); } }
実行
Gradle で実行
> gradle -q run ・・・ 情報: Mapped "{[/data/{name}],produces=[application/json]}" onto public reactor.core.publisher.Flux<sample.controller.SampleController$Data> sample.controller.SampleController.dataList(java.lang.String) 11 14, 2016 9:32:23 午後 org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping register 情報: Mapped "{[/]}" onto public reactor.core.publisher.Mono<java.lang.String> sample.controller.SampleController.sample() 11 14, 2016 9:32:23 午後 org.xnio.Xnio <clinit> INFO: XNIO version 3.3.6.Final 11 14, 2016 9:32:23 午後 org.xnio.nio.NioXnio <clinit> INFO: XNIO NIO Implementation Version 3.3.6.Final
動作確認1
$ curl -s http://localhost:8080/ sample
動作確認2
$ curl -s http://localhost:8080/data/a [{"name":"a-1","value":1},{"name":"a-2","value":2},{"name":"a-3","value":3}]