はじめに
Struts Webアプリケーションフレームワークが最初にリリースされてから10年近くが経ち、Strutsを利用して開発されたアプリケーションの数は数千に達します。Strutsは、当時から現在に至るまで、JavaベースのWebアプリケーションを作成するうえで最も人気の高いフレームワークです。Strutsにも欠点がないわけではありませんが、信頼性に優れ保守しやすいWebベースソフトウェアの構築を簡単に行うことができます。現在、Strutsを使用するアプリケーションの数は、競合する他のすべてのWebフレームワークを使用するアプリケーションを合わせた数の2倍はあるでしょう。JSF(JavaServer Faces)など、Strutsの後に登場した多くのフレームワークがStrutsの概念の多くをそのまま利用していることは、Strutsに対する賛辞に他なりません。
とはいえ、Strutsは決して完璧ではなく、近年は、別のソリューションの利用が大きく増えてきました。開発者の要望に対応するために、Strutsチームは、フレームワークのメジャーアップデートをリリースしました。これが、ご存知の「Struts 2」です。
本稿の目的は、Struts 1の開発者に対して、コードを参照しながらStruts 2の動作の概要を説明することです。そのため、Struts 1アプリケーション用とStruts 2アプリケーション用の2回分のサンプルアプリケーションを目にすることになります。ここですべてを理解することはできませんが、Struts 2の概要を知るには十分でしょう。
背景
Strutsが最初にリリースされたのは2001年でした。中心的な役割を果たしたのはCraig McClanahan氏でした。Struts以前のWebアプリケーションは、多くの場合、JSP(JavaServer Pages)スクリプトレット、Javaサーブレット、各種Javaクラスなど、Javaのさまざまな要素がごちゃごちゃと混在していました。このように場当たり的に作成されたアプリケーションは、一般にはテストが困難で、さらには保守することも困難でした。
StrutsによってMVC(Model-View-Controller)アーキテクチャが普及しました。MVCを使用することで、アプリケーションを拡張するための柔軟なモジュール式コードを作成できるようになりました。MVCスタイルの開発は、Webアプリケーションを作成するための優れた手法であることが経験的に証明されています。
ここ数年、Strutsの欠点を解決するべく、競合フレームワークが台頭してきました。そこで、Strutsコミュニティでは、これらの問題を解決するために次世代のStrutsに取り組み始めました。そのうちにWebWorkというフレームワークがStrutsの有力な後継者として名を馳せてきました。最終的に、StrutsチームとWebWorkは力を合わせることを決断し、これがStruts 2の出現へとつながりました。Struts 2は、多くの点でStruts 1より大幅に改善されています。
Struts 1のチュートリアル 1
前述のように、本稿ではStruts 1とStruts 2の違いを示すために、Struts 1フレームワークとStruts 2フレームワークで同じアプリケーションを作成する場合に使用されるコードを比較します。したがって、Struts 1を一通り理解していることが前提です。作成するアプリケーションの名前は「defect-tracker」です。このアプリケーションでは、ユーザーがアプリケーションの不具合(defect)を記録し、開発者がその不具合の解決策を記録します。アプリケーション自体は、最も基本的な要件から成る単純なものですが、ほぼすべてのWebアプリケーションに共通する基本的な機能を十分に扱うことができます。
Struts 1アプリケーションを開始するにあたって、最初に必要なファイルは「web.xml」構成ファイルです。Struts 1用に「web.xml」ファイルを変更して、アプリケーションURI要求をStrutsコントローラにルーティングできるようにする必要があります。
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet
</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<!--Map Struts-related request to action-->
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
Struts ActionServletは、パターン「*.do」に一致するURI要求を受け取ります。ActionServletは「struts-config.xml」構成ファイルの定義に従い、この要求にマップされているStruts Actionを検索します。ActionServletオブジェクトとActionオブジェクトは、StrutsアプリケーションのMVCアーキテクチャの「コントローラ」の部分に相当します。Struts Actionと「ビュー」(通常はJSPとして実装)との間でデータをやり取りするには、StrutsではActionFormオブジェクトを使用します。ActionFormオブジェクトは「モデル」に相当します。
ユーザーがフォームを送信すると、ActionFormとそのデータがActionのexecuteメソッドに渡されます。Actionはデータを検証し、必要なビジネスロジックを起動します(ビジネスロジックは、Strutsをまったく意識しない別の層にカプセル化しておくことが理想です)。ビジネスロジックが実行されると、Actionは、1つまたは複数のActionForwardオブジェクトで指定されているビューに制御を移します(これらのActionForwardオブジェクトも「struts-config.xml」で構成されています)。ActionForwardのエントリは、Actionの予想される結果を表します。これらは、Actionごとに定義することも、すべてのActionに適用されるように全体で定義することもできます。
「struts-config.xml」ファイルの一部を以下に示します。defect-trackerアプリケーションには、ActionとActionFormのセットが2つあります。1つ目は既存の不具合エントリのリストを扱うもので、2つ目は不具合データの操作(いわゆる「CRUD(Create-Update-Delete)」)を扱うものです。
struts-config.xml
<global-forwards>
<forward name="list" path="/list.do"/>
</global-forwards>
<form-beans>
<form-bean name="listForm" type="web.DefectsListForm" />
<form-bean name="actionForm" type="web.DefectsActionForm" />
</form-beans>
<action-mappings>
<action
path="/list"
name="listForm"
scope="request"
type="web.DefectsList"
validate="false">
<forward name="list" path="/pages/defects.jsp" />
</action>
<action
path="/action"
name="actionForm"
scope="request"
type="web.DefectsAction"
parameter="method"
validate="false">
<forward name="edit" path="/pages/editDefect.jsp" />
<forward name="add" path="/pages/addDefect.jsp" />
<forward name="list" path="/list.do" redirect="true" />
</action>
</action-mappings>
「struts-config.xml」内の最後の2つのエントリでは、アプリケーションのメッセージとラベルを表示するプロパティファイルに加えて、アプリケーション内のActionFormオブジェクトの検証ルールを追加するために使用する別の構成ファイルを宣言しています。
defect-trackerアプリケーションには、Defectという1つのドメインオブジェクトがあり、これがシステムの不具合エントリを表します。起動時に、ユーザーは「list.do」にリダイレクトされます。このファイルは、不具合エントリのリストを取得するDefectsList Actionオブジェクトのexecuteメソッドにマップされています。
DefectsListのexecuteメソッド
public ActionForward execute(
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response) {
DefectsListForm defectsListForm = (DefectsListForm) form;
//Get data from business layer and assign it to the form
//object...
//...
return mapping.findForward("list");
}
executeメソッドはアプリケーションデータを取得し、ActionForm DefectsListingActionFormクラスのsetDefects()メソッドを使用して、そのデータをDefectインスタンスのコレクションに割り当てます。
DefectsListFormのDefectsプロパティ
private List defects;
public List getDefects() {
return defects;
}
public void setDefects(List defects) {
this.defects = defects;
}
「defects.jsp」ファイルは、Struts 1タグライブラリを使用してこれらの不具合エントリを表示し、「struts-config.xml」で宣言した「.properties」ファイルからラベルとメッセージを表示します。
Struts 1のチュートリアル 2
すべての不具合の表示(defects.jsp)
ユーザーは、不具合を追加、編集、または削除できます。たとえば、ユーザーがレコードを編集する場合、ハンドラはまずHTTPServletRequestからIDを取得し、それをActionのexecuteメソッドにパラメータとして渡します。executeメソッドは、次に、ビジネス層内のメソッドを呼び出して不具合を取得し、それをデータが読み込まれる編集フォームに転送します。新しい不具合を追加する場合は、空白のフォームが表示されます。
ユーザーが自分で入力した不具合を解決することはあり得ないため、追加フォームにはフィールドのサブセットのみが表示されます。
新しい不具合に関する情報の入力(addDefect.jsp)
不具合の更新(editDefect.jsp)
Struts 1では、入力文字列の変換が常に適切に行われるとは限らないため、場合によっては、開発者がフォームデータを変換し、それをドメインオブジェクトに渡す必要があります。
DateConverterクラスは、日付フィールドに対してこの処理を行います。追加、編集、または削除が正しく行われると、ユーザーはリストに再び戻ります。最初のActionであるDefectsListは、そのexecuteメソッドを自動的に呼び出しました。executeメソッドで事足りる場合も多いのですが、関連するアクションを開発者が1つのActionオブジェクトにまとめることができるように、Struts 1にはDispatchActionクラスが用意されています。DispatchActionでは、methodという要求パラメータを使用して、呼び出すメソッドをStruts Actionに指示します。したがって、executeに代わって、add、edit、deleteなどのメソッドを定義して、各種類の要求を処理できます。
DispatchActionを使用するには、アプリケーションのActionオブジェクトでActionではなくDispatchActionを拡張し、呼び出すメソッドを識別するフォームパラメータを定義する必要があります。DefectsActionから不具合を削除するメソッドを次に示します。
DefectsActionのdeleteメソッド
public ActionForward delete(
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response) {
long id = Long.parseLong(request.getParameter("id"));
//Invoke business layer to delete the object using the id...
return mapping.findForward("list");
}
Struts 1開発者は、Apache Commons Validatorフレームワークを使用してデータを検証できます。検証を有効にするには、「struts-config.xml」ファイルにValidatorプラグインを登録し、ActionFormsがActionFormではなくValidatorFormを拡張する必要があります。
検証ルールは「validation.xml」ファイルで宣言できます。たとえば、不具合の説明が必須であることが「validation.xml」ファイルで宣言されているものとします。その場合、レコードを保存する前に、Actionによってvalidateメソッドが呼び出されます。ユーザーが説明を入力していなかった場合は、Struts ActionErrorsコレクションが読み込まれ、その内容が<html:errors/>タグによってビューページに表示されます。
<form name="actionForm">
<field property="defect.description" depends="required">
<arg0 key="defects.description"/>
</field>
</form>
保存時のフォームの検証
ActionMessages errors = form.validate(mapping, request);
if (!errors.isEmpty()) {
saveErrors(request, errors);
「addDefect.jsp」に表示される検証エラーメッセージ
Struts 1バージョンのdefect-trackerに関する説明も、終わりに近づいてきました。Struts 1の復習だと思って読んでいただければ幸いです。しかし、これまでに作成してきたアプリケーションは、このままでは実際に動作しません。StrutsはWebフレームワークを提供しますが、ビジネスロジックを実装したり、データをデータベースに保存するアプリケーション部分については役立ちません。
ビジネスロジックは、EJB(Enterprise JavaBean)で実装される個別のビジネス層か、Strutsの概念を持たないプレーンなJavaクラスにまとめるのが最適です。ビジネス層ロジックを編成するのに適したフレームワークとして評判が良いのが、Springフレームワークです。Springは、フレームワークに不慣れな人でも利用できる依存性注入タイプのフレームワークです。Springでは、Javaクラスとその依存関係を構成ファイルで宣言しておくと、実行時に依存オブジェクトを手動で取得しなくても、自動的に注入されます。さらに、Springには多くの便利な機能が用意されており、さまざまなJavaフレームワークおよびツールセットとの統合も可能です。
defect-trackerの場合、私は主に2つの理由でSpringフレームワークを選択しました。第一に、Springは非常に一般的であり、Strutsとの統合性に優れています。多くのStruts 1アプリケーションでも、既にSpringが使用されています。第二に、詳しくは後述しますが、Struts 2ではSpringとの統合性がさらに強まり、Strutsとの間での引数の処理も向上しています。
Spring開発者はかなりの割合でStrutsを使用するため、SpringにはStruts 1との連携のためのクラスが用意されています。Strutsを使用するには2つの方法があり、1つはSpringのContextLoaderPluginを使用して注入を管理するという方法で、もう1つはSpringのActionSupportクラスを使用してSpringのアプリケーションコンテキストを取得するという方法です。どちらの方法もごく簡単に実装できます。
本稿のテーマはStrutsであるため、アプリケーションのビジネス層とデータ層については簡単に触れるにとどめますが、今回のアプリケーションではこの2つの層にSpringを使用し、永続化についてはHibernateフレームワークを利用しました。Springとdefect-trackerアプリケーションを連携させるための手順を、以下に簡単に示します。
最初に、「web.xml」ファイル内でSpring構成ファイルを指定する必要があります。
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/application-context.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.
ContextLoaderListener</listener-class>
</listener>
SpringのActionSupportクラスを使用するのが最も簡単な方法であり、ここではその方法を使用しています。ActionメソッドでSpringが管理するBeanを取得し、それを使用してビジネスロジックを呼び出します。たとえば、DefectsActionクラスのsaveメソッドでSpringのコンテキストを取得し、DefectManagerビジネスインターフェイスの実装で提供されるメソッドを呼び出します。
...
WebApplicationContext ctx = getWebApplicationContext();
DefectsManager mgr = (DefectsManager) ctx.getBean("defectsManager");
mgr.saveDefect(defect);
...
DefectManagerにはビジネスルールをコーディングします。たとえばユーザーが不具合に対して解決方法を入力したら、DefectManagerは自動的にそのユーザーを監査し(もちろん本当のアプリケーションで実際の名前を取得します)、DefectのisResolvedフラグをtrueに設定します。すると、SpringはHibernateを呼び出して、必要なデータベース処理を行います。Spring対応のActionクラス(DefectsListおよびDefectsAction)の完全なリストについては、リンク先を参照してください。
Struts 2の場合 1
Struts 1について理解したところで、もう1つのdefect-trackerを見てみましょう。今度はStruts 2を使用します。Struts 1とStruts 2の比較のために、設計はあまり変えないようにしたいと思います。
defect-trackerをStruts 2アプリケーションとして実装するには、まず「web.xml」ファイルを変更する必要があります。Struts 1では、ActionServletという名前のJavaサーブレットを使用して、特定のURIパターン(既定では「*.do」)に一致する要求をインターセプトしました。
Struts 2では、代わりにサーブレットフィルタを使用します。Struts 2の既定の拡張子は「*.do」ではなく、「*.action」です。この拡張子をフィルタマッピングで指定する必要はなく、これが前提となります。これは、多くの既定のStrutsプロパティと同様、「struts.properties」ファイルを編集し、classpathに配置することによってオーバーライドできます。Strutsの各バージョンはそれぞれ異なる方法で要求をマップするため、同じアプリケーションでStruts 1とStruts 2の両方を一緒に使用しても問題はありません。
Struts 2の「web.xml」内のフィルタマッピング
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.
FilterDispatcher</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Struts 1では、Strutsの構成に「struts-config.xml」ファイルを使用しました。たとえば、Actionへのパス名の割り当て、そのActionに伴うActionFormの定義、さまざまな種類のAction結果に対するActionForwardの指定は、「struts-config.xml」で行いました。
Struts 2では、この点が大きく変わっています。まず、Struts 2では「struts-config.xml」が「struts.xml」という名前のファイルに置き換わりました。「struts.xml」ファイルは、classpath内のどこにでも配置できます。「struts.xml」をよく見ると、いくつかの大きな変更に気づきます。何よりもまず、ActionFormが見当たりません。Struts 2では、開発者がページのデータを格納する場所について、制限を設けていません。たとえば、フォームデータをActionクラスから取得することができます(Struts 2ではActionが重要です)。また、Struts 2では、ActionForwardオブジェクトがResultオブジェクトに置き換わりました。Struts 1と同様、Actionは、「struts.xml」内のエントリに一致する文字列結果を返します。
defect-tracker内の2つのActionが、Struts 2ではどのように構成されているかを、以下に示します。要素名もそれぞれ変わっていることに注意してください。ほとんどの場合、この新しい要素名は、それぞれの内容を直感的に表すものとなっています。
<struts>
<include file="struts-default.xml"/>
<constant name="struts.custom.i18n.resources"
value="MessageResources" />
<package name="default" extends="struts-default">
<action name="list" class="web.DefectsList">
<result>/pages/defects.jsp</result>
</action>
<action name="action_*" method="{1}"
class="web.DefectsAction">
<result name="input">/pages/editDefect.jsp</result>
<result type="redirect">list.action</result>
</action>
</package>
</struts>
「struts.xml」内では、関連するアクションをパッケージ(package)にグループ化します。ここでは、既定のパッケージをそのまま使用します。もしたとえば「crud」という名前のパッケージを作成し、同様のタイトルをnamespace属性に指定した場合は、そのパッケージ内のActionに対応するURI要求は「/crud/*.action」という形式になります。開発者はここで定数を定義することにより、Struts 2フレームワーク内のいくつかの既定の設定をオーバーライドできます。Action宣言を見るとわかるように、1つの結果を名前なしで指定することができます。
Struts 2では、この既定のエントリが「success」結果とみなされるため、名前を指定する必要がありません。本稿の前半部分で説明したように、Struts 1では、DispatchActionを使用して不具合のCRUDイベントを処理していました。Struts 2では、DispatchActionを拡張する必要はありません(実際、Struts 2にDispatchActionは存在しません)。Struts 2では、特定のActionメソッドに制御を移すのではなく、ワイルドカード/プレースホルダのメカニズムを使用できます。たとえば、Struts 2のDeffectsActionでは、「action_go」というURIによって、対応するActionクラスのgoメソッドが呼び出されます。
「list.action」URI要求は、Struts 2バージョンのDefectsList Actionクラスにマップされます。そのコードを以下に示します。
package web;
import business.DefectsManager;
import com.opensymphony.xwork2.ActionSupport;
import java.util.List;
public class DefectsList extends ActionSupport {
private List defects;
private DefectsManager defectsManager;
public List getDefects() {
return defects;
}
public void setDefects(List defects) {
this.defects = defects;
}
public void setDefectsManager(DefectsManager defectsManager) {
this.defectsManager = defectsManager;
}
public String execute() {
this.defects = this.defectsManager.getDefects();
return SUCCESS;
}
}
元のDefectsList Actionクラスと比較してみると、このコードの方がずっとすっきりしていますね。Struts 2 ActionSupportクラスを拡張する必要はありませんが、多くの有用なメソッドや定数にアクセスできます(結果として使用するSUCCESS定数など)。コードがこれだけすっきりしたのは、以前はexecuteメソッドに4つもパラメータがあったのに、今度は1つもなくなったからです。不要になったパラメータの1つはActionForm用であり、ActionFormはもうありません。さらに他のパラメータも、Struts 2が内部でSpringフレームワークを使用することにより、HTTPServletRequestオブジェクトなどの依存オブジェクトが実行時に必要に応じて注入されるようになったため、不要になりました。
Struts 2において依存性の注入と同じくらい重要な概念に、インターセプタの使用があります。インターセプタは、基本的に、Struts 2フレームワークで提供されるJavaクラスです。インターセプタを使用すると、Action実行の前後にロジックを実行できます。多くのStruts 2インターセプタが最初から用意されていますが、独自のインターセプタも作成できます。通常、インターセプタを使用するには、Actionクラスが適切なインターフェイスを実装します。次に、これが、実行するジョブを持つそのインターフェイスに関連付けられているインターセプタを呼び出します。
SpringはStruts 2に組み込まれているため、Spring Beanの注入は、Springマネージドビーンのsetterメソッドを作成するのと同じくらい簡単です。Struts 2は、この依存オブジェクトを実行時に自動的に注入します。Struts ActionクラスをSpring BeanとしてSpringの構成ファイル内で宣言することもできます。詳細については、Struts 2のドキュメントを参照してください。
Struts 2の「list」アクション(defects.jsp)の結果を示します。Struts 2タグライブラリは、Struts 1タグライブラリより使いやすく、ずっと強力です。タグライブラリの注目すべき機能の1つが、テーマの使用です。設定するテーマに応じて、タグは定型化された多くのマークアップを生成し、ページがごちゃごちゃになるのを防ぎます。テーマは全体で設定することも、個々のタグに対してオーバーライドすることもできます。ここでは、生成される出力がStruts 1バージョンのdefect-trackerに似るように、後者の方法を使用しています。リストページ上のStruts 2タグの例を示します。
<!--passes id as parameter to the input method on our Action-->
<s:url id="editLink" action="action!input">
<s:param name="id" value="%{id}" />
</s:url>
<!--Displays a formatted date as a label-->
<s:date name="submittedOn" format="yyyy-MM-dd hh:mm" />
Struts 2のDefectsActionは、含まれるメソッド数を考えると、DefectsListよりすっきりしています。対処するActionFormもなく、長々としたメソッドAPIもありません。また、DefectsListの場合と同様、Springビジネス層に簡単にアクセスできます。完全なコードを以下に示します。
package web;
import business.DefectsManager;
import business.data.Defect;
import com.opensymphony.xwork2.ActionSupport;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts2.interceptor.ServletRequestAware;
import org.aspectj.weaver.patterns.ThisOrTargetAnnotationPointcut;
public class DefectsAction extends ActionSupport
implements ServletRequestAware {
private Defect defect;
private DefectsManager defectsManager;
private HttpServletRequest request;
public Defect getDefect() {
return defect;
}
public void setDefect(Defect defect) {
this.defect = defect;
}
public void setDefectsManager(DefectsManager defectsManager) {
this.defectsManager = defectsManager;
}
public void setServletRequest(HttpServletRequest
httpServletRequest) {
this.request = httpServletRequest;
}
public String input() {
if (request.getParameter("id") != null) {
long id = Long.parseLong(request.getParameter("id"));
this.defect = this.defectsManager.getDefect(id);
}
return INPUT;
}
public String delete(){
long id = Long.parseLong(request.getParameter("id"));
this.defectsManager.removeDefect(id);
return SUCCESS;
}
public String save() {
this.defectsManager.saveDefect(this.defect);
return SUCCESS;
}
}
Struts 1と同様、HTTPServletRequestにアクセスして、不具合のIDを取得する必要があります。このIDを取得するために、DefectsActionは、使用する中でおそらく最も一般的なStruts 2インターセプタを利用します。ServletRequestAwareインターフェイスを実装し、要求のsetterメソッドを追加すると、フレームワークによって、必要なときにそれを使用できます。このクラスのinputメソッドは、Struts 1版のアプリケーションのaddメソッドとeditメソッドに代わるものです。IDがあれば、不具合を照会します。それ以外の場合は、そのまま継続して結果を求めます。これが、「editDefect.jsp」になります。
Struts 2では、ページにnullオブジェクトへの参照が渡されると、自動的に新しいオブジェクトが作成されます。新しいDefectオブジェクトを作成するときには、まさにこれが必要です。
Struts 2の場合 2
不具合エントリを作成/変更するためのデータフォーム
<s:form action="action!save" method="post" >
<s:hidden name="defect.id" value="%{defect.id}" />
<s:textfield label="%{getText('defect.description')}"
name="defect.description" value="%{defect.description}"
size="100" maxlength="200" required="true" />
<s:select label="%{getText('defect.priority')}"
name="defect.priority" value="%{defect.priority}"
list="#{'1':'1','2':'2','3':'3','4':'4'}" />
<br/>
<s:if test="defect.id > 0">
<s:textfield label="%{getText('defect.resolution')}"
name="defect.resolution" value="%{defect.resolution}"
size="100" maxlength="200" />
</s:if>
<br/>
<s:submit value="%{getText('defect.editrecord')} " />
<s:submit value="%{getText('defect.cancel')}" action="list" />
</s:form>
Struts 2では、式をサポートするためにOGNLライブラリを利用しています。これによって、ラベルとモデルデータを一緒に効率的に表示できます。Struts 2の<if>タグを使用すると、既存のIDが見つかったかどうかに応じて、resolutionフィールドの表示を切り替えることができます。フォームのaction属性には「action!save」が設定されていますが、これは、送信時にsaveメソッドが呼び出されることを表します。
最後に、Struts 2における検証について簡単に説明します。Struts 2では、WebWorkで提供される検証フレームワークが使用されています。似たような宣言的な検証用のメソッドも提供されていますが、WebWorkの方がきめ細かな対応が可能です。検証には、モデル単位の検証と、アクション単位の検証の2種類があります。アクションレベルで検証するには、「{your action}-validation.xml」という名前のファイルを作成し、それをActionクラスと同じパッケージに配置します。
モデルレベルで検証するには、モデルオブジェクトの名前を持つ同様のファイルを作成します。一般的な方法では、「Defect-validation.xml」ファイルで行ったのと同様にモデルレベルの検証を行い、次に、Actionの検証ファイルで、モデルの検証ファイル内のルールごとに検証を指示します。
Defect-validation
<validators>
<field name="defect.description">
<field-validator type="requiredstring">
<message key="errors.required"/>
</field-validator>
</field>
</validators>
Actionの検証(「type="visitor"」属性を指定することでモデルに検証を委任)
<validators>
<field name="defect">
<field-validator type="visitor">
<param name="appendPrefix">false</param>
<message/>
</field-validator>
</field>
</validators>
Struts 2では、既定で検証が有効です。validateメソッドを呼び出すことはありません。Struts 2では、inputを返すActionについては検証の呼び出しが行われないため、検証が不要な場合にこれを抑制するコードを作成しなくて済みます。Struts 2には、検証エラーメッセージを表示するための類似のタグセットがあります。
まとめ
Struts 2には理解すべき点が多いことは否定できません。多くの変更が加えられてはいますが、このフレームワークは全体にわたって明らかにStruts 1の影響を受けています。そのうえ、Struts 2には多くの新しい機能が追加されているので、Struts 2はStruts 1の優れた後継者と言えるでしょう。
本稿では、Struts 1とStruts 2を比較し、その概要を説明しました。本稿で使用したソースファイルは、本稿の冒頭のリンクからダウンロードできます。Struts 2のWebサイトには、本稿では扱っていない詳細情報が記載されており、アクセスしてみることを強くお勧めします。また、そのWebサイトに用意されているチュートリアルとドキュメントを参照して、フレームワークについて理解を深めてください。