Modelオブジェクト(ModelDriven)
ValueStackの構造にあるModelオブジェクトとは、Struts 2のModelDriven機能で用いるオブジェクトです。「Model」は、MVCモデル(Model-View-Controller)のM(odel)のことで、ひとまとまりのデータとそれを操作するオブジェクトを意味します。Struts 2のModelDrivenとは、そのようなModelオブジェクトを、Actionクラスとは別に管理するための機能です。
これまでのサンプルコードでは、直接Actionクラスのフィールドにデータをならべて、それぞれのSetterやGetterを、Actionクラスのメソッドとして定義していました。そのため、Actionクラスの定義がふくれあがり、クラスとしての構造が分かりにくくなっていました。
ModelDrivenを適用すれば、データをModelクラスとして分離して定義することができます。たとえ複数のActionクラスで同じデータにアクセスする場合でも、Actionクラスをシンプルに記述できるのです。
前述したように、Modelオブジェクトは、Actionクラスとは別にValueStackに保持されています。つまり、JSPページからActionクラスを経由せずに、直接Modelオブジェクトを指定して参照できるということです。
今回は、このModelDriven機能を使って、ActionクラスとViewレイヤーでデータを連携するアプリケーションを紹介します。
ModelDrivenのサンプル
ModelDrivenの仕組みは、ModelDrivenインターセプターによって実現されています。
ModelDrivenインターセプターは、デフォルトのインターセプターに含まれていて、パラメータの設定も不要です。
では、ごくかんたんなModelDrivenのサンプルを作ってみましょう。次の画面のような、本の情報を登録するアプリケーションです。
蔵書管理などのイメージで、入力-確認-登録実行というフローです。
ファイル構成
全体のファイル構成は、次のようになります。順に見ていきましょう。ここでは、「BooksSample1」のリンクで動作するファイルだけを示しています。ダウンロードできるサンプルには、後述するサンプルも含まれていて、books.actionパッケージに、すべてのクラス、contentフォルダ以下に、すべてのJSPファイルとなっています。
<ContextRoot> ├ /WEB-INF │ ├ /classes │ │ ├ /books.action │ │ │ ├ BooksModel.class │ │ │ └ BooksAction.class │ │ └ struts.xml │ ├ /content │ │ ├ booksRegister.jsp │ │ ├ booksResult1.jsp │ │ └ booksInput1.jsp │ ├ /lib │ │ └ 参照ライブラリー │ └ web.xml └ index.jsp
web.xml、struts.xmlは、前回とまったく同じです。
ModelDrivenインターフェイスの実装
Actionクラスで、ModelDrivenを利用するには、com.opensymphony.xwork2.ModelDrivenインターフェイスを実装する必要があります。インターフェイスといっても、次のようにgetModelというメソッドを実装するだけです。getModelでは、戻り値として、モデルとなるデータクラスのインスタンスを設定します。
public interface ModelDriven { public abstract Object getModel(); }
Actionクラスでの実装は、次のようになります。型パラメータを用いて、BooksModel
クラスを指定しています。また、フィールドの初期化時に、Modelオブジェクトをインスタンス化しています。
public class BooksAction extends ActionSupport implements ModelDriven<BooksModel> { private BooksModel model = new BooksModel(); @Override public BooksModel getModel() { return model; } ...中略... }
ModelオブジェクトとなるBooksModel
クラスは、登録情報の文字列をフィールドにして、それぞれのgetter/setterを定義したものです。
public class BooksModel { private String isbn; // ISBN private String title; // 書名 private String author; // 作者名 // 以下フィールドのGetter/Setter ...中略... }
なお、ダウンロードファイルのサンプルコードには、前回と同じような日本語処理の暫定処理を追加しています。
booksInput1.jsp
booksInput1.jspでは、登録用フォームを表示します。次のように、BooksModel
クラスのフィールド名と同じにします。
<s:textfield name="title" label="書名" /> <s:textfield name="author" label="著者" /> <s:textfield name="isbn" label="ISBN" />
submitボタンをクリックすると、booksアクションに遷移して、booksAction
クラスのexecute
メソッドが実行されます。その際、booksAction
クラスの変数model
には、入力した値がセットされています(なお、今回のサンプルでは、データ連携部分のみを説明する目的のため、バリデーション機能を利用していません)。
booksActionのexecute
メソッドでは、次のようにアノテーションによって、Action名(books)と、JSPファイルの指定を行っています。booksResult1.jspでは、入力結果を表示します。
@Action( value = "/books", results = { @Result(name = "success", location = "booksResult1.jsp") } ) public String execute() throws Exception { return "success"; }
booksResult1.jsp
booksResult1.jspでは、変数model
の内容を表示します。表示のタグは、次のように、<s:property>
タグを用います。前述したように、直接Modelオブジェクトが参照できますので、title
などのBooksModel
クラスのフィールド名をそのまま指定するだけです。
<s:property value="title" /> <s:property value="author" /> <s:property value="isbn" />
このJSPファイルでは、さらに次のように、「register」という別のActionに遷移するリンクをつけています。
<a href="<s:url action="register"/>">登録実行</a>
registerアクションで、データベースへの登録処理などを行うイメージです。このサンプルでは、次のように、booksAction
クラスのregister
メソッドを実行するようにしています。ただここでは、登録処理はなく、単に"success"を返すようにしています(データベースへの登録は、あとのサンプルで説明します)。
@Action( value = "/register", results = { @Result(name = "success", location = "booksRegister.jsp") } ) public String register() throws Exception { return "success"; }
booksRegister.jsp
booksRegister.jspは、booksResult1.jspとほとんど同じで、Modelオブジェクトを表示しています。次のような画面になります。
登録確認とほとんど同じなのに、じつはこちらの画面では値が表示できていません。というのも、Actionクラスは、URIのリクエスト単位でインスタンス化されるため、「登録実行」をクリックすると、新たにbooksAction
クラスがインスタンス化されて、変数model
も初期化されてしまうからです。この遷移では、フォームでの受け渡しデータはありませんから、変数model
には何も値が設定されないのです。値を表示したり、データベースに登録したりするには、別の手段で値をセットする必要があります。
このようなケースでデータを受け渡すには、いくつかの方法が考えられます。今回は、その中の一つ、セッションにModelを保持する方法を説明することにしましょう。