#freeze
[[Spring-MVC/ステップ・バイ・ステップ]]

2008/04/14からのアクセス回数 &counter;

#contents

mvc-conventionのサンプルプログラムは、とてもシンプルで素晴らしいのですが、
規約をどのように使用したかを解説していないので、ソースを見ただけでは理解
しにくいので、ここで説明します。

** 設定ファイルの説明 [#bcc9406e]
サンプルの設定ファイルは、
- web.xml
- applicationContext.xml
- coverc-servlet.xml

最後のcoverc-servlet.xmlは、「サーブレット名-servlet.xml」がサーブレット関連Bean定義ファイルの
デフォルトファイル名です。DsipatcherServletの初期化で使用されます。

*** web.xmlの設定 [#k104e4bd]
web.xmlでSpring-MVC特有の定義は、
#pre{{
    <listener>
        <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
    </listener>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <servlet>
        <servlet-name>coverc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>coverc</servlet-name>
        <url-pattern>*.htm</url-pattern>
    </servlet-mapping>
}}

- ContextLoaderListenerは、WebアプリケーションのContextをロードするためのサーブレット・リスナー
です
- サーブレットの定義では、DispatcherServletを指定します
- サーブレット・マッピングは、xxxx.htmという要求をcovercというサーブレットにマッピングしています

サーブレット名称は、Servlet関連Bean定義ファイルを読み込むために使用されますので、適宜変更してください。

サーブレット関連のBean定義ファイルを細分して定義する場合には、web.xmlの
contextConfigLocationに以下のように定義ファイルを列記します。

#pre{{
    <servlet>
        <servlet-name>cart</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
	<init-param>
	     <param-name>contextConfigLocation</param-name>
	     <param-value>/WEB-INF/servlet-def.xml,/WEB-INF/servlet-stub.xml</param-value>
	</init-param>
    </servlet>
}}

*** applicationContext.xml [#b9d80f43]
applicationContext.xmlの定義は至って簡単です。

webアプリケーションで共通に利用されるrecipeManagerの定義をしています。

#pre{{
<bean id="recipeManager" class="org.springframework.showcase.coverc.service.StubRecipeManager"/>
}}

*** coverc-servlet.xml [#ub9f2ead]
coverc-servlet.xmlでは、
- ControllerClassNameHandlerMappingの定義
- SwitchBoardControllerの定義
- EditRecipeControllerの定義
- viewNameTranslatorの定義
- viewResolverの定義

をしています。

ControllerClassNameHandlerMappingは、コントローラのクラス名でHTTP要求を振り分けるHandlerMapppingです。

#pre{{
    <bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"/>
}}

DefaultRequestToViewNameTranslatorは、ModelAndViewオブジェクトにビューの名前が定義
されていない場合に、デフォルトのview名称を返してくれるクラスです。
詳しくは、[[Spring-MVC/ステップ・バイ・ステップ/Convention over configuration]]を参照してください。

#pre{{
    <bean id="viewNameTranslator" class="org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator"/>
}}

InternalResourceViewResolverは、論理ビュー名称から、実際のビューファイル名にマッピングしています。

ここでは、xxxという論理ビュー名に対するビューファイルとして、/WEB-INF/jsp/xxx.jspを返すように
- prefixに/WEB-INF/jspを指定
- suffixに.jspを指定

しています。

#pre{{
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
}}

Spring2.0からBean定義ファイルで継承が使えるようになりました。

baseRecipeControllerは、SwitchBoardController、EditRecipeControllerの親Bean定義で
この定義で、共通に使用する属性recipeManagerの定義をしています。
親Bean定義では、クラスを指定する必要がなく、abstract="true"とすることで、
必要な属性のみを定義することができます。

#pre{{
    <bean id="baseRecipeController" abstract="true">
        <property name="recipeManager" ref="recipeManager"/>
    </bean>
}}

SwitchBoardControllerの定義は、
#pre{{
    <bean class="org.springframework.showcase.coverc.web.SwitchBoardController"
          parent="baseRecipeController"/>
}}

EditRecipeControllerは、SimpleFormControllerのサブクラスなので、ちょっと多くの設定
が必要です。

- commandNameは、コマンドオブジェクトの名称を指定します
- commandClassは、コマンドオブジェクトのクラス名を指定します
- formViewは、フォームビューの論理名を指定します
- successViewは、成功時のビューを指定します、ここではswitchboard/listRecipes.htmにリダイレクトしています

#pre{{
    <bean class="org.springframework.showcase.coverc.web.EditRecipeController"
          parent="baseRecipeController">
        <property name="commandName" value="recipe"/>
        <property name="commandClass" value="org.springframework.showcase.coverc.domain.Recipe"/>
        <property name="formView" value="editRecipe"/>
        <property name="successView" value="redirect:switchboard/listRecipes.htm"/>
    </bean>
}}

** javaソースファイルの説明 [#rce5b505]
*** モデルクラス [#h1f7c550]
ドメインモデルクラスは、Recipe.java1個のみです。
属性にid, nameを持ち、それぞれのgetter/setterを定義し、cloneを追加した
きわめて簡素なものです。

#pre{{
public class Recipe implements Cloneable {
    private Long id;
    private String name;

    // getter/setterは省略

    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
}}

*** サービスクラス [#g38f3e78]
サービスクラスは、RecipeManagerインタフェースとその実装例としてStubRecipeMangerが
あります。

RecipeManagerインタフェースは、

- findAll: すべてのRecipeを取り出すメソッド
- findById: 指定されたIdのRecipeを取り出すメソッド
- save: 指定されたRecipeを保存するメソッド

を定義しています。

#pre{{
public interface RecipeManager {
    Collection findAll();
    Recipe findById(Long id);
    void save(Recipe user);
}
}}

StubRecipeManagerでは、TreeMapを使ってRecipeをメモリ上で管理しています。
loadRecipesメソッドでTreeMapに3個のRecipeをセットしています。

- findAllでは、TreeMapから取りだしたRecipeをArrayListにセットして返しています。

従ってfindAllで戻されたオブジェクトの名前は、recipeListになります。
詳しくは、[[Spring-MVC/ステップ・バイ・ステップ/Convention over configuration]]を参照してください。

#pre{{
public class StubRecipeManager implements RecipeManager {
    private Map recipes = new TreeMap();

    public StubRecipeManager() {
        loadRecipes();
    }

    public void save(Recipe recipe) {
        // passed in should be a clone - simply replace
        putRecipe(recipe);
    }

    public Recipe findById(Long id) {
        Recipe recipe = (Recipe) this.recipes.get(id);
        if (recipe != null) {
            return cloneRecipe(recipe);
        }
        return null;
    }

    public Collection findAll() {
        List recipeList = new ArrayList();
        Iterator itr = this.recipes.values().iterator();
        while (itr.hasNext()) {
            Recipe recipe = (Recipe) itr.next();
            recipeList.add(cloneRecipe(recipe));
        }
        return recipeList;
    }
}
}}

*** コントローラクラス [#wbd60333]
SwitchBoardControllerは、recipeManagerを属性に持ち、
- listRecipesメソッド
を定義しています。

 ModelAndView(). addObject(findAllの戻り値);
としている部分が、規約の使い方を示すための例です。
findAllの戻り値はRecipeを要素に持つArrayListですので、そのオブジェクトは、recipeListとしてモデルに追加されます。
詳しくは、[[Spring-MVC/ステップ・バイ・ステップ/Convention over configuration]]を参照してください。

#pre{{
public class SwitchBoardController extends MultiActionController {
    private RecipeManager recipeManager;

    // setRecipeManagerは省略

    public ModelAndView listRecipes(HttpServletRequest request, HttpServletResponse response) throws Exception {
        return new ModelAndView().addObject(this.recipeManager.findAll());
    }
}
}}

EditRecipeControllerも、recipeManagerを属性に持ち、
- formBackingObject : フォームにオブジェクトをセットするためのメソッド
- doSubmitAction : submit要求によって呼び出されるメソッド

#pre{{
public class EditRecipeController extends SimpleFormController {
    private RecipeManager recipeManager;

    // setRecipeManagerは省略

    protected Object formBackingObject(HttpServletRequest request) throws Exception {
        long id = ServletRequestUtils.getRequiredLongParameter(request, "id");
        Recipe recipe = this.recipeManager.findById(new Long(id));
        return recipe;
    }

    protected void doSubmitAction(Object object) throws Exception {
        Recipe recipe = (Recipe) object;
        this.recipeManager.save(recipe);
    }
}
}}

本当にこれだけで、よいのかと思うくらい少ない量のソースで、mvc-conventionの例題が
作られています。

** コメント [#qec290b0]
この記事は、

#vote(おもしろかった[12],そうでもない[0],わかりずらい[2])
#vote(おもしろかった[13],そうでもない[0],わかりずらい[2])

皆様のご意見、ご希望をお待ちしております。

#comment_kcaptcha

トップ   編集 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
SmartDoc