Spring:次のJavaマイクロフレームワーク

この記事では、 機能的なWebフレームワークと呼ばれるSpring Framework 5の新しいコンセプトについて説明し、軽量のアプリケーションとマイクロサービスの開発にどのように役立つかを説明します。







Springマイクロフレームワークが1つの文になっていることに驚くかもしれません。 しかし、そうです、 Springはあなたの次のJavaマイクロフレームワークかもしれません。 混乱を避けるために、 microの意味を定義してみましょう。









これらの点のいくつかは、 Spring Bootを使用する場合に関連しますが、 Spring Framework自体に追加の魔法を追加します@Controller



ような基本的なアノテーションでさえ、自動設定やコンポーネントスキャンはもちろん、簡単ではありません。 一般に、大規模なアプリケーションの場合、 SpringがDI、ルーティング、構成などを処理することは、かけがえのないものです。 ただし、アプリケーションが1つの大きなマシンの単なるギアであるマイクロサービスの世界では、 Spring Bootのすべてのパワーが少し冗長になる可能性があります。







この問題を解決するために、Springチームは機能的なWebフレームワークと呼ばれる新しい機能を導入しました。これについて説明します。 全体として、これは以前はSpring Reactive Webと呼ばれていた、より大きなSpring WebFluxサブプロジェクトの一部です。







はじめに、基本に戻って、Webアプリケーションとは何か、その中にどのコンポーネントが含まれているのかを見てみましょう。 もちろん、基本的なものがあります-Webサーバーです。 リクエストの手動処理とアプリケーションメソッドの呼び出しを回避するには、 ルーターが役立ちます 。 最後に、 ハンドラーが必要です。これは、リクエストを受け入れて答えを返すコードです。 実際、必要なのはそれだけです! そして、 機能的なSpring Webフレームワークが提供するのはこれらのコンポーネントであり、すべての魔法を取り除き、基本的な最小限に焦点を合わせます。 これは、 Springが急激に方向を変えてSpring MVCから離れることを意味するものではないことに注意してください。







ハンドラー



例を見てみましょう。 はじめに、 Spring Initializrに移動し、 Spring Boot 2.0Reactive Webを唯一の依存関係として使用して新しいプロジェクトを作成しましょう。 これで、最初のハンドラー 、つまりリクエストを受信して​​応答を返す関数を作成できます。







 HandlerFunction hello = new HandlerFunction() { @Override public Mono handle(ServerRequest request) { return ServerResponse.ok().body(fromObject("Hello")); } };
      
      





したがって、 ハンドラーは、( ServerRequest



型の) request



パラメーターを受け取り、テキスト "Hello"を持つServerResponse



型のオブジェクトを返すHandlerFunction



インターフェースの単なる実装です。 Springは、サーバーからの応答を作成する便利なビルダーも提供します。 この場合、 ok()



を使用して、200のHTTP応答コードを自動的に返します。応答を返すには、提供されたオブジェクトから応答を作成するためのもう1つのヘルパーfromObject



が必要です。







また、コードをもう少し簡潔にし、Java 8などのラムダを使用することもできます。 HandlerFunction



は単一の抽象メソッドインターフェイス(SAM)であり、次のように関数を記述できます。







 HandlerFunction hello = request -> ServerResponse.ok().body(fromObject("Hello"));
      
      





ルーター



ハンドラーができたので、今度はrouterを定義します 。 たとえば、HTTP GET



メソッドを使用してURL "/"が呼び出されたときにハンドラーを呼び出します。 これを実現するには、ハンドラー関数をルートにマップするタイプRouterFunction



オブジェクトを定義します。







 RouterFunction router = route(GET("/"), hello);
      
      





route



GET



RouterFunctions



RouterFunctions



静的メソッドであり、いわゆるRouterFunction



を作成できます。 このような関数はリクエストを受け取り、すべての述語(URL、メソッド、コンテンツタイプなど)に一致するかどうかを確認し、目的のハンドラー関数を呼び出します。 この場合、述部はhttp GETメソッドおよびURL '/'であり、ハンドラー関数はhello



であり、これは上で定義されています。







Webサーバー



そして今、すべてを1つのアプリケーションにまとめるときです。 軽量でシンプルなReactive Netty



サーバーを使用します。 ルーターをWebサーバーと統合するには、それをHttpHandler



に変換する必要があります。 その後、サーバーを起動できます。







 HttpServer .create("localhost", 8080) .newHandler(new ReactorHttpHandlerAdapter(httpHandler)) .block();
      
      





ReactorHttpHandlerAdapter



はNettyが提供するクラスで、 HttpHandler



を受け入れます。残りのコードについては説明は不要だと思います。 localhost



ホストとポート8080



バインドされた新しいWebサーバーを作成し、ルーターから作成されたhttpHandler



を提供します。







これで、アプリケーションの準備ができました! そしてその完全なコード:







 public static void main(String[] args) throws IOException, LifecycleException, InterruptedException { HandlerFunction hello = request -> ServerResponse.ok().body(fromObject("Hello")); RouterFunction router = route(GET("/"), hello); HttpHandler httpHandler = RouterFunctions.toHttpHandler(router); HttpServer .create("localhost", 8080) .newHandler(new ReactorHttpHandlerAdapter(httpHandler)) .block(); Thread.currentThread().join(); }
      
      





最後の行は、JVMプロセスを存続させるためにのみ必要です。 HttpServer自体はブロックしません。 すぐにアプリケーションが起動することに気付くかもしれません-コンポーネントのスキャンや自動設定はありません。







このアプリケーションを通常のJavaアプリケーションとして実行することもできます;アプリケーションコンテナーは必要ありません。







デプロイメントアプリケーションをパッケージ化するには、 Maven Springプラグインを利用して 、単に







./mvnw package









このコマンドは、JARに含まれるすべての依存関係を使用して、いわゆるファットJARを作成します。 このファイルは、JRE以外がインストールされていなくてもデプロイおよび実行できます。







java -jar target/functional-web-0.0.1-SNAPSHOT.jar









また、アプリケーションのメモリ使用量を確認すると、32 MBの領域にとどまり、メタスペース(クラス)で22 MBが使用され、ヒープで約10 MBが直接使用されていることがわかります。 もちろん、私たちのアプリケーションは何もしませんが、それでもフレームワークとランタイムだけで最小限のシステムリソースが必要なことを示しています。







JSONサポート



この例では、文字列を返しましたが、JSON応答を返すのも同じくらい簡単です。 JSONを返す新しいエンドポイントでアプリケーションを拡張しましょう。 私たちのモデルは非常に単純です-nameというname



1つの文字列フィールドです。 不要な定型コードを避けるために、 Lombokプロジェクトの機能である@Data



アノテーションを使用します。 このアノテーションを使用すると、ゲッター、セッター、 equals



、およびhashCode



メソッドが自動的に作成されるため、これらを手動で解放する必要はありません。







 @Data class Hello { private final String name; }
      
      





ここで、URL /json



にアクセスするときにJSON応答を返すようにルーターを拡張する必要があります。 これは、既存のルートでandRoute(...)



メソッドを呼び出すことで実行できます。 また、ルーターコードを別の関数に入れて、アプリケーションコードから分離し、後でテストで使用できるようにします。







 static RouterFunction getRouter() { HandlerFunction hello = request -> ok().body(fromObject("Hello")); return route( GET("/"), hello) .andRoute( GET("/json"), req -> ok() .contentType(APPLICATION_JSON) .body(fromObject(new Hello("world"))); }
      
      





再起動後、アプリケーションは、タイプapplication/json



コンテンツを要求するときにURL /json



にアクセスすると{ "name": "world" }



を返します。







アプリケーションコンテキスト



お気づきかもしれませんが、アプリケーションコンテキストを定義しなかったため、単に必要ではありません! RouterFunction



をSpring WebFluxアプリケーションのコンテキストでBeanとして宣言でき、特定のURLのリクエストも処理できるという事実にもかかわらず、ルーターはNettyサーバーの上で簡単に起動して、シンプルで軽量なJSONサービスを作成できます。







テスト中



リアクティブアプリケーションをテストするために、 SpringWebTestClient



と呼ばれる新しいクライアントを提供します( WebTestClient



似ていMockMvc



)。 既存のアプリケーションコンテキスト用に作成できますが、 RouterFunction



用に定義することもできます。







 public class FunctionalWebApplicationTests { private final WebTestClient webTestClient = WebTestClient .bindToRouterFunction( FunctionalWebApplication.getRouter()) .build(); @Test public void indexPage_WhenRequested_SaysHello() { webTestClient.get().uri("/").exchange() .expectStatus().is2xxSuccessful() .expectBody(String.class) .isEqualTo("Hello"); } @Test public void jsonPage_WhenRequested_SaysHello() { webTestClient.get().uri("/json").exchange() .expectStatus().is2xxSuccessful() .expectHeader().contentType(APPLICATION_JSON) .expectBody(Hello.class) .isEqualTo(new Hello("world")); } }
      
      





WebTestClient



は、HTTPコード、応答の内容、応答の種類などを検証するために、受信した応答に適用できる多くのWebTestClient



が含まれています。







結論として



Spring 5では、小型で軽量のマイクロサービススタイルのWebアプリケーションを開発するための新しいパラダイムを導入しています。 このようなアプリケーションは、アプリケーションコンテキスト、自動構成なしで動作し、一般にルーターおよびハンドラー機能とWebサーバーがアプリケーション本体で明示的に定義されている場合、 マイクロフレームワークアプローチを使用します。







コード



GitHubで利用可能







参照資料





翻訳者から



私は元の記事の著者でもあるため、コメントで質問することができます。








All Articles