2012/06/11

Spring FrameworkのHandlerInterceptorについての備忘録。

チームメイトのJavaなねーさんに教えて貰ったのですが、Spring Frameworkには、HandlerInterceptorなるものがあって、このクラスをラップすることによって、各Contorollerクラスの前後で共通の処理を定義してあげる事が出来るそうな。
マジ女子力高い。


実装はこんな感じで。



public class HogeInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        //  ここに処理を書く
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        //  ここに処理を書く
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        //  ここに処理を書く
    }
}




後はSpringの設定ファイルに



<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/*"/>
        <bean class="com.example.HogeInterceptor" />
    </mvc:interceptor>
</mvc:interceptors>

って追加してあげるだけ。
<mvc:mapping path>の値を指定してあげる事で、Contoroller毎に使用するインターセプターを切り替える事も出来るみたい。
ホント便利ね、Spring・・・。


で、今回ハマったのがafterCompletionメソッドのところ。
現在作っているシステムでは、レスポンスヘッダをちょこちょこと弄る必要があった為に、このafterCompletionメソッドで共通的に出力するレスポンスヘッダを設定していました。

ところが、ある特定の条件下のみ、この設定が反映されない現象が確認されて、ちょっと頭を悩ませていた感じです。
(httpに精通されている方であれば、もうこの地点で何が起こったか把握できるのでしょう・・・Zzz)

最初は設定を疑って、インターセプターがコールされていないんじゃないかと思ったのですが、どうやらコールそのものはちゃんとされているようでした。

で、その後いろいろ調べていたところ、最初自分はafterCompletionメソッドはContorollerの処理が終わった直後にコールされるのかと思っていたのですが、それは勘違いで、実際はViewのレンダリングが終わった後にコールされているみたいです。

ですので、例えばContorollerがhtmlやjsonなど、レスポンスボディに相当するデータを返却するような実装だった場合(っていうか殆どそうだと思いますが・・・。)、既にレスポンスヘッダは出力済みの状態となる為、afterCompletionメソッドでレスポンスヘッダの設定をしても、反映されなくなるというオチでした。

ですので、ヘッダの出力処理をpreHandleメソッドの方にて実行するよう修正する事で、問題なく動かすことが出来ました。
実際判明してみると非常にシンプルな問題なんですけどね・・・。

やはりこういったフレームワークはちゃんと理解して使わないと、いざこういったケースに陥った時にどうにもならなくなりますね・・・。
頑張って勉強しよう・・・Zzz