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を保持する方法を説明することにしましょう。

