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 多个拦截器正常情况下的工作流程: 实现第二个自定义拦截器: ```java 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"); } } ``` 配置第二个拦截器 ```xml ``` 执行结果:
交换拦截器在springmvc配置文件中的定义顺序: ```xml ``` 执行结果:
测试结果表明:
  • 多个拦截器是有执行的先后顺序的,这个顺序就是定义的先后顺序
  • 拦截器的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个字符