拦截器概述
Spring MVC中的拦截器(Interceptor)类似于Servlet中的过滤器(Filter),它主要用于拦截用户请求并做出相应的处理。例如通过拦截器可以进行权限验证、记录请求信息的日志、判断用户是否登录。
拦截器的定义
要使用Spring MVC中的拦截器,就要对拦截器进行定义和配置。通常拦截器类可以通过两种方式来定义。一种是通过实现HandleInterceptor接口,另一种是通过实现WebRequestInterceptor接口。
以实现HandleInterceptor接口的定义方式为例,自定义拦截器类的代码如下所示:
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
/**
* 实现了HandlerInterceptor接口的自定义拦截器类
*/
public class CustomInterceptor implements HandlerInterceptor{
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler)throws Exception {
return false;
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler,
Exception ex) throws Exception {
}
}
关于上述代码中的三个方法具体描述如下:
preHandler()方法: 该方法会在控制器方法执行前执行,其返回值表示是否中断后续操作。当返回值为true时,表示继续向下执行下去;当其返回值为false时,会中断后续所有的操作。
postHandle()方法: 该方法会在控制器方法调用完之后,且解析视图之前执行。
afterCompletion()方法: 该方法会在整个请求完成,即视图渲染结束之后执行。
拦截器的配置
要使自定义拦截器生效,还需要在Spring MVC的配置文件中进行配置,配置代码如下所示:
<!-- 配置拦截器 -->
<mvc:interceptors>
<!--使用bean直接定义在<mvc:interceptors>下面的拦截器将拦截所有请求-->
<bean class="com.lwz.interceptor.CustomInterceptor"/>
<!-- 拦截器1 -->
<mvc:interceptor>
<!-- 配置拦截器作用的路径 -->
<mvc:mapping path="/**" />
<!-- 配置不需要拦截器作用的路径 -->
<mvc:exclude-mapping path="" />
<!-- 定义在<mvc:interceptor>下面的表示匹配指定路径的请求才进行拦截的 -->
<bean class="com.lwz.interceptor.Interceptor1" />
</mvc:interceptor>
<!-- 拦截器2 -->
<mvc:interceptor>
<mvc:mapping path="/hello" />
<bean class="com.lwz.interceptor.Interceptor2" />
</mvc:interceptor>
<mvc:interceptor>
<!-- 拦截所有的路径 -->
<mvc:mapping path="/**" />
<bean class="com.lwz.interceptor.LoginInterceptor" />
</mvc:interceptor>
.....
</mvc:interceptors>
在配置拦截器的时候要注意的一点是,<mvc:interceptor>
中的子元素顺序必须按照<mvc:mapping ... />
——><mvc:exclude-mapping ... />
——><bean ... />
的顺序。
拦截器的执行流程
单个拦截器的执行流程
在运行程序时,拦截器的执行是有一定顺序的,该顺序与配置文件中所定义的拦截器的顺序是相关的。如果项目中只定义一个拦截器,那么该拦截器在程序中的执行顺序如图所示:
为了验证上面所讲解的拦截器执行流程,下面用过一个案例来演示单个拦截器的使用
创建控制器类
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloController {
/**
* 页面跳转
*/
@RequestMapping("/hello")
public String Hello() {
System.out.println("Hello!");
return "success";
}
}
创建拦截器类
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
/**
* 实现了HandlerInterceptor接口的自定义拦截器类
*/
public class CustomInterceptor implements HandlerInterceptor{
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler)throws Exception {
System.out.println("CustomInterceptor...preHandle");
//对拦截的请求进行放行处理
return true;
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("CustomInterceptor...postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler,
Exception ex) throws Exception {
System.out.println("CustomInterceptor...afterCompletion");
}
}
创建并配置Spring MVC配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- 定义组件扫描器,指定需要扫描的包 -->
<context:component-scan base-package="包名" />
<!-- 定义视图解析器 -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 设置前缀 -->
<property name="prefix" value="/WEB-INF/jsp/" />
<!-- 设置后缀 -->
<property name="suffix" value=".jsp" />
</bean>
<!-- 配置拦截器 -->
<mvc:interceptors>
<!--使用bean直接定义在<mvc:interceptors>下面的拦截器将拦截所有请求-->
<bean class="包名.CustomInterceptor"/>
</mvc:interceptors>
输出结果为:
CustomInterceptor...preHandle
Hello!
CustomInterceptor...postHandle
CustomInterceptor...afterCompletion
多个拦截器的执行流程
在大型的企业级项目中,通常不止一个拦截,可能会定义很多个拦截器来实现不同的功能。那么多个拦截器是如何运行的呢?接下来通过一张流程图来描述拦截器的执行流程。(假设有两个拦截器interceptor1和interceptor2,在配置文件中,1在2的前面)
从上图可以看出,当多个拦截器提示工作的时候,它们的preHandle()方法会按照配置文件中拦截器的配置顺序执行,而它们的postHandle()方法和afterCompletion()方法则按照配置顺序的反序执行
为了验证上面所讲解的拦截器执行流程,下面用过一个案例来演示多个拦截器的使用
创建两个拦截器,拦截器1和拦截器2
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
/**
* 以实现接口的方式定义拦截器
*/
public class Interceptor1 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
System.out.println("Intercepter1...preHandle");
return true;
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("Intercepter1...postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler,
Exception ex) throws Exception {
System.out.println("Intercepter1...afterCompletion");
}
}
拦截器2
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
/**
* 以实现接口的方式定义拦截器
*/
public class Interceptor2 implements HandlerInterceptor{
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
System.out.println("Interceptor2...preHandle");
return true;
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("intercepter2...postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("Intercepter2...afterCompletion");
}
}
在Spring MVC配置文件中,配置拦截器
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- 定义组件扫描器,指定需要扫描的包 -->
<context:component-scan base-package="包名.controller" />
<!-- 定义视图解析器 -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 设置前缀 -->
<property name="prefix" value="/WEB-INF/jsp/" />
<!-- 设置后缀 -->
<property name="suffix" value=".jsp" />
</bean>
<!-- 配置拦截器 -->
<mvc:interceptors>
<!-- 拦截器1 -->
<mvc:interceptor>
<!-- 配置拦截器作用的路径 -->
<mvc:mapping path="/**" />
<!-- 定义在<mvc:interceptor>下面的表示匹配指定路径的请求才进行拦截的 -->
<bean class="包名.Interceptor1" />
</mvc:interceptor>
<!-- 拦截器2 -->
<mvc:interceptor>
<mvc:mapping path="/hello" />
<bean class="包名.Interceptor2" />
</mvc:interceptor>
</mvc:interceptors>
</beans>
输出结果为:
Interceptor1...preHandle
Interceptor2...preHandle
Hello!
Interceptor2...postHandle
Interceptor1...postHandle
Interceptor2...afterCompletion
Interceptor1...afterCompletion