Spring MVC Interceptor HandlerInterceptorAdapter、HandlerInterceptorの例

Spring Interceptorは、クライアントのリクエストをインターセプトし、それらを処理するために使用されます。時には、HTTPリクエストをインターセプトして、コントローラーハンドラーメソッドに渡す前にいくつかの処理を行いたい場合があります。それがSpring MVC Interceptorの役割です。

Spring Interceptor

Struts2のインターセプターと同様に、Springのインターセプターを作成するには、org.springframework.web.servlet.HandlerInterceptorインターフェースを実装するか、HandlerInterceptorインターフェースの基本実装を提供するorg.springframework.web.servlet.handler.HandlerInterceptorAdapter抽象クラスをオーバーライドします。

Spring Interceptor – HandlerInterceptor

Spring HandlerInterceptorは、HTTPリクエストをインターセプトする場所に基づいて3つのメソッドを宣言しています。

  1. boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler):このメソッドは、リクエストがハンドラメソッドに渡される前にインターセプトするために使用されます。このメソッドは、別のSpringインターセプターを介してリクエストを処理させるために ‘true’ を返す必要があります。または、さらにSpringインターセプターがない場合は、ハンドラメソッドに送信します。このメソッドが ‘false’ を返すと、SpringフレームワークはリクエストがSpringインターセプター自体によって処理され、さらなる処理が不要と見なします。この場合、レスポンスオブジェクトを使用してクライアントリクエストに応答を送信する必要があります。オブジェクトhandlerは、リクエストを処理するために選択されたハンドラオブジェクトです。このメソッドは例外をスローすることもでき、その場合はSpring MVC例外処理がエラーページをレスポンスとして送信するのに役立ちます。
  2. void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView):このHandlerInterceptorインターセプターメソッドは、HandlerAdapterがハンドラを呼び出したが、DispatcherServletがまだビューをレンダリングしていない場合に呼び出されます。このメソッドは、ビューページで使用されるModelAndViewオブジェクトに追加の属性を追加するために使用できます。このSpringインターセプターメソッドを使用して、ハンドラメソッドがクライアントリクエストを処理するのにかかる時間を決定することができます。
  3. void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex): このメソッドは、HandlerInterceptor のコールバックメソッドであり、ハンドラが実行され、ビューがレンダリングされた後に呼び出されます。

複数の Spring インターセプターが構成されている場合、preHandle() メソッドは構成の順序で実行され、postHandle() メソッドと afterCompletion() メソッドは逆の順序で呼び出されます。以下の画像のような簡単な Spring MVC アプリケーションを作成しましょう。Spring インターセプターを構成して、コントローラーハンドラーメソッドのタイミングをログに記録します。最終的な Spring インターセプターの例のプロジェクトは、以下の画像のようになります。我々が興味を持っているコンポーネントについて調べてみましょう。

Spring インターセプター – コントローラークラス

package com.journaldev.spring;

import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

/**
 * Handles requests for the application home page.
 */
@Controller
public class HomeController {
	
	private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
	
	@RequestMapping(value = "/home", method = RequestMethod.GET)
	public String home(Locale locale, Model model) {
		logger.info("Welcome home! The client locale is {}.", locale);
		//インターセプターの実行を確認するために、いくつかの時間遅れを追加
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		Date date = new Date();
		DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
		
		String formattedDate = dateFormat.format(date);
		
		model.addAttribute("serverTime", formattedDate );
		logger.info("Before returning view page");
		return "home";
	}
	
}

I am just adding some processing time in the execution of the handler method to check our spring interceptor methods in action.

Spring MVC インターセプター – HandlerInterceptorAdapter の実装

単純に、抽象クラスHandlerInterceptorAdapterを拡張しています。HandlerInterceptorAdapterはHandlerInterceptorインターフェースのための抽象アダプタークラスであり、単純なpre-only/post-onlyインターセプターの実装を容易にします。

package com.journaldev.spring;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

public class RequestProcessingTimeInterceptor extends HandlerInterceptorAdapter {

	private static final Logger logger = LoggerFactory
			.getLogger(RequestProcessingTimeInterceptor.class);

	@Override
	public boolean preHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler) throws Exception {
		long startTime = System.currentTimeMillis();
		logger.info("Request URL::" + request.getRequestURL().toString()
				+ ":: Start Time=" + System.currentTimeMillis());
		request.setAttribute("startTime", startTime);
		//if returned false, we need to make sure 'response' is sent
		return true;
	}

	@Override
	public void postHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		System.out.println("Request URL::" + request.getRequestURL().toString()
				+ " Sent to Handler :: Current Time=" + System.currentTimeMillis());
		//we can add attributes in the modelAndView and use that in the view page
	}

	@Override
	public void afterCompletion(HttpServletRequest request,
			HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		long startTime = (Long) request.getAttribute("startTime");
		logger.info("Request URL::" + request.getRequestURL().toString()
				+ ":: End Time=" + System.currentTimeMillis());
		logger.info("Request URL::" + request.getRequestURL().toString()
				+ ":: Time Taken=" + (System.currentTimeMillis() - startTime));
	}

}

ロジックは非常に単純で、ハンドラーメソッドの実行タイミングとビューページのレンダリングを含むリクエスト処理にかかる合計時間を記録しています。

Spring MVC インターセプターの設定

Springインターセプターをリクエストにワイヤリングする必要があります。すべてのインターセプターをワイヤリングするには、mvc:interceptors要素を使用できます。リクエストに対してSpringインターセプターを含める前にURIパターンを提供するには、mapping要素を使用できます。最終的なSpringビーンの設定ファイル(spring.xml)は以下のようになります。

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="https://www.springframework.org/schema/mvc"
	xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns:beans="https://www.springframework.org/schema/beans"
	xmlns:context="https://www.springframework.org/schema/context"
	xsi:schemaLocation="https://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
		https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
		https://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

	<!-- DispatcherServlet Context: defines this servlet's request-processing 
		infrastructure -->

	<!-- Enables the Spring MVC @Controller programming model -->
	<annotation-driven />

	<!-- Handles HTTP GET requests for /resources/** by efficiently serving 
		up static resources in the ${webappRoot}/resources directory -->
	<resources mapping="/resources/**" location="/resources/" />

	<!-- Resolves views selected for rendering by @Controllers to .jsp resources 
		in the /WEB-INF/views directory -->
	<beans:bean
		class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<beans:property name="prefix" value="/WEB-INF/views/" />
		<beans:property name="suffix" value=".jsp" />
	</beans:bean>

	<!-- Configuring interceptors based on URI -->
	<interceptors>
		<interceptor>
			<mapping path="/home" />
			<beans:bean class="com.journaldev.spring.RequestProcessingTimeInterceptor"></beans:bean>
		</interceptor>
	</interceptors>

	<context:component-scan base-package="com.journaldev.spring" />

</beans:beans>

I will not explain all other components of the web application, because we are not interested in them and they don’t have any specific spring interceptor related configuration.

Spring MVC インターセプターアプリケーションのテスト

アプリケーションをサーブレットコンテナにデプロイし、ホームコントローラーを呼び出すと、以下のようなロガー出力が表示されます。

INFO : com.journaldev.spring.RequestProcessingTimeInterceptor - Request URL::https://localhost:9090/SpringInterceptors/home:: Start Time=1396906442086
INFO : com.journaldev.spring.HomeController - Welcome home! The client locale is en_US.
INFO : com.journaldev.spring.HomeController - Before returning view page
Request URL::https://localhost:9090/SpringInterceptors/home Sent to Handler :: Current Time=1396906443098
INFO : com.journaldev.spring.RequestProcessingTimeInterceptor - Request URL::https://localhost:9090/SpringInterceptors/home:: End Time=1396906443171
INFO : com.journaldev.spring.RequestProcessingTimeInterceptor - Request URL::https://localhost:9090/SpringInterceptors/home:: Time Taken=1085

以下のリンクからSpring Interceptorの例題プロジェクトをダウンロードし、複数のインターセプターを持ち、異なる構成順でチェックしてください。

Springインターセプタープロジェクトのダウンロード

Source:
https://www.digitalocean.com/community/tutorials/spring-mvc-interceptor-example-handlerinterceptor-handlerinterceptoradapter