StrutsでSpringのDI機能を使う

このページはいわゆるStrutsとSpringの統合方法を紹介したページです。 StrutsからSpringのDI機能が使えるようになると、

  • ActionのプロパティをSpringから柔軟に注入できるようになるので、 Actionの共通化・再利用化が促進され、プログラムの品質が向上する。
  • Strutsに依存せずに処理部の単体テストが可能になる。
  • SpringのAOP(Aspect Oriented Programming)機能が使える。
等の利点があります。

この方法は後述のように幾つかありますが、 共通する基本はSpringで管理されているBeanを参照できるようにする、ということです。 この部分の設定は、Strutsと連係する場合だけでなく、 JSFや、その他のWebフレームワークとの連係の場合も同じ作業です。

  1. spring.jar、log4j-*.jar、commons-logging.jar をWEB-INF/libに配置します。 サーブレットコンテナ側にJakarta Commons DBCPのjarファイルがない場合には、 それらも配置する必要がありますが、Apache Tomcatでは不要です。
  2. log4j.xml、 commons-logging.propertiesを適切に記述して、 WEB-INF/classesに配置します。
  3. SpringのBean定義のロードは、サーブレットのリスナーで行います。(★1)
    そのためweb.xmlに、ContextLoaderListenerを記述します。

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/beans.xml</param-value>
    </context-param>
    

    初期化パラメータcontextConfigLocationのデフォルトは、 /WEB-INF/applicationContext.xmlです。つまりこの名前でBean定義XML文書を配置している場合は、 context-paramは不要です。 パラメータとして複数のXML文書を指定したい場合は、 「/WEB-INF/beans1.xml,/WEB-INF/beans2.xml」のように、カンマで区切って指定できます。 また、「applicationContext*.xml」のようにワイルドカードを指定することもできます。
    ★1 Strutsでは、リスナーの代わりにStrutsプラグインを使う、 つまりstruts-config.xmlのplug-in要素で設定することも可能です。 その方法については後述します。
  4. applicationContext.xmlなどの上記で設定したパスに、実際にBean定義XML文書を作って配置します。
  5. また、SpringのBean定義というものは、全てロード時にインスタンス化されるので、 全てのBeanクラスを開発してロードできる状態に配置する必要があります。
  6. Webアプリケーションからは、次のようにしてBeanを取り出せます。

    ApplicationContext context =
        WebApplicationContextUtils.getWebApplicationContext(sc);
    TestBean b = (TestBean) context.getBean("testBean");
    

    scはServletContextです。Servletはもちろん、 StrutsのActionや、JSFのManaged Beanなら取り出して使うことができますね。

さて、上記の方法だけでは、 結局Beanの名前はStruts Actionの中に記述しないといけないので、あまりDIの利点を生かせません。 そこで上記に加え、ここから先Strutsに特化した連係方法が3つ用意されています。 それは

  • ActionSupportクラスを使う
  • DelegatingRequestProcessorクラスを使う
  • DelegationActionProxyクラスを使う
です。 詳細に解説された Get a better handle on Struts actions, with Spring (IBM deverloperWorks) の受け売りなので、コードサンプルなどはそちらをご覧下さい。

最初の方法は単純で、標準のActionの代わりに org.springframework.web.struts.ActionSupportを拡張してActionクラスを作る方法です。 この基底クラスにはgetWebApplicationContext()メソッドが用意されており、 これでSpringのApplicationContextが参照できるので、 先述と同様getBean("Bean定義名")でSpring配下のBeanが参照できます。
利点 ・簡単で分かりやすい。
・Spring側にはActionの定義を書かなくて良い。(というか書けない)
欠点 ・AOP機能は使えない。
・既に自分でActionを拡張した共通クラスを作っている場合、自分で両者を統合しなければいけない。
・参照したいBeanの名前自身はActionの中に直接記述するか、どうにかして注入しなければいけないので、 あまりDIの利点を生かせない。
・StrutsとSpring両者に依存したコードになってしまい、別のフレームワークへの移行はさらに難しくなる。

2番目の方法は、struts-config.xmlにさらに次の記述を加えます。

<controller processorClass="org.springframework.web.struts.DelegatingRequestProcessor"/>

この方法では、Spring側にもActionの定義を書くことができ、 Springの中でActionクラスが持つプロパティに値を注入できます。 そしてstruts-config.xmlのAction定義側では、 type=でActionの完全修飾のクラス名を指定する必要がありません。 Actionのパス(例えば「/SearchItem」)と同じ名前のSpring Bean定義がActionクラスとして自動的に呼び出されます。 (先頭のスラッシュも必要です。またモジュールになっている場合はモジュールも含めたパスになります)
利点 ・Actionのプロパティに注入する方法がDI的で、その利点を生かせる。
・struts-config.xmlのActionの定義にtype=属性を書かなくて良い。
欠点 ・既に自分でRequestProcessorを拡張した共通クラスを作っている場合、自分で両者を統合しなければいけない。

3番目の方法は、struts-config.xmlの各Actionに type="org.springframework.web.struts.DelegatingActionProxy" と記述することで、このクラスにSpring配下のActionを探してもらう方法です。
利点 ・Actionのプロパティに注入する方法がDI的で、その利点を生かせる。
・既に自分でActionやRequestProcessorを拡張した共通クラスを作っている場合、 それらと最も簡単に共存できる。
欠点 ・struts-config.xmlのActionの定義に、毎回type=属性を書かなくてはいけない。 ⇒Strutsのワイルドカード・マッピング機能や、 1.3でサポートされたマッピングの継承機能を活用することでかなり回避できます。

リスナーの代わりにStrutsプラグインとしてBean定義をロードする
Strutsの場合は、Struts自身がプラグインという、 Servletリスナーと似たような機構を持っているので、 リスナーは使わないでプラグインを使うこともできます。 Struts構成ファイルstruts-config.xmlに次のように書きます。

<plug-in className="org.springframework.web.struts.ContextLoaderPlugIn">
    <set-property property="contextConfigLocation" 
            value="/WEB-INF/applicationContext.xml,/WEB-INF/query-beans.xml"/>
</plug-in>

ただし、結局ContextLoaderListenerにはない、PlugInならではのメリットも特に無いので、 Strutsだからといって無理にこちらを使う必要はないでしょう。

Java kowaza Top

(first uploaded 2006/05/01 last updated 2007/01/28, URANO398)

Gポイントポイ活 Amazon Yahoo 楽天

無料ホームページ 楽天モバイル[UNLIMITが今なら1円] 海外格安航空券 海外旅行保険が無料!