Пример обработчика перехватчика Spring MVC Interceptor HandlerInterceptorAdapter, HandlerInterceptor

Spring Interceptor используются для перехвата клиентских запросов и их обработки. Иногда мы хотим перехватить HTTP-запрос и выполнить некоторую обработку перед передачей его методам обработчика контроллера. Вот где пригодны Spring MVC Interceptor.

Spring Interceptor

Как и у Struts2 Interceptors, мы можем создать собственный Spring interceptor, реализовав либо интерфейс org.springframework.web.servlet.HandlerInterceptor, либо переопределив абстрактный класс org.springframework.web.servlet.handler.HandlerInterceptorAdapter, который предоставляет базовую реализацию интерфейса HandlerInterceptor.

Spring Interceptor – HandlerInterceptor

Spring HandlerInterceptor объявляет три метода, основанных на том, где мы хотим перехватить HTTP-запрос.

  1. boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler): Этот метод используется для перехвата запроса до передачи его методу обработчика. Этот метод должен возвращать ‘true’, чтобы Spring знал, что обработку запроса следует выполнять через другой перехватчик Spring или передавать его методу обработчика, если дополнительных перехватчиков Spring нет. Если этот метод возвращает ‘false’, фреймворк Spring предполагает, что запрос уже обработан перехватчиком Spring, и дополнительная обработка не требуется. В этом случае мы должны использовать объект response для отправки ответа на запрос клиента. Объект 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 Interceptor будет выглядеть как на изображении ниже, мы рассмотрим компоненты, которые нас интересуют.

Spring Interceptor – Класс контроллера

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 Interceptor – Реализация HandlerInterceptorAdapter

Для простоты я расширяю абстрактный класс HandlerInterceptorAdapter. HandlerInterceptorAdapter является абстрактным адаптерным классом для интерфейса HandlerInterceptor, предназначенным для упрощенной реализации только предварительных/последующих перехватчиков.

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);
		// если вернулось false, мы должны убедиться, что 'response' отправлен
		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());
		// мы можем добавить атрибуты в modelAndView и использовать их на странице представления
	}

	@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 для связывания всех перехватчиков. Мы также можем указать шаблон URI для сопоставления до включения перехватчика Spring для запроса через элемент 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

Просто разверните приложение в контейнере сервлетов и вызовите контроллер home, вы увидите вывод регистратора что-то вроде ниже.

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 выполняются в определенном порядке. Вот и все, что касается использования перехватчиков Spring. Вы можете скачать пример проекта с использованием перехватчиков Spring по ссылке ниже и попробовать использовать несколько перехватчиков, проверив различный порядок конфигурации.

Скачать проект с использованием перехватчиков Spring

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