SpringMVC拦截器(Interceptor)详解

1、拦截器概述

1.1 什么是拦截器?

       Spring MVC中的拦截器(Interceptor)类似于Servlet中的过滤器(Filter),但是比过滤器的功能更加强大,它主要用于拦截用户请求并作相应的处理。例如通过拦截器可以进行权限验证、记录请求信息的日志、判断用户是否登录等。要使用Spring MVC中的拦截器,就需要对拦截器类进行定义和配置。通常拦截器类可以通过两种方式来定义:
1. 通过实现HandlerInterceptor接口,或继承HandlerInterceptor接口的实现类(如HandlerInterceptorAdapter)来定义。
2. 通过实现WebRequestInterceptor接口,或继承WebRequestInterceptor接口的实现类来定义。

2、自定义拦截器

以实现HandlerInterceptor接口方式为例,自定义拦截器类的代码如下:

package com.xust.iot.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

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

public class MyFirstInterceptor implements HandlerInterceptor {


@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

   System.out.println("1.执行目标方法之前......");

	//返回true表示放行,可以去执行目标方法,否者表示不允许执行目标方法
	return true;
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
	System.out.println("3.执行目标方法之后......");
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
	 System.out.println("5.来到页面之后......");
}
}

上述代码中,自定义拦截器实现了HandlerInterceptor接口,并实现了接口中的三个方法:

  • preHandle():在目标方法执行之前回执行,有个boolean类型的返回值,当返回true表示放行,即允许执行目标方法;当返回false,表示不放行,即不运行执行目标方法,此时会中断以后的所有过程
  • postHandle():在目标方法执行结束后会执行,且解析视图之前执行
  • afterCompletion():在请求到达页面,即视图渲染完成后执行

开发拦截器就像开发servlet或者filter一样,都需要在配置文件进行配置,配置代码如下:

<!--拦截器-->
<mvc:interceptors>
	<!--这样配置的拦截器默认拦截所有请求-->
	<bean id="myInterceptor" class="com.xust.iot.interceptor.MyFirstInterceptor"/>
</mvc:interceptors>

上面的代码中,<mvc:interceptors>元素用于配置一组拦截器,子元素<bean>中定义的是全局拦截器,它会拦截所有的请求;而也可以使用<mvc:interceptor>元素中定义指定路径的拦截器,它会对指定路径下的请求生效。<mvc:interceptor>元素的子元素<mvc:mapping>用于配置拦截器作用的路径,该路径在其属性path 中定义。如果在请求路径中包含不需要拦截的内容,还可以通过<mvc:exclude-mapping>元素进行配置。
注意:<mvc:interceptor>中的子元素必须按照上述代码中的配置顺序进行编写,即<mvc:mapping> <mvc:exclude-mapping> <bean>,否则文件会报错。

下面写一个控制器来测试一下正常情况下单个拦截器的工作流程:

package com.xust.iot.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class IntercrtorTestController {

@RequestMapping(value = "/test01")
public String handler01(Model model){

	System.out.println("2.执行了目标方法......");
	model.addAttribute("msg","你好啊!!!");
	return "success";
}

}

目标页面success.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>成功</title>
</head>
<body>

<%
  System.out.println("4.来到了success.jsp页面");
%>
${msg}

</body>
</html>

测试结果

2.1 单个拦截器正常情况下的工作流程:
  1. 拦截器preHandle方法执行,返回true继续以后的过程
  2. 控制器目标方法执行
  3. 拦截器postHandle方法执行
  4. 页面渲染完成来到页面
  5. 拦截器afterCompletion方法执行

单个拦截器非正常情况下的工作流程
单个拦截器的非正常情况分为两种情况:
    1、拦截器中的preHandler方法返回false;
    2、虽然preHandler方法返回了true,但是其中有一个过程“炸了”,比如发生了异常没有处理
接下来通过代码来测试:

- 第一种情况:preHandler返回false

package com.xust.iot.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

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

public class MyFirstInterceptor implements HandlerInterceptor {


@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

   System.out.println("1.执行目标方法之前......");

	//返回true表示放行,可以去执行目标方法,否者表示不允许执行目标方法
	return false;
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
	System.out.println("3.执行目标方法之后......");
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
	 System.out.println("5.来到页面之后......");
}
}

测试结果:

可以看到,后面的过程直接无法执行
  • 第二种情况:preHandle方法放行了,但是有一个过程“炸了”,比如我们在控制器目标方法中制造一个异常:
package com.xust.iot.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class IntercrtorTestController {

@RequestMapping(value = "/test01")
public String handler01(Model model){

    int i=30/0;
	System.out.println("2.执行了目标方法......");
	model.addAttribute("msg","你好啊!!!");
	return "success";
}

}

测试结果:

可以看到,只要拦截器的prehandle方法放行了,拦截器的afterCompletion方法总会执行/font>
2.2 多个拦截器正常情况下的工作流程:

实现第二个自定义拦截器:

package com.xust.iot.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

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

public class MySecondInterceptor implements HandlerInterceptor {

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
	System.out.println("1.执行目标方法之前......MySecondInterceptor");
	return true;
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
	System.out.println("3.执行目标方法之后......MySecondInterceptor");
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
	System.out.println("5.来到页面之后......MySecondInterceptor");
}
}

配置第二个拦截器

<!--拦截器-->
<mvc:interceptors>
     <!--拦截器1-->
	<!--这样配置的拦截器默认拦截所有请求-->
	<bean id="myInterceptor" class="com.xust.iot.interceptor.MyFirstInterceptor"/>

    <!--拦截器2-->
	<!--使用<mvc:interceptor>可以具体配置拦截器拦截那些请求>-->
	<mvc:interceptor>
		<mvc:mapping path="/test01"/>
		<bean  class="com.xust.iot.interceptor.MySecondInterceptor"/>
	</mvc:interceptor>
</mvc:interceptors>

执行结果:

交换拦截器在springmvc配置文件中的定义顺序:

<!--拦截器-->
<mvc:interceptors>
    <!--拦截器2-->
	<!--使用<mvc:interceptor>可以具体配置拦截器拦截那些请求>-->
	<mvc:interceptor>
		<mvc:mapping path="/test01"/>
		<bean  class="com.xust.iot.interceptor.MySecondInterceptor"/>
	</mvc:interceptor>

     <!--拦截器1-->
	<!--这样配置的拦截器默认拦截所有请求-->
	<bean id="myInterceptor" class="com.xust.iot.interceptor.MyFirstInterceptor"/>
</mvc:interceptors>

执行结果:

测试结果表明:

  • 多个拦截器是有执行的先后顺序的,这个顺序就是定义的先后顺序
  • 拦截器的preHandle方法:按照定义顺序顺序执行的
  • 拦截器的postHandle方法:按照定义顺序逆序执行的
  • 拦截器的preHandle方法:按照定义顺序顺逆序执行的
  • 多个拦截器非正常情况下的工作流程
    1、一个拦截器的preHandle方法返回false的情况:
2、所有拦截器的preHandle方法都返回true,但是中间有过程发生了异常:

3、小结

3.1 单拦截器的执行顺序
  • 正常情况下会按照:preHandle—>目标方法—>postHnadle—>页面渲染—>afterCompetion执行
  • 当preHandle返回false,就没有以后流程的事儿了
  • 当preHandler返回了true,但是中间过程发生异常,会直接结束以后的流程但是afterCompetion总会执行
3.2 多个拦截器的执行顺序
  • 正常情况
会按照配置中定义的顺序顺序执行所有拦截器的preHandle方法,然后执行控制器中目标方法,之后按照定义顺序的逆序执行postHandle方法,然后渲染页面,最后按照定义的顺序的逆序执行afterComprtion方法
  • 有拦截器返回false
在多个拦截器中只要有一个拦截器的preHandle方法返回了false,那么以后的流程都没有了,会直接回按照这些拦截器配置的定义顺序的逆序执行afterCompetion方法

留言区

还能输入500个字符