Struts2 は WebWorks2 をベースとした MVC フレームワークです。Struts1 と変わらずコマンドパターンのフレームワークになっています。

コマンドパターンの実装部分では、OPENSYMHONY の XWork が使われています。

Struts2の特徴

  • WebWorks の後継となる WebWorks2 がベースの MVC フレームワーク
  • コマンドパターンが使われている(XWork が使われている)
  • アノテーションと XML ファイルによる設定
  • ActionForm がない。代わりにアクションにフォームデータを格納する
  • アクションは POJO で作成できる
  • アクションがスレッドセーフ
  • 設定ファイルに OGNL 式が書ける
  • View に JSP、Freemaker、Velocity、XSLT が使える
  • SpringFramework との連携が考慮されている
  • Ajax をサポートしている
  • プラグインによりフレームワークの拡張が行える

情報が正確ではない可能性が大いにあるので(ドキュメント読まずにソース見ながら書いているので・・・)、鵜呑みにしないでください。

随時更新予定です。

執筆時の環境

目次

  1. アーキテクチャ
    1. アーキテクチャ概要図
    2. 主なクラス
    3. 概要クラス図とシーケンス図
  2. Struts2 の主な機能
    1. コンフィグレーションファイルの指定
    2. Zero コンフィグレーション
    3. カスタム設定読み込み
    4. Dependency Injection
    5. インターセプタ
    6. アクションの作成
    7. Result について

アーキテクチャ

アーキテクチャ概要図

Struts2のアーキテクチャ

主なクラス(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.DispatcherserviceAction を呼び出します。

DispatcherserviceAction の中で、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>

上記のように設定した場合、

Zeroコンフィグレーション

「/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 に記述した &lg;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 について

以下、後で書く