単純なプログラムを書換えよう(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が発生する.
- filter
- listener
- 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 -->