インターセプタはSpringMVCの機能の1つで、コントローラメソッドの前後で任意の処理を実行させることができる。
インターセプタを利用するにはHandlerInterceptorAdapterを継承して、必要に応じてpreHandle、postHandle、afterCompletionの3メソッドを実装する。あとはSpring設定ファイルにインターセプタを使用するための定義を記述をするだけでよい。
なおpreHandle、postHandle、afterCompletionの各メソッドは実行されるタイミングが異なる。
メソッド名 | 実行されるタイミング |
---|---|
preHandle | コントローラメソッドの実行前 |
postHandle | コントローラメソッドの実行後 |
afterCompletion | リクエスト処理が完了した後 |
実際にどのようなタイミングで各メソッドが実行されるかサンプルを使って説明する。
まずコントローラとJSPを作成する。それぞれでシステムアウトで文字列を出力。
package mvctest;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
@RequestMapping("/sample")
public class SampleController {
@RequestMapping(value="/submit1", method={RequestMethod.POST, RequestMethod.GET })
public String submit1(Model model,
@RequestParam(required=false) String lastname,
@RequestParam(required=false) String firstname) {
System.out.println("submit1.do");
model.addAttribute("lastname", lastname);
model.addAttribute("firstname", firstname);
// sample1.jspを呼び出す
return "sample1";
}
}
sample1.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<% System.out.println("sample1.jsp"); %>
<!DOCTYPE>
<html>
<head>
<meta charset="UTF-8">
<script src="js/jquery-1.11.1.min.js"></script>
</head>
<body>
<form action="/test/sample/submit1.do" method="POST">
<input type="submit" value="submit1">
<input type="text" id="lastname" name="lastname" value="">
<input type="text" id="firstname" name="firstname" value="">
${lastname} ${firstname}
</form>
</body>
</html>
次にインターセプタを作成する。
package mvctest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
// HandlerInterceptorAdapterを継承
public class SampleInterceptor extends HandlerInterceptorAdapter {
// ①コントローラメソッドの実行前に呼ばれる
@Override
public boolean preHandle(
HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
System.out.println("preHandle");
return true;
}
// ②コントローラメソッドの実行後に呼ばれる
@Override
public void postHandle(
HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
}
// ③リクエスト処理の完了後に呼ばれる
@Override
public void afterCompletion(
HttpServletRequest request,
HttpServletResponse response,
Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion");
}
}
インターセプタにはHandlerInterceptorAdapterを継承させる。
preHandle、postHandle、afterCompletionの3メソッドを実装し、それぞれのメソッド内で文字列を出力してみる。
各メソッドで共通の引数はHttpServletRequest、HttpServletResponse、ハンドラ(handler)と呼ばれるObjectである。このハンドラとはコントロールメソッドのこと。実態はHandlerMethodクラスで、実行されたコントローラメソッドの情報を格納している(本記事最後を参照)。postHandleの引数ModelAndViewはビューや画面情報をまとめて保持するクラスである。コントローラメソッドの戻り値はビュー名やModelAndViewが返却できるが、ModelAndViewだった場合にここで参照できる。
afterCompletionの引数Exceptionは、例外が処理されなかった場合に、その例外クラスが引き渡される。
インターセプタをSpring設定ファイルに登録する。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd">
<context:component-scan base-package="mvctest" />
<mvc:annotation-driven />
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
<!-- インターセプタの定義 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/*/submit*.do"/>
<bean class="mvctest.SampleInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
</beans>
設定ファイルにはインターセプタのクラス名と動作対象とするパスを記述する。
パスにはコンテキストパスは含めない。例えばURLが「http://localhost/test/sample/submit1.do」でコンテキストパスが「test」の場合、以下の記述は誤りである("/test"が不要)。誤)<mvc:mapping path="/test/*/submit*.do"/>
インターセプタは<mvc:interceptors>タグ内に複数登録できる。
全ての準備が揃ったところで実行してみる。URLへアクセスしてコントローラメソッドを実行させると、標準出力に以下が表示される。
preHandle
submit1.do
postHandle
sample1.jsp
afterCompletion
以下の順で実行されていることがわかる。
①preHandle
②コントローラメソッド(submit1)
③posthandle
④JSP
⑤afterCompletion
コントローラメソッドを2つ作って、片方からもう片方へフォワードで呼び出してみる。
package mvctest;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
@RequestMapping("/sample")
public class SampleController {
@RequestMapping(value="/submit1", method={RequestMethod.POST, RequestMethod.GET })
public String submit1(Model model,
@RequestParam(required=false) String lastname,
@RequestParam(required=false) String firstname) {
System.out.println("submit1.do");
model.addAttribute("lastname", lastname);
model.addAttribute("firstname", firstname);
// submit2へフォワードする
return "forward:submit2.do";
}
@RequestMapping(value = "/submit2", method = { RequestMethod.POST, RequestMethod.GET })
public String submit2(Model model) {
System.out.println("submit2.do");
return "sample1";
}
}
この状態で実行すると、標準出力に以下が表示される。
preHandle
submit1.do
postHandle
preHandle
submit2.do
postHandle
sample1.jsp
afterCompletion
afterCompletion
以下の順で実行されていることがわかる。
①preHandle
②コントローラメソッド(submit1)
③posthandle
④preHandle
⑤コントローラメソッド(submit2)
⑥posthandle
⑦JSP
⑧afterCompletion
⑨afterCompletion
preHandleとpostHandleはそれぞれのコントローラメソッドの前後で呼ばれる。なおafterCompletionも2回呼ばれるらしい。
最後にコントローラメソッドの引数のハンドラ(Object型)を出力してみる。
System.out.println(handler.getClass());
System.out.println(handler);
出力結果
class org.springframework.web.method.HandlerMethod
public java.lang.String mvctest.SampleController.submit1(org.springframework.ui.Model,java.lang.String,java.lang.String)
コントローラメソッドの情報が出力される。引数の型はObjectだが実態はHandlerMethodクラスとなっている。