単純なプログラムを書換えよう(WebWork2編)−Inversion of Control

はじめに

「Inversion of Control(以下IoCと略)」とは,インスタンスの生成を,プログラムではなくフレームワークで行う仕組みのことである.今回の場合,コントローラで行っていたモデルの生成は不要となる.フレームワーク(WebWork2)が適宜インスタンスを生成する.

ディレクトリ構成

プログラムを動かすために,以下のファイルを用意する.

counter05-1.jsp/counter05-2.jsp
ブラウザに表示するためのjsp.counter05-1.jspは最初に表示する画面,counter05-2.jspはプログラム起動後に遷移する画面.
Controller.class/Model.class/ModelAware.class
javaのクラス
xwork.xml
WebWork2の画面遷移等を指定するファイル
components.xml
WebWork2で制御するクラスとスコープを定義するファイル
web.xml
filterとlistenerを追加

ディレクトリ構成は以下のようになる.各ファイルの内容はそれぞれ後述する.

${TOMCAT_HOME}/webapps/counter/counter05-1.jsp
                              /counter05-2.jsp
                              /WEB-INF/classes/counter05/Controller.class
                                                        /Model.class
                                                        /ModelAware.class
                                              /components.xml
                                              /xwork.xml
                                      /lib/commons-logging.jar
                                          /ognl-2.6.3-modified.jar
                                          /oscore-2.2.1.jar
                                          /velocity-dep-1.3.1.jar
                                          /webwork-2.0.jar
                                          /xwork-1.0.jar
                                          /web.xml
        

URL

アクセスするためのURLは以下の通りとなる.

http://localhost:[ポート番号]/counter/counter05-1.jsp
        

ポート番号が8080の場合は,

http://localhost:8080/counter/counter05-1.jsp
        

となる.

counter05-1.jsp/counter05-2.jsp

counter04-1.jsp/counter04-2.jspを一部修正する.

<%-- counter05-1.jsp --%>
<html>
  <head>
    <title>counter05-1</title>
  </head>
  <body>
    value:0<br/>
    <form action="Counter05.action">
<%--   <input type="hidden" name="value" value="<ww:property value="value"/>"/>
      後述するcomponents.xmlの記述でWebWork2がインスタンスをセッション単位で管理してくれる.hiddenは不要になる.
  --%>
      <input type="submit" name="action" value="inc"/>
      <input type="submit" name="action" value="dec"/>
    </form>
  </body>
</html>
<%-- end --%>

<%-- counter05-2.jsp --%>
<%@ taglib prefix="ww" uri="webwork" %>
<%-- WebWorkのカスタムタグを使うのでtaglibを宣言 --%>
<html>
  <head>
    <title>counter05-2</title>
  </head>
  <body>
    value:<ww:property value="value"/><br/>
    <form action="Counter05.action">
<%--   <input type="hidden" name="value" value="<ww:property value="value"/>"/>
      後述するcomponents.xmlの記述でWebWork2がインスタンスをセッション単位で管理してくれる.hiddenは不要になる.
  --%>
      <input type="submit" name="action" value="inc"/>
      <input type="submit" name="action" value="dec"/>
    </form>
  </body>
</html>
<%-- end --%>
    

Controller.java

ModelAwareをimplementsし,WebWork2が生成したインスタンスを格納するためのsetter(ModelAware.javaに定義)を実装する.ModelのインスタンスはWebWork2が生成するので,プログラム内でのインスタンスの生成は不要になる.

//Controller.java
package counter05;

import com.opensymphony.xwork.*;

public class Controller extends ActionSupport implements ModelAware{ //ModelAwareインタフェースをimplement
  private Model model; //Modelのインスタンスは生成しない.
  private String action;

  public void setModel(Model model){
    //WebWork2が生成したインスタンスを格納するためのsetter.後述するcomponents.xmlの記述に従いWebWork2が生成したModelのインスタンスを保管する
    this.model=model;
  }
  public void setValue(String value){
    this.model.setValue(value);
  }
  public String getValue(){
    return this.model.getValue();
  }
  public void setAction(String action){
    this.action=action;
  }
  public String getAction(){
    return action;
  }
  public String execute(){
    if(getAction().equals("inc")){
      model.inc(); 
    }
    else{
      model.dec(); 
    }
    return SUCCESS;
  }
}
//end
    

Model.java

変更なし.

//Model.java
package counter05;

public class Model{
  private int value;
  
  public Model(){
    this(0);
  }
  public Model(int value){
    this.setValue(value);
  }

  public void setValue(String value){
    this.setValue(Integer.parseInt(value));
  }
  public void setValue(int value){
    this.value=value;
  }
  public String getValue(){
    return Integer.toString(this.getIntValue());
  }
  public int getIntValue(){
    return value;
  }

  public int inc(){
    return inc(1);
  }
  public int inc(int value){
    setValue(getIntValue()+value);
    return getIntValue();
  }
  public int dec(){
    return dec(1);
  }
  public int dec(int value){
    setValue(getIntValue()-value);
    return getIntValue();
  }
}
//end
    

ModelAware.java

生成したモデルのインスタンスを保持するためのsetterメソッドを定義したインタフェース.

//ModelAware.java
package counter05;

public interface ModelAware{
  void setModel(Model model);
}
//end
    

xwork.xml

xwork.xmlに以下の修正を加える

  • interceptor“component”を追加
  • Counter05を追加
<!-- xwork.xml -->
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xwork
  PUBLIC
  "-//OpenSymphony Group//XWork 1.0//EN"
  "http://www.opensymphony.com/xwork/xwork-1.0.dtd">
<xwork>
  <include file="webwork-default.xml"/>

  <package name="default" extends="webwork-default">
    <interceptors>
      <interceptor-stack name="defaultModelDrivenStack">
        <interceptor-ref name="model-driven"/>
        <interceptor-ref name="defaultStack"/>
      </interceptor-stack>

      <!-- Counter05の中でcomponentとdefaultStackをまとめ,defaultComponentStackとして使う場合は必要 -->
      <interceptor-stack name="defaultComponentStack">
        <interceptor-ref name="component"/>
        <interceptor-ref name="defaultStack"/>
      </interceptor-stack>
    </interceptors>
    <default-interceptor-ref name="defaultStack"/>

    <!-- Counter05を追加 -->
    <action name="Counter05" class="counter05.Controller">
      <result name="success" type="dispatcher">
        <param name="location">counter05-2.jsp</param>
      </result>
      <interceptor-ref name="defaultStack"/>
      <interceptor-ref name="component"/> <!-- IoCを使う場合にintercepter“component”が必要 -->
<!--  <interceptor-ref name="defaultComponentStack"/>
       interceptor-stack“defaultComponentStack”を定義しているので,こうもかける -->
    </action>
  </package>
</xwork>
<!-- end -->
        

components.xml

WebWork2がインスタンスを生成するクラスの記述と,保持するsetterを定義したインタフェースの設定を行うファイル.

<!-- components.xml -->
<?xml version="1.0" encoding="utf-8"?>
<components>
  <component>
    <scope>session</scope> <!-- インスタンスをセッション単位で作成 -->
    <class>counter05.Model</class>
    <enabler>counter05.ModelAware</enabler>
  </component>
</components>
<!-- end -->
    

web.xml

IoCを使うために,filterとlistenerを追加する.これらの追加がないと,インスタンスの生成ができず,プログラム実行時にNullPointerExceptionが発生する.また,以下の順番で記述しないと,Tomcat起動時にParse Errorが発生する.

  1. filter
  2. listener
  3. servlet
<!-- web.xml -->
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
  PUBLIC
  "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
  "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
  <display-name>WebWork Sample</display-name>

  <!-- IoC使用時に必要 記述順は“filter・listener・servlet”の順 -->
  <filter>
    <filter-name>container</filter-name>
    <filter-class>com.opensymphony.webwork.lifecycle.RequestLifecycleFilter</filter-class>
  </filter>

  <filter-mapping>
    <filter-name>container</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

  <listener>
    <listener-class>com.opensymphony.webwork.lifecycle.ApplicationLifecycleListener</listener-class>
  </listener>

  <listener>
    <listener-class>com.opensymphony.webwork.lifecycle.SessionLifecycleListener</listener-class>
  </listener>
  <!-- 追加ここまで -->

  <servlet>
    <servlet-name>webwork</servlet-name>
    <servlet-class>com.opensymphony.webwork.dispatcher.ServletDispatcher</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>webwork</servlet-name>
    <url-pattern>*.action</url-pattern>
  </servlet-mapping>

  <taglib>
    <taglib-uri>webwork</taglib-uri>
    <taglib-location>/WEB-INF/lib/webwork-2.0.jar</taglib-location>
  </taglib>
</web-app>
<!-- end -->