Struts2 は WebWorks2 をベースとした MVC フレームワークです。Struts1 と変わらずコマンドパターンのフレームワークになっています。
コマンドパターンの実装部分では、OPENSYMHONY の XWork が使われています。
Struts2の特徴
- WebWorks の後継となる WebWorks2 がベースの MVC フレームワーク
- コマンドパターンが使われている(XWork が使われている)
- アノテーションと XML ファイルによる設定
- ActionForm がない。代わりにアクションにフォームデータを格納する
- アクションは POJO で作成できる
- アクションがスレッドセーフ
- 設定ファイルに OGNL 式が書ける
- View に JSP、Freemaker、Velocity、XSLT が使える
- SpringFramework との連携が考慮されている
- Ajax をサポートしている
- プラグインによりフレームワークの拡張が行える
情報が正確ではない可能性が大いにあるので(ドキュメント読まずにソース見ながら書いているので・・・)、鵜呑みにしないでください。
随時更新予定です。
執筆時の環境
- JDK1.5.12
- Struts2 2.0.11
目次
アーキテクチャ
アーキテクチャ概要図
主なクラス(Struts2、XWork)
org.apache.struts2.dispatcher.FilterDispatcher
Struts2 のリクエストはすべてこのフィルタが受け付け、適切なクラスに処理をディスパッチします。
ディスパッチする先の Dispatcher クラスは、FilterDispatcher#init(FilterConfig) で作成、初期化されます。
- URLパターン
-
フィルタの url-pattern は「/*」にすることが推奨(JavaDoc によると MUST)されています。
<filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
「/struts/」で始まるパスがリクエストされると、FilterDispatcher は静的リソースを検索します。
たとえば「http://localhost:8080/struts2/struts/ajax/dojoRequire.js」のようにアクセスされた場合「/struts/」以下のパスをクラスパスから検索します。
デフォルトでは、
- org.apache.struts2.static
- template
- org.apache.struts2.interceptor.debugging
のクラスパスが指定されていますので、上記URLにアクセスされた場合は「/org/apache/struts2/static/ajax/dojoRequire.js」「/template/ajax/dojoRequire.js」「/org/apache/struts2/interceptor/debugging/dojoRequire.js」からマッチする静的コンテンツを返すようになります。
org.apache.struts2.dispatcher.mapper.ActionMapper
Httpリクエストとアクションのマッピングを管理します。リクエストに対応する適切なアクション情報を ActionMapping
オブジェクトとして返します。
org.apache.struts2.dispatcher.Dispatcher
Dispatcher クラスは Struts2 の設定情報を保持し、 FilterDispatcher から呼び出され実際にリクエストをディスパッチします。
Dispatcher インスタンスはすべてのリクエストで共有されます。
com.opensymphony.xwork2.config.ConfigurationManager
設定情報を司るクラスです。複数の ConfigurationProvider
を持ち、ConfigurationProvider
から情報を受け取り Configuration
インスタンスを作成します。
com.opensymphony.xwork2.config.ConfigurationProvider
設定情報を提供するクラスです。設定情報ごとにクラスを用意します。たとえば、struts.xml ファイルの設定情報を読み込み、Configuration
に設定するのは org.apache.struts2.config.StrutsXmlConfigurationProvider
クラスになります。
com.opensymphony.xwork2.config.Configuration
設定情報を表すクラスです。
com.opensymphony.xwork2.ActionProxy
アクションのプロキシとなるクラスです。アクションの呼び出しを ActionInvocation
に委譲します。
ActionProxy
の内部では、ActionInvocation
が呼び出されます。
com.opensymphony.xwork2.ActionInvocation
アクションの呼び出しを行います。アクションの実体とインタセプタを保持しています。
ActionProxy
によって初期化され、起動されます。ActionProxy
によって invoke() メソッドが呼び出された後は、インタセプタによって invoke() が再帰呼び出しされます。
すべてのインタセプタの呼び出しが完了した後アクションが呼び出されます。その後、Result の呼び出しが行われます。
Result の実装として、ServletDispatcherResult
などがあります。Result は View の実装(ユーザへのレスポンス)に結び付けられています。
com.opensymphony.xwork2.interceptor.Interceptor
アクションの処理を実行する前や後に処理を追加します。
インターセプタはステートレスなクラスとして作成しなければなりません。
Action
ActionInvocation
から呼び出されるアクションクラスです。
デフォルトでは、ActionInvocation
から execute
メソッドが呼び出されます。
public String execute() throws Exception
概要クラス図とシーケンス図
Struts2 では、すべてのリクエストが org.apache.struts2.dispatcher.FilterDispatcher
を通して処理されます。
FilterDispatcher
はリクエストパスからどのアクションを呼び出すかを解決し、org.apache.struts2.dispatcher.Dispatcher
の serviceAction
を呼び出します。
Dispatcher
は serviceAction
の中で、ActionProxyFactory
を使って ActionProxy
を作成して
実際にアクションを実行するのは、ActionInvocation
の役割になります。
Struts2 の主な機能
コンフィグレーションファイルの指定
web.xml の Filter の設定で init-param に config というパラメータを設定すると、Struts2 の設定ファイルを指定できます。
デフォルトは「struts-default.xml,struts-plugin.xml,struts.xml」になっています。このファイルは、クラスパス上から検索されます。
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
<init-param>
<param-name>config</param-name>
<param-value>
struts-default.xml,struts.xml,my-struts.xml
</param-value>
</init-param>
</filter>
Zero コンフィグレーション
web.xml の Filter の設定で init-param に actionPackages というパラメータを設定すると、Struts2 はこのパッケージにあるクラスで、com.opensymphony.xwork2.Action インターフェースを実装したクラスか、「Action」で終わるクラス名を持つクラスを自動で Action クラスとして認識してくれます。
「,」で区切ることで、複数パッケージ指定することもできます。
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
<init-param>
<param-name>actionPackages</param-name>
<param-value>
com.hamasyou.struts2.webapp
</param-value>
</init-param>
</filter>
上記のように設定した場合、
「/usecase1/itemSearch.action」にアクセスすると「com.hamasyou.struts2.webapp.usecase1.ItemRegisterAction」が呼び出されます。
「/usecase2/admin/itemRegister.action」にアクセスすると「com.hamasyou.struts2.webapp.usecase2.admin.ItemRegisterAction」が呼び出されます。
また、「/usecase1/itemSearch!allSearch.action」のように「!メソッド名」でアクションを呼び出すと、指定されたメソッド名が呼び出されます。!メソッド名を指定しない場合は execute メソッドが呼び出されています。
Struts2 のアクションの呼び出しは「.action」という形になります。(Struts1のときは .do でした。)
カスタム設定読み込み
Struts2 の起動時に、カスタムプロパティを読み込むことが出来る。
web.xml の init-param に configProviders というパラメータを設定すると、Struts2 はこのパラメータに指定されたクラスからカスタムプロパティの読み込みを行うようになります。
指定するクラスは、com.opensymphony.xwork2.config.ConfigurationProvider を実装する必要があります。
「,」で区切ることで、クラスを複数指定できます。
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
<init-param>
<param-name>configProviders</param-name>
<param-value>
com.hamasyou.struts2.webapp.config.MyCustomeConfigurationProvider
</param-value>
</init-param>
</filter>
Dependency Injection
メソッドかフィールドに @Inject を指定することで、型によるインジェクションが行われます。
@Inject の例
@Inject
private MyObject myObject;
private MyObject2 myObject2;
@Inject
public void setMyObject2(MyObject2 myObject2) {
this.myObject2 = myObject2;
}
public String execute() throws Exception {
System.out.println("myObject:" + this.myObject);
System.out.println("myObject2:" + this.myObject2);
return "success";
}
インジェクションするオブジェクトの設定は、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>
<bean class="com.hamasyou.struts2.webapp.MyObject" />
<bean class="com.hamasyou.struts2.webapp.MyObject2" />
</struts>
myObject:com.hamasyou.struts2.webapp.MyObject@135d18b myObject2:com.hamasyou.struts2.webapp.MyObject2@50078e
インターセプタ
アクションの呼び出しの前に、ActionInvocation
によってインタセプタが呼び出されます。
struts-default.xml を見ると、デフォルトのインターセプタとして以下の設定がされているのがわかります。
struts-default.xml のインターセプタの設定
<interceptors>
<interceptor name="alias" class="com.opensymphony.xwork2.interceptor.AliasInterceptor"/>
<interceptor name="autowiring" class="com.opensymphony.xwork2.spring.interceptor.ActionAutowiringInterceptor"/>
<interceptor name="chain" class="com.opensymphony.xwork2.interceptor.ChainingInterceptor"/>
<interceptor name="conversionError" class="org.apache.struts2.interceptor.StrutsConversionErrorInterceptor"/>
<interceptor name="cookie" class="org.apache.struts2.interceptor.CookieInterceptor"/>
<interceptor name="createSession" class="org.apache.struts2.interceptor.CreateSessionInterceptor" />
<interceptor name="debugging" class="org.apache.struts2.interceptor.debugging.DebuggingInterceptor" />
<interceptor name="externalRef" class="com.opensymphony.xwork2.interceptor.ExternalReferencesInterceptor"/>
<interceptor name="execAndWait" class="org.apache.struts2.interceptor.ExecuteAndWaitInterceptor"/>
<interceptor name="exception" class="com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor"/>
<interceptor name="fileUpload" class="org.apache.struts2.interceptor.FileUploadInterceptor"/>
<interceptor name="i18n" class="com.opensymphony.xwork2.interceptor.I18nInterceptor"/>
<interceptor name="logger" class="com.opensymphony.xwork2.interceptor.LoggingInterceptor"/>
<interceptor name="modelDriven" class="com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor"/>
<interceptor name="scopedModelDriven" class="com.opensymphony.xwork2.interceptor.ScopedModelDrivenInterceptor"/>
<interceptor name="params" class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/>
<interceptor name="prepare" class="com.opensymphony.xwork2.interceptor.PrepareInterceptor"/>
<interceptor name="staticParams" class="com.opensymphony.xwork2.interceptor.StaticParametersInterceptor"/>
<interceptor name="scope" class="org.apache.struts2.interceptor.ScopeInterceptor"/>
<interceptor name="servletConfig" class="org.apache.struts2.interceptor.ServletConfigInterceptor"/>
<interceptor name="sessionAutowiring" class="org.apache.struts2.spring.interceptor.SessionContextAutowiringInterceptor"/>
<interceptor name="timer" class="com.opensymphony.xwork2.interceptor.TimerInterceptor"/>
<interceptor name="token" class="org.apache.struts2.interceptor.TokenInterceptor"/>
<interceptor name="tokenSession" class="org.apache.struts2.interceptor.TokenSessionStoreInterceptor"/>
<interceptor name="validation" class="org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor"/>
<interceptor name="workflow" class="com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor"/>
<interceptor name="store" class="org.apache.struts2.interceptor.MessageStoreInterceptor" />
<interceptor name="checkbox" class="org.apache.struts2.interceptor.CheckboxInterceptor" />
<interceptor name="profiling" class="org.apache.struts2.interceptor.ProfilingActivationInterceptor" />
<interceptor name="roles" class="org.apache.struts2.interceptor.RolesInterceptor" />
インターセプタは、塊ごとに名前をつけて管理することが出来ます。それが、インターセプタスタックと呼ばれるものです。
struts-default.xml には、以下のような設定がされています。
<!-- Basic stack -->
<interceptor-stack name="basicStack">
<interceptor-ref name="exception"/>
<interceptor-ref name="servletConfig"/>
<interceptor-ref name="prepare"/>
<interceptor-ref name="checkbox"/>
<interceptor-ref name="params"/>
<interceptor-ref name="conversionError"/>
</interceptor-stack>
アクションは、次のインターセプタスタックがデフォルトで適用されるようになっています。
<default-interceptor-ref name="defaultStack"/>
struts-default.xml を見るとわかりますが、これらのインターセプタの設定はパッケージごとに行うようです。上記のインターセプタ、インターセプタスタックは、「struts-default」というパッケージに定義されています。
自分たちのアクションを struts.xml に登録した際のパッケージは、struts-default を継承する必要がありそうです。
struts.xml の例
<package name="mypackage" namespace="/mypackage" extends="struts-default">
<action
name="itemSearch"
class="com.hamasyou.struts2.webapp.usecase1.ItemSearchAction">
<result name="success">/WEB-INF/jsp/top/top.jsp</result>
<interceptor-ref name="debugging" />
</action>
<action
name="itemRegister"
class="com.hamasyou.struts2.webapp.usecase2.admin.ItemRegisterAction">
<result name="success">/WEB-INF/jsp/top/top.jsp</result>
<interceptor-ref name="executeAndWaitStack" />
</action>
</package>
ここでは、インターセプタを指定しているので、デフォルトのインターセプタ(defaultStack)は使われません。
アクションの作成
Struts2 のアクションクラスは POJO で作成することができます。デフォルトでは、execute メソッドを持つクラスを作成すればアクションとして実行することができます。
public class MyAction {
public String execute() throws Exception {
return "success";
}
}
アクションの呼び出しは、通常、「struts.xml に記述した <action> の名前 + ".action"」ですが、「struts.xml に記述した ≶action> の名前 + "!メソッド名.action"」とすることで、execute 以外のメソッドを呼び出すことができます。
また、struts.xml の <action> の設定で、method プロパティを設定することで、execute 以外のメソッドを呼び出すこともできます。
<action
name="itemRegister"
method="hoge"
class="com.hamasyou.struts2.webapp.usecase2.admin.ItemRegisterAction">
<result name="success">/WEB-INF/jsp/top/top.jsp</result>
</action>
- Zero コンフィグレーション
- クラス名を「Action」で終わる名前にしておくことで、Zero コンフィグレーションを利用して、無設定で Action クラスとして実行することができます。
Result について
以下、後で書く