Struts2のblank.warを読んでみた

Struts2デファクトになるのかなぁ。どうだろう。わかんないけどかなり知っておいて損はないだろう。ということでStruts2の勉強開始。struts-2.0.9を対象に。

アクション

Struts2でのアクションクラスはActionSupportクラスを継承するようだ。

サンプルには以下のクラスが定義されている。

  • ExampleSupportクラス ... サンプル用のベースクラス
  • HelloWorldクラス ... メッセージを表示するアクションクラス
  • Loginクラス ... Login処理っぽいことを行うアクションクラス

ExampleSupportクラスはActionSupportクラスを継承しているだけで中身は空っぽ。
ActionSupportクラスはActionクラスをサポートしてくれるもの(そのままやな)。"success"をリターンするexecute()メソッドや、"input"をリターンするinput()メソッドなどを提供する。http://struts.apache.org/2.0.9/struts2-core/apidocs/com/opensymphony/xwork2/ActionSupport.html#input()

HelloWorldクラス

アクションを定義している部分はこれ。

package example;
/**
 * <code>Set welcome message.</code>
 */
public class HelloWorld extends ExampleSupport {
    public String execute() throws Exception {
        setMessage(getText(MESSAGE));
        return SUCCESS;
    }
...

execute()メソッドがアクションとして実行されるメソッド。Stringをリターンすればいいみたい。シンプル!!!ここでは、"success"という文字列をリターンしている。

getText(arg)というメソッドは、ActionSupportクラスが提供するもので、メッセージリソースからメッセージを取得するもの。

jsp

struts2のタグリブはこんな感じで定義する。

<%@ taglib prefix="s" uri="/struts-tags" %>

とかなどを利用できるようになる。http://struts.apache.org/2.0.9/docs/struts-tags.html

HelloWorld.jsp

HelloWorld.jspからStruts2のタグライブラリの使い方を見てみる。

    <title><s:text name="HelloWorld.message"/></title>

リソースファイルからnameで指定したキーに対応するプロパティに置き換えるタグ。
Generic TagsのData Tagsという種類。

<h2><s:property value="message"/></h2>

スタックからmessegeというプロパティの値に置き換えるタグ。おそらくスタック上にHello
Worldオブジェクトが乗っかっていれば、HelloWorldオブジェクトに対してgetMessage()が呼ばれているんだと思われる。
こいつも、Generic TagsのData Tagsという種類。

        <s:url id="url" action="HelloWorld">
            <s:param name="request_locale">en</s:param>
        </s:url>
        <s:a href="%{url}">English</s:a>

タグとタグでURL文字列を作っている。タグの%{url}で先ほど生成したURL文字列を参照している。
こいつらも、Generic TagsのData Tagsという種類。

Login.jsp

今度はLogin.jspから。

<s:form action="Login">
    <s:textfield key="username"/>
    <s:password key="password" />
    <s:submit/>
</s:form>

タグでformを定義している。サブミットしたときの遷移先(アクション)は、Loginアクションを指定している。
タグはテキストフィールドを、タグはパスワードフィールドを意味する。属性keyに定義してあるのは、Loginクラスのプロパティ名ということ。直感的だなー。

設定ファイル

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>

FilterDispatcherなるFilterがActionを実行する。すべてのリクエストをマッピングしないといけないようだ。http://struts.apache.org/2.0.9/struts2-core/apidocs/org/apache/struts2/dispatcher/FilterDispatcher.html

struts.xml

Struts1のときはstruts-config.xmlだったのだがstruts.xmlになった。あと、この設定ファイルの置き場所が、クラスパス上になった。サンプルでは、classes直下においてある。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
    "http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
    <constant name="struts.enable.DynamicMethodInvocation" value="false" />
    <constant name="struts.devMode" value="false" />
    <include file="example.xml"/>
    <!-- Add packages here -->
</struts>

タグの部分はstruts2が持っているプロパティを設定している部分。struts.enable.DynamicMethodInvocationの部分は、リクエストに、アクションクラス!メソッド.action、という形式でアクションを呼び出せないようにする、ってことらしい。ほぉほう。つぎに、struts.devModeの部分は、開発者モードじゃないよってことみたい。
struts-core-2.0.9.jarにorg/apache/struts2/default.propertiesなるファイルがあり、様々なプロパティのデフォルト値が定義してある。struts.devModeについてはこんな感じ。

### when set to true, Struts will act much more friendly for developers. This
### includes:
### - struts.i18n.reload = true
### - struts.configuration.xml.reload = true
### - raising various debug or ignorable problems to errors
### For example: normally a request to foo.action?someUnknownField=true should
### be ignored (given that any value can come from the web and it
### should not be trusted). However, during development, it may be
### useful to know when these errors are happening and be told of
### them right away.
struts.devMode = false

ふーん。

で最後に、タグでexample.xmlをインクルードする。Struts1のときはweb.xml側で分割したstruts-config.xmlを読み込むような設定をしていたから、楽になった感じがする。

example.xml

struts.xmlがインクルードする定義ファイル。アクションのマッピングが定義してある。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
        "http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
    <package name="example" namespace="/example" extends="struts-default">
        <action name="HelloWorld" class="example.HelloWorld">
            <result>/example/HelloWorld.jsp</result>
        </action>
        <action name="Login_*" method="{1}" class="example.Login">
            <result name="input">/example/Login.jsp</result>
            <result type="redirect-action">Menu</result>
        </action>
        <action name="*" class="example.ExampleSupport">
            <result>/example/{1}.jsp</result>
        </action>
        <!-- Add actions here -->
    </package>
</struts>

この定義の意味するところはこういうこと。

  • /example/HelloWorld.actionというリクエストに対してexample.HelloWorldクラスを対応付けている。execute()メソッドを実行した後に、/example/HelloWorld.jspに遷移する。
  • /example/Login_メソッドというリクエストに対してexample.Loginクラスのメソッドを対応付けている。呼び出したメソッドがinputという文字列を返した場合は、/example/Login.jspに遷移する。inputという文字列でない場合は、/example/Menu.actionにリダイレクトする。
  • /example/JSPファイル名.actionというリクエストに対してexample.ExampleSupportクラスを対応付けている。execute()メソッドを実行後、/example/JSPファイル名.jspに遷移する。

あと、この定義には明示されていないが、/example/Login.actionというリクエストに対して、example.Login#execute()が対応付けられている。#デフォルトのルールだろうか?

そのほかのファイル

Login-validation.xmlなるファイルを定義することで、Loginアクションのバリデーションを定義できるっぽい。

<validators>
    <field name="username">
        <field-validator type="requiredstring">
            <message key="requiredstring"/>
        </field-validator>
    </field>
    <field name="password">
        <field-validator type="requiredstring">
            <message key="requiredstring"/>
        </field-validator>
    </field>
</validators>

どこにもこのファイルを参照する定義は書いてないなぁ。アクションクラス名-validation.xmlとして、クラスパス上に配置するってことがルールなのかな。
定義ファイルは参照しやすくなっているみたいだけど、実際にバリデーション定義がやりやすいんだろうか?Struts1のバリデータプラグインとかではメンテがつらかったからなぁ。#テストのことを考えて、結局、ActionFormに書くことが多かった気がする。

ふぅ。