JSR 371(MVC 1.0) Early Draft version 1.0 の日本語訳(前編)
この記事について
Java EE 8の新機能であるMVC 1.0のJSR 371 Early Draft Release 1 を、英語の勉強がてら日本語訳してみました。
僕はあまり英語が得意ではないので、正確な内容を知りたいという方は、下記の英語原文をお読みください。
https://jcp.org/en/jsr/detail?id=371
また、この仕様は現段階ではEarly Draftという初稿段階のようなものなので、今後の仕様策定の中で、内容がどんどん変更される可能性があります。
(仕様の正式リリースは、2016年第3四半期の予定です)
これらの点に留意していただいた上で、この記事をお読みください。
※JIRAによると、Early Draft Release 2が、6月30日に出る予定みたいです。
mvc-spec - Java.net JIRA
中編(第3章~第4章)→JSR 371(MVC 1.0) Early Draft version 1.0 の日本語訳(中編) - Java EE 事始め!
後編(第5章~第6章)→JSR 371(MVC 1.0) Early Draft version 1.0 の日本語訳(後編) - Java EE 事始め!
JSR 371の章立て
- Chapter 1 導入
- Chapter 2 モデル、ビュー、コントローラー
- Chapter 3 例外ハンドリング
- Chapter 4 イベント
- Chapter 5 アプリケーション
- Chapter 6 ビューエンジン
今回は、Chapter 1~2までを訳します。Chapter 3以降は、後編で別記事とします。
Chapter 1 導入
Mode-View-Controller(略してMVC)は、ほとんどのHTMLアプリケーション開発で利用されている、Webフレームワークの一般的なパターンです。「Model」はアプリケーションのデータの参照、「View」はアプリケーションのデータ表示、「Controller」は入力の管理・モデルの変更・出力の生成に責任を負います。
Web UIフレームワークは、「アクションベース」と「コンポーネントベース」に分類されます。アクションベースフレームワークでは、HTTPリクエストはアプリケーションコードによってアクションに変換されたコントローラーにルーティングされます。コンポーネントベースフレームワークでは、HTTPリクエストはグループ化され、主にフレームワークのコンポーネントによって扱われ、アプリケーションコードとの相互作用はほとんど(または全く)ありません。言い換えれば、コンポーネントベースフレームワークにおいては、コントローラーロジックの大半は、アプリケーションの代わりにフレームワークに提供されます。
この仕様で定義されたAPIは、アクションベースに分類されるものです。それゆえ、JSFのようなコンポーネントベースフレームワークを置き換えることが目的ではありません。しかし、Java EEプラットフォーム上にWebアプリケーションを簡単に構築するための異なったアプローチです。
1.1 ゴール
このAPIのゴールは下記です。
1.2 ゴールでないもの
このAPIで、ゴールとしないものは下記です。
1.3 追加情報
イシュートラッキングシステムはこちらです。
https://java.net/jira/browse/MVC SPEC
Javadocはこちらです。
https://mvc-spec.java.net/
参照実装はこちらから入手できます。
https://ozark.java.net/
エキスパートグループは、コミュニティからのこの仕様に関するあらゆるフィードバックを求めています。コメントは、こちらに送ってください。
[users@mvc-spec.java.net]
Chapter 2 モデル、ビュー、コントローラー
本章では、MVCアーキテクチャーパターンを構成する3つの要素(モデル、ビュー、コントローラー)について紹介します。
2.1 コントローラー
MVCのコントローラーは、@Controllerが付加されたJAX-RSリソースメソッドです。このアノテーションがクラスに適用されていた場合、そのクラス内の全リソースメソッドがコントローラーとみなされます。一部のメソッドのみに@Controllerを付加すると、そのメソッドはコントローラーとなり、他のメソッドは従来のJAX-RSリソースメソッドとなる、ハイブリッドクラスが定義できます。
単純なhello-worldコントローラーは、下記のようになります。
@Path("hello") public class HelloController { @GET @Controller public String hello() { return "hello.jsp"; } }
このサンプルでは、helloはJSPへのパスを返すコントローラーメソッドです。コントローラーメソッドの意味は、JAX-RSリソースメソッドとは少し異なります。特に、Stringの戻り値は、テキストコンテンツの代わりに、ビューのパスとして解釈されます。さらに、レスポンスのデフォルトのメディアタイプはtext/htmlとみなされます。しかしながらJAX-RSのように、@Produceでメディアタイプを指定することも可能です。
コントローラーメソッドの戻り値の型は、4つに限られます。
- void
voidの場合、@Viewの付加が要求されます。
- String
返されたStringは、ビューへのパスと解釈されます。
- Viewable
Viewableクラスは、ビューとそれがどのように処理されるべきかという情報をカプセル化したクラスです。
- Response
上記3つのいずれかをエンティティタイプとする、JAX-RSのResponseです。
下記のクラスでは、同じ意味を持つコントローラーメソッドが定義されています。
@Controller @Path("hello") public class HelloController { @GET @View("hello.jsp") public void helloVoid() { } @GET public String helloString() { return "hello.jsp"; } @GET public Viewable helloViewable() { return new Viewable("hello.jsp"); } @GET public Response helloResponse() { return Response.status(Response.Status.OK) .entity("hello.jsp").build(); } }
@Viewアノテーションはvoidのメソッドのみに適用でき、他の全ケースでは無視されます。
コントローラーメソッドの戻り値の型は上記のように制限がありますが、MVCはコントローラーメソッドに利用可能な引数の型には制限はありません。すなわち、JAX-RSリソースでインジェクションできる全ての引数の型は、MVCコントローラーでも利用可能です。同様に、フィールドやプロパティのインジェクションも制限されておらず、JAX-RSと完全に互換性があります(2.1.1で説明されている制約)。
2.1.1 コントローラーインスタンス
JAX-RSのリソースクラスがネイティブ(JAX-RSによって生成・管理される)・CDIビーン・Managed Bean・EJBにできるのと異なり、MVCクラスはCDI管理ビーンであることが要求されます。それに従い、JAX-RSリソースメソッドとMVCコントローラーの両方を含むハイブリッドクラスも、CDI管理されなければなりません。
JAX-RSのように、リソースクラスのデフォルトのインスタンスライフサイクルは「per-request」です。すなわち、コントローラークラスはリクエストごとにインスタンス化・初期化されなければなりません。実装(訳注:仕様MVCに対する実装のことと思われます)はCDIを通じて、他のライフサイクルをサポート可能です。他のライフサイクルであるJAX-RSクラスへの警告と同じものが、MVCクラスにも適用されます。これらのライフサイクルおよび警告については、JAX-RS 2.0のJSRを確認してください。
2.1.2 Viewable
Viewableは、ビューに関する情報と共に、オプションとして、それ(訳注:ビューと思われます)がどのように処理されるべきかの情報をカプセル化したクラスです。より正確には、Viewableインスタンスは、ModelsとViewEngineオブジェクトへの参照を含むことができます(詳しくは2.2およびChapter 6へ)。Viewableは、これら全てのオブジェクトへの従来のコンストラクタを定義します。それゆえ、CDI管理ビーンではありません。
より詳しいことについては、ViewableクラスのJavadocを参照してください。
2.1.3 Response
Responseオブジェクトを返すことで、アプリケーションはヘッダーを含むレスポンスの全部にアクセスすることが可能になります。例えば、Responseインスタンスは、エラー状態が発生した際に、HTTPステータスコードを変更することが可能です。JAX-RSは次に示すような、レスポンスを構築する流暢なAPIを提供しています。
@GET @Controller public Response getById(@PathParam("id") String id) { if (id.length() == 0) { return Response.status(Response.Status.BAD_REQUEST) .entity("error.jsp").build(); } ... }
Responseを直接利用することで、アプリケーションは、コンテンツタイプを上書きしたり、文字エンコーディングを設定したり、キャッシュ管理ポリシーを設定したり、HTTPリダイレクトを発生させたりすることが可能になります。より詳しいことについては、ResponseクラスのJavadocを参照してください。
2.2 モデル
MVCコントローラーは、Webアプリケーションページを生成するために、データモデルとビュー(テンプレート)を結合します。この仕様では2種類のモデルをサポートしています。1つはCDIベースの@Namedビーン、2つ目は名前とオブジェクトを紐付けたマップを定義しているModelsインターフェイスです。Modelsインターフェイスのサポートは、すべてのビューエンジンで必須です。CDIベース@Namedビーンのサポートはオプションですが強く推奨されます。アプリケーション開発者はいつでもサポートされているCDIベースのモデルを使うことが奨励されます。それゆえに、CDIとELのプラットフォーム上の統合という利点を得ることができます。
それでは、hello-worldの例に戻って、モデルがどのように変更されるか紹介します。モデルを使う方法は2つあると紹介しましたが、まずはCDIのケースで必要となる、CDI@Namedビーンを定義しました。
@Named("greeting") @RequestScoped public class Greeting { private String message; public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } ... }
JSPのビューエンジンが@Namedビーンをサポートしていることにより、すべてのコントローラーはモデルへの書き込みおよびビューを返すことが求められます。モデルにアクセスするには、CDIインジェクションを直接使えばよいです。
@Path("hello") public class HelloController { @Inject private Greeting greeting; @GET @Controller public String hello() { greeting.setMessage("Hello there!"); return "hello.jsp"; } }
もし、コントローラーによって返されたビューを処理するビューエンジンでCDIが有効でない場合は、代わりにコントローラーはModelsマップを使うことができます。
@Path("hello") public class HelloController { @Inject private Models models; @GET @Controller public String hello() { models.put("greeting", new Greeting("Hello there!"); return "hello.jsp"; } }
この例では、モデルは上記の@Namedアノテーションのものと同じ名前を与えられていますが、代わりにインジェクション可能なModelsマップを使っています。
上記のように、CDI@NamedビーンがModelsマップよりも推奨されますが、後者のサポートも、CDIを認識しないビューエンジンとの統合で必要になるでしょう。ビューエンジンとのもっと詳しい情報は、Chapter 6を参照してください。
2.3 ビュー
ビュー(時々テンプレートとしても参照される)は、出力ページの構造を定義し、1つまたは複数のモデルを参照できます。モデルからの情報の抽出と出力ページの生成により、ビューを処理(描画)することが、ビューエンジンの責任です。
ここに、hello-worldの例のJSPページを示します。
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Hello</title> </head> <body> <h1>${greeting.message}</h1> </body> </html>
JSPでは、ELを通してモデルのプロパティにアクセスできます。上記の例では、プロパティmessageがgreetingモデルから読まれています。greetingという名前は、2.2のコントローラーにより、@NamedアノテーションまたはModelsマップのキーで指定されたものです。