Spring MVC拦截器完全指南:HandlerInterceptor与HandlerInterceptorAdapter实战教程

这是文章《Spring MVC 的拦截器 HandlerInterceptorAdapter、HandlerInterceptor 示例》的第1部分(共1部分)。

Spring拦截器被用于拦截客户端请求并处理它们。有时候,我们想要在将HTTP请求交给控制器处理方法之前拦截它并进行一些处理。这就是Spring MVC拦截器的用途所在。

Spring拦截器

就像我们有Struts2拦截器一样,我们可以通过实现org.springframework.web.servlet.HandlerInterceptor接口或者通过重写抽象类org.springframework.web.servlet.handler.HandlerInterceptorAdapter来创建我们自己的Spring拦截器。这个抽象类提供了HandlerInterceptor接口的基本实现。

Spring拦截器 – HandlerInterceptor

Spring的HandlerInterceptor根据我们希望拦截HTTP请求的位置声明了三个方法。

  1. boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler):在请求交给处理方法之前,用于拦截请求的方法。如果没有更多的Spring拦截器,该方法应返回’true’,以让Spring知道通过另一个Spring拦截器处理请求,或将请求发送给处理方法。如果该方法返回’false’,则Spring框架将假定请求已经由Spring拦截器自身处理,并且不需要进一步处理。在这种情况下,我们应该使用响应对象将响应发送到客户端请求。Object handler是用于处理请求的选定处理器对象。此方法还可以抛出异常,在这种情况下,Spring MVC异常处理应该用于将错误页面作为响应发送。
  2. void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView):当HandlerAdapter调用处理程序但DispatcherServlet尚未呈现视图时,调用此HandlerInterceptor拦截器方法。可以使用此方法向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.Olivia.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;

/**
 * 处理应用程序主页面的请求
 */
@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("欢迎访问首页!客户端地区设置是 {}.", 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("在返回视图页面之前");
		return "home";
	}
	
}

我只是在执行处理程序方法时增加一些处理时间,以检查我们的Spring拦截器方法的执行情况。

Spring MVC 拦截器 – HandlerInterceptorAdapter 实现

为了简化,我将抽象类HandlerInterceptorAdapter进行了扩展。HandlerInterceptorAdapter是HandlerInterceptor接口的抽象适配器类,用于简化仅实现前置拦截器或后置拦截器的操作。

package com.Olivia.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("请求URL::" + request.getRequestURL().toString()
				+ ":: 开始时间=" + 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("请求URL::" + request.getRequestURL().toString()
				+ " 发送到处理程序 :: 当前时间=" + 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("请求URL::" + request.getRequestURL().toString()
				+ ":: 结束时间=" + System.currentTimeMillis());
		logger.info("请求URL::" + request.getRequestURL().toString()
				+ ":: 耗时=" + (System.currentTimeMillis() - startTime));
	}

}

逻辑非常简单,我只是记录处理程序执行的时间以及包括渲染视图页面在内的请求处理所花费的总时间。

Spring MVC拦截器配置

我们需要将Spring拦截器与请求进行连接,我们可以使用mvc:interceptors元素来连接所有的拦截器。我们还可以通过mapping元素提供URI模式来匹配,在包含Spring拦截器之前为请求进行匹配。我们最终的Spring bean配置文件(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上下文:定义此servlet的请求处理基础设施 -->

	<!-- 启用Spring MVC @Controller编程模型 -->
	<annotation-driven />

	<!-- 通过高效地提供${webappRoot}/resources目录中的静态资源来处理/resources/**的HTTP GET请求 -->
	<resources mapping="/resources/**" location="/resources/" />

	<!-- 将@Controller选择的渲染视图解析为/WEB-INF/views目录中的.jsp资源 -->
	<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>

	<!-- 基于URI配置拦截器 -->
	<interceptors>
		<interceptor>
			<mapping path="/home" />
			<beans:bean class="com.Olivia.spring.RequestProcessingTimeInterceptor"></beans:bean>
		</interceptor>
	</interceptors>

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

</beans:beans>

由于我们对其他组件不感兴趣且它们没有特定的Spring拦截器相关的配置,我不会解释所有其他网页应用程序的组件。

Spring MVC 拦截器应用测试

只需将应用程序部署在Servlet容器中并调用主页控制器,您将会看到类似下面的日志输出。

INFO : com.Olivia.spring.RequestProcessingTimeInterceptor - 请求URL::https://localhost:9090/SpringInterceptors/home:: 开始时间=1396906442086
INFO : com.Olivia.spring.HomeController - 欢迎访问首页!客户端地区设置是 en_US.
INFO : com.Olivia.spring.HomeController - 在返回视图页面之前
请求URL::https://localhost:9090/SpringInterceptors/home 发送到处理程序 :: 当前时间=1396906443098
INFO : com.Olivia.spring.RequestProcessingTimeInterceptor - 请求URL::https://localhost:9090/SpringInterceptors/home:: 结束时间=1396906443171
INFO : com.Olivia.spring.RequestProcessingTimeInterceptor - 请求URL::https://localhost:9090/SpringInterceptors/home:: 耗时=1085

输出结果确认了Spring拦截器方法的执行顺序与定义的顺序一致。以上就是使用Spring拦截器的全部内容。您可以从下面的链接下载Spring拦截器示例项目,并尝试使用多个拦截器以及不同的配置顺序进行检验。

下载Spring拦截器项目

bannerAds