リファレンス

Chapter 12. Web MVC framework

12.1. Introduction to the web MVC framework

Spring MVCはとても素晴らしい、と言うことが書いてある。
Spring MVCの中心となるのはDispatcherServlet。コントローラにはControllerというインタフェースが用意されている。

public interface Controller {

	/**
	 * Process the request and return a ModelAndView object which the DispatcherServlet
	 * will render. A null return is not an error: It indicates that this object
	 * completed request processing itself, thus there is no ModelAndView to render.
	 * @param request current HTTP request
	 * @param response current HTTP response
	 * @return a ModelAndView to render, or null if handled directly
	 * @throws Exception in case of errors
	 */
	ModelAndView handleRequest(
                 HttpServletRequest request, 
                 HttpServletResponse response)
             throws Exception;

}

ただし、Controllerインタフェースを実装する以外に、Spring フレームワークによって既に提供されているAbstractController, AbstractCommandController and SimpleFormControllerなどを使用することも可能。
handleRequestの戻り値はModelAndViewクラス型となっているが、これは基本的にはViewの名前とモデルとなるMapで構成されている。レンダリングを行うViewクラスを自分で実装して設定することも可能。

例.必ずエラーページに遷移するView。

public class ErrorView implements View {
    public void render(Map model, HttpServletRequest request,
            HttpServletResponse response) throws Exception {
        RequestDispatcher rd = request.getRequestDispatcher("/WEB-INF/jsp/error.jsp");
        rd.forward(request, response);
    }
}

例.ErrorViewを返すコントローラ

public class MyController implements Controller {
    public ModelAndView handleRequest(HttpServletRequest arg0,
            HttpServletResponse arg1) throws Exception {
        if (error) {
            ErrorView ev = new ErrorView();
            return new ModelAndView(ev);
        }
    }
}

せっかくなのでbean定義に書いてみる。

<beans>
    …
    <bean id="errorView" class="ErrorView" />
    <bean id="errorModelAndView" class="org.springframework.web.servlet.ModelAndView">
        <property name="view">
            <ref bean="errorView" />
        </property>
    </bean>
    …
</beans>

bean定義からModelAndViewを読み込むコントローラ。

public class MyController implements Controller {
    public ModelAndView handleRequest(HttpServletRequest arg0,
            HttpServletResponse arg1) throws Exception {
        ApplicationContext context
            = RequestContextUtils.getWebApplicationContext(arg0);
        return (ModelAndView) context.getBean("errorModelAndView");
    }

}

こんな感じに使うのが正しいかどうかは不明!

12.1.1. Pluggability of other MVC implementations

他のWEB MVCフレームワークを使えますよ、ということが書かれている。ここで具体名が出ているのはStrutsとWebWork。

12.1.2. Features of Spring MVC

  • 各役割の明確な分割…コントローラ、バリデータ、コマンドオブジェクト、フォームオブジェクト、モデル、DispatcherServlet、ハンドラマッピング、Viewリゾルバ...
  • フレームワーク、アプリケーションクラスのJavaBeanとしてのコンフィグレーション、及びその取得の容易性
  • コントローラを拡張することでどのような画面シナリオにも対応可能。
  • 既存のクラスをそのままSpring MVCで使用することが出来る。
  • データバインディング、バリデーションがカスタマイズ可能
  • ハンドラマッピング、Viewリゾルバがカスタマイズ可能。
  • 柔軟なモデル転送。
  • ロケール対応、テーマがカスタマイズ可能。
  • タグライブラリ。

12.2. The DispatcherServlet

Spring MVCも他のMVCフレームワーク同様、サーブレットがコントローラにリクエスト処理をディスパッチするリクエストドリブンなフレームワークである。SpringのDispathcerServletはWebApplicationContextと連携し、Springの機能を利用できるようにしている。
当然だけど、DispathcerServletはweb.xmlに設定する。

web.xml

<web-app>
    ...
    <servlet>
        <servlet-name>example</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>example</servlet-name>
        <url-pattern>*.form</url-pattern>
    </servlet-mapping>
</web-app>

DispatcherServletで使用されるデフォルトのBeanFactoryはXmlBeanFactory。DispathcerServletは初期化時に[サーブレット名]-servlet.xmlというファイルをWEB-INFディレクトリ直下から取得する。
WebApplicationContextはRequestContextUtilsを使用することでWeb層のどこからでも取得が可能。

WebApplicationContextの取得

RequestContextUtils.getWebApplicationContext(ServletRequest request)

DispatcherServletにはリクエスト処理やViewのレンダリングで使用する特別なBeanがいくつかある。これらのBeanは他のBean同様、bean定義ファイルに登録することで利用可能となる。

handler mapping(s) ある条件を満たしたときに実行されるプリ/ポストプロセッサ、コントローラ。
controller(s) コントローラ。
view resolver View名をViewに変換する。
locale resolver クライアントのロケールを取得する。
theme resolver アプリケーションで扱うテーマを決定する。
multipart resolver ファイルアップロードを処理する。
handlerexception resolver 例外処理を実行する。
DispatcherServletの処理フロー。

  1. WebApplicationContextを取得し、リクエスト属性に保存する。保存時のデフォルトのキーは「DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE」
  2. ロケールゾルバがリクエスト属性に保存される。
  3. テーマリゾルバがリクエスト属性に保存される。
  4. マルチパートリゾルバが登録されており、リクエストがファイルアップロードである場合、リクエストをMultipartHttpServletRequestでラップする。
  5. 適切なリクエストハンドラを検索し、プリプロセッサ・ポストプロセッサ・コントローラのチェーンを実行する。
  6. 実行結果としてモデルが返却されたら、WebApplicationContextに登録されたViewリゾルバを使用して、Viewのレンダリングを実行する。モデルが返却されない場合は、既にViewのレンダリングが行われているものとし、レンダリングは行わない。

リクエスト処理中に発生した例外は、WebApplicationContextに登録した、handlerexceptionリゾルバによって処理される。
また、以下のパラメータをweb.xmlに設定することで、DispatcherServletをカスタマイズすることが可能。

contextClass WebApplicationContext実装クラス名。指定しない場合はXmlWebApplicationContext。
contextConfigLocation コンフィグレーションファイルの場所。カンマ区切りで複数指定可能。
namespace WebApplicationContextの名前空間。指定しない場合はサーブレット名-servlet

12.3. Controllers

コントローラはユーザ入力を受け取り、それをビューによって使用されるにモデル反映する。Springのコントローラの基本はContrllerインタフェースである。

Controllerインタフェース。

public interface Controller {

    /**
     * Process the request and return a ModelAndView object which the DispatcherServlet
     * will render.
     */
    ModelAndView handleRequest(
        HttpServletRequest request,
        HttpServletResponse response)
    throws Exception;
}

Springでは様々なController実装を用意しているとのことで、各コントローラについて。

12.3.1. AbstractController and WebContentGenerator

Springが提供しているControllerクラスは全てAbstractControllerクラスを継承している。
AbstractControllerがサポートする属性。

supportedMethods サポートするHTTPのメソッド(GETとかPOSTとか)。カンマ区切りで指定する。サポートしないメソッドのアクセスがあった場合は例外が発生する。
requiresSession セッションが必要かどうか。セッションが生成されていないリクエストが来たら例外が発生する。
synchronizeSession セッション単位で同期処理を行うかどうか。
cacheSeconds レスポンスのキャッシュ設定。-1に設定するとキャッシュしない。
useExpiresHeader HTTP1.0のExpires属性。デフォルトはtrue。
useCacheHeader HTTP1.1のCache-Control属性。デフォルトはtrue。

12.3.2. Other simple controllers

他にもシンプルなコントローラがあるよ、ってこと。

12.3.3. The MultiActionController

MultiActionControllerは一つのコントローラーに複数の操作を定義したい場合に使用する。MultiActionControllerはリクエストとメソッド名をマッピングし、適切なメソッドを呼び出す。デフォルトではリクエストされたリソース名の拡張子を除いた部分のメソッドを起動する。http://〜/hello.htmというパスの場合は、helloという名前のメソッドを実行する。

delegate MultiActionControllerの使用方法には二つのシナリオがある。一つ目はMultiActionControllerのサブクラスを作成し、サブクラスにMethodNameResolverによって決定されるメソッドを定義する場合(この場合はdelegate属性は定義不要)。もう一つはMultiActionController(及びそのサブクラス)には実行メソッドを定義せず、delegate属性に指定されたクラスのメソッドを実行する方法である。
methodNameResolver リクエスト情報から実行するメソッドを決定するクラス。指定しない場合はInternalPathMethodNameResolver。
実行されるメソッドは

ModelAndView actionName(HttpServletRequest, HttpServletResponse);

※actionNameは適切なメソッド名に変換すること。

の型でなければならない。(ソースを見ると第三引数にHttpSessionがあってもよさそう)

delegate属性はわかりにくいけど、要は、

<beans>
    <bean id="springappController" class="org.springframework.web.servlet.mvc.multiaction.MultiActionController">
        <property name="delegate">
            <ref bean="delegate" />
        </property>
    </bean>
    <bean id="delegate" class="DelegateObject"/>
</beans>

で、DelegateObjectにModelAndView actionName(HttpServletRequest, HttpServletResponse);を定義しろってことだと思う。

また、第三引数をException型にすることで例外処理を行うことが出来る。

ModelAndView anyMeaningfulName(HttpServletRequest, HttpServletResponse, ExceptionClass);

※anyMeaningfulNameは適切なメソッド名に変換すること。

MethodNameResolverはリクエスト情報から実行するメソッド名を決定する。Springでは以下の三つのMethodNameResolverを用意している。

  1. ParameterMethodNameResolver…リクエストパラメータをメソッド名とする。設定時に「paramName」で指定した値がメソッドを取得する対象のリクエストパラメータ名になる。
  2. InternalPathMethodNameResolver…ユーザがリクエストしたリソース名の拡張子を除いた部分が起動対象のメソッド名となる。
  3. PropertiesMethodNameResolver…ユーザがリクエストしたパス名とメソッド名をマッピングしたプロパティファイルを作成しておき、そこから実行対象のメソッド名を取得する。

例.PropertiesMethodNameResolver

<bean id="propsResolver" class="org....mvc.multiaction.PropertiesMethodNameResolver">
    <property name="mappings">
        <props>
            <prop key="/index/welcome.html">retrieveIndex</prop>
            <prop key="/**/notwelcome.html">retrieveIndex</prop>
            <prop key="/*/user?.html">retrieveIndex</prop>
        </props>
    </property>
</bean>

<bean id="paramMultiController" class="org....mvc.multiaction.MultiActionController">
    <property name="methodNameResolver"><ref bean="propsResolver"/></property>
    <property name="delegate"><ref bean="delegate"/></property>
</bean>
12.3.4. CommandControllers

CommandControllerはモデルオブジェクトとの連携、リクエストパラメータの動的なバインディングを提供する。

  • AbstractCommandController…CommandControllerの抽象基底クラス。リクエストパラメータのモデルへのバインドを行う。このクラスはフォーム機能は提供していないが、バリデーション機能は提供する。
  • AbstractFormController…フォームのサブミットをサポートするController。
  • SimpleFormController…AbstractFormControllerを実装したクラス。
  • AbstractWizardFormController…ウィザード形式の画面遷移を実行するコントローラ。