Spring MVC の Interceptor である HandlerInterceptorAdapter の使用例。

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

春のインターセプター

私たちにはStruts2のインターセプターがあるように、org.springframework.web.servlet.HandlerInterceptorインターフェースを実装するか、基本的なHandlerInterceptorインターフェースの実装を提供するorg.springframework.web.servlet.handler.HandlerInterceptorAdapterの抽象クラスをオーバーライドすることで、独自のSpringインターセプターを作成することができます。

スプリングインターセプター – HandlerInterceptor

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

    1. boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler): このメソッドは、リクエストがハンドラーメソッドに渡される前にインターセプトするために使用されます。このメソッドは、Springにリクエストを別のSpringインターセプターを介して処理するか、他のSpringインターセプターがない場合はハンドラーメソッドに送信するために ‘true’ を返さなければなりません。このメソッドが ‘false’ を返すと、SpringフレームワークはリクエストがSpringインターセプター自体によって処理され、それ以上の処理が不要であると想定します。この場合、responseオブジェクトを使用してクライアントの要求に応答する必要があります。Object handlerは、要求を処理するために選択されたハンドラーオブジェクトです。このメソッドは例外をスローすることもできますが、その場合はSpring MVC例外処理を使用してエラーページを応答として送信するのに役立ちます。

void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView): このHandlerInterceptorインターセプターメソッドは、HandlerAdapterがハンドラーを呼び出したが、DispatcherServletがビューをレンダリングする前に呼び出されます。このメソッドは、ビューページで使用されるModelAndViewオブジェクトに追加属性を追加するために使用できます。このSpringインターセプターメソッドを使用して、ハンドラーメソッドがクライアントのリクエストを処理するのにかかる時間を判断することができます。

void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex): これはHandlerInterceptorのコールバックメソッドであり、ハンドラーが実行され、ビューがレンダリングされた後に呼び出されます。

複数のスプリング・インターセプターが設定されている場合、preHandle()メソッドは設定の順序で実行されますが、postHandle()およびafterCompletion()メソッドは逆の順序で呼び出されます。コントローラーハンドラーのメソッドのタイミングをログに記録するSpringインターセプターを設定する、シンプルなSpring MVCアプリケーションを作成しましょう。最終的なSpringインターセプターの例プロジェクトは、下のイメージのようになります。このイメージに含まれているコンポーネントに興味を持ちましょう。

スプリングのインターセプター – コントローラークラス

package com.scdev.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);
		//adding some time lag to check interceptor execution
		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";
	}
	
}

私は、Springのインターセプタメソッドが実行されているかを確認するために、ハンドラメソッドの実行において処理時間を追加しているだけです。

Spring MVCのInterceptor – HandlerInterceptorAdapterの実装

簡単にするために、私は抽象クラスであるHandlerInterceptorAdapterを拡張しています。HandlerInterceptorAdapterは、事前専用または事後専用のインターセプタの簡略化された実装のためのHandlerInterceptorインターフェースの抽象アダプタークラスです。

package com.scdev.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のインターセプターの設定

私たちは、リクエストにスプリングのインターセプタを接続する必要があります。すべてのインターセプタをワイヤリングするために、mvc:interceptors要素を使用することができます。また、マッピング要素を通じてリクエストにスプリングのインターセプタを含める前に、URIパターンを指定することもできます。最終的な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.scdev.spring.RequestProcessingTimeInterceptor"></beans:bean>
		</interceptor>
	</interceptors>

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

</beans:beans>

他の全てのウェブアプリケーションのコンポーネントについては説明しません。興味がなく、特定のSpringインターセプター関連の設定もありません。

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

サーブレットコンテナにアプリケーションを展開し、ホームコントローラを呼び出すだけで、以下のようなログ出力を確認できます。

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

出力は、スプリングのインターセプターメソッドが定義された順序で実行されることを確認しています。これでスプリングのインターセプターの使用方法については以上です。下記のリンクからSpringインターセプターの例プロジェクトをダウンロードして、複数のインターセプターを持っていて異なる設定順序で確認してみてください。

Spring Interceptors プロジェクトをダウンロードしてください。

コメントを残す 0

Your email address will not be published. Required fields are marked *