13SpringMVC中拦截器的配置(拦截规则)和多个拦截器的preHandle,postHandle执行顺序原理详解

拦截器

Servlet中的过滤器的实现及其原理,参考文章

配置一个拦截器

SpringMVC中请求的处理流程: 用户请求--->listener--->filter--->DispatcherServlet--->filter--->preHandle--->controller--->postHandle

第一步: 编写一个Java类实现HandlerInterceptor(HandlerInterceptorAdapter已过时)接口,由于接口中的方法都有默认的方法体,我们可以根据需求实现方法

方法名 功能
boolean preHandle(processedRequest, response) 在控制器方法执行之前执行 , 返回true表示放行即调用控制器方法 , 返回false表示拦截即不调用控制器方法
postHandle(processedRequest, response, mv) 在控制器方法执行之后执行
afterComplation() 在控制器方法执行之后,且模型数据都被渲染到视图上后执行
java 复制代码
// 将当前bean加入IoC容器,这样就可以在配置文件中引用当前的bean作为拦截器
@Component
public class FirstInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("FirstInterceptor-->preHandle");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("FirstInterceptor-->postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("FirstInterceptor-->afterCompletion");
    }
}

第二步: 在SpringMVC的配置文件中将实现了HandlerInterceptor接口的类设置为拦截器并设置拦截的规则

  • 通过ref或bean标签可以设置拦截器但是不能设置拦截规则,此时默认拦截DispatcherServlet处理的所有的请求
  • 通过mvc:interceptor标签可以设置拦截器同时设置拦截的规则即拦截哪些请求或放行哪些请求
  • 使用view-controller标签配置的视图控制器对应的跳转页面的请求只要符合拦截器拦截的规则也会被拦截
标签名 描述
bean标签的class属性 根据全类名告诉SpringMVC哪个类是拦截器
ref标签的bean属性 引用IoC容器中的某个bean的Id作为拦截器,所以需要保证容器中注册了对应的bean
mvc:mapping 设置需要拦截的请求路径 /**表示拦截所有请求,/* 表示拦截上下文路径后的一层路径的请求
mvc:exclude-mapping 设置排除即不需要拦截的请求路径
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
     <!--扫描组件-->
    <context:component-scan base-package="com.atguigu.mvc"></context:component-scan>
    <!--使用view-controller标签配置的视图控制器对应的跳转页面的请求也会被拦截-->
    <mvc:view-controller path="/" view-name="index"></mvc:view-controller>
    <mvc:annotation-driven />
    <!--配置拦截器-->
    <mvc:interceptors>
        <!--根据全类名将某个类设置为拦截器-->
        <bean class="com.atguigu.mvc.interceptors.FirstInterceptor"></bean>
        <!--引用IoC容器中的某个bean的Id作为拦截器,需要保证容器中注册了对应的bean-->
        <ref bean="firstInterceptor"></ref>
        <!--设置拦截器及拦截的规则-->
        <mvc:interceptor>
            <!--配置需要拦截的请求路径-->
        	<mvc:mapping path="/**"/>
            <!--配置不需要拦截的请求路径-->
            <mvc:exclude-mapping path="/"/>
            <!--引用容器中的某个bean作为拦截器-->
            <ref bean="firstInterceptor"></ref>
        </mvc:interceptor>
    </mvc:interceptors>
</beans>

第三步: 编写处理请求的控制器方法,测试拦截器方法的执行顺序,只要请求符合拦截的规则,拦截器中重写的方法就一定会执行,无论有没有匹配请求的控制器方法

html 复制代码
<a th:href="@{/testInterceptor}">测试拦截器</a>
java 复制代码
@Controller
public class TestController {
    @RequestMapping("/testInterceptor")
    @ResponseBody
    public String testInterceptor(){
        return "success";
    }
}
properties 复制代码
FirstInterceptor-->preHandle
success
FirstInterceptor-->postHandle
FirstInterceptor-->afterCompletion

配置多个拦截器

多个拦截器的执行顺序和在SpringMVC的配置文件的中配置的拦截器顺序以及拦截器的preHandle()方法的返回值有关

第一步: 创建一个拦截器类并且preHandle方法的返回值是false

java 复制代码
// 将当前bean加入IoC容器,这样就可以在配置文件中引用当前的bean作为拦截器
@Component
public class SecondInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("SecondInterceptor-->preHandle");
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("SecondInterceptor-->postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("SecondInterceptor-->afterCompletion");
    }
}

第二步: 在SpringMVC的配置文件中设置多个拦截器及其拦截规则

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!--扫描组件-->
    <context:component-scan base-package="com.atguigu.mvc"></context:component-scan>
    <!--配置视图控制器-->
    <mvc:view-controller path="/" view-name="index"></mvc:view-controller>
    <mvc:annotation-driven />
    <!--配置拦截器-->
    <mvc:interceptors>
        <ref bean="firstInterceptor"></ref>
        <ref bean="secondInterceptor"></ref>
    </mvc:interceptors>
</beans>

第三步: 编写控制器方法测试多个拦截器中方法的执行顺序

java 复制代码
Controller
public class TestController {
    @RequestMapping("/testInterceptor")
    @ResponseBody
    public String testInterceptor(){
        return "success";
    }
}
properties 复制代码
FirstInterceptor-->preHandle
SecondInterceptor-->preHandle
success
FirstInterceptor-->afterCompletion

拦截器执行顺序的原理

mappedHandler是控制器执行链,包含控制器方法,拦截器集合(自定义的和SpringMvc提供的),拦截器的索引

正序执行所有拦截器的prehandle方法

java 复制代码
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
    // 如果有一个拦截器的prehandle方法返回fasle,直接终止当前方法即剩下的拦截器和对应控制器的方法都不会执行
    return;
}
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    // 遍历拦截器集合中的每一个拦截器
    for(int i = 0; i < this.interceptorList.size(); i++) {
        HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i);
        // 判断当前拦截器的prehandle方法的返回值
        if (!interceptor.preHandle(request, response, this.handler)) {
            // 如果prehandle方法返回false,调用当前拦截器的afterComplation方法
            this.triggerAfterCompletion(request, response, (Exception)null);
      		// 返回false后剩下的拦截器和对应控制器的方法都不会执行
            return false;
        }
        // interceptorIndex的值是prehandle方法返回的是false的某个拦截器之前的拦截器的索引
        this.interceptorIndex = i;
    }
    // 返回true后才会执行下一个拦截器或对应控制器的方法
    return true;
}

反序执行所有拦截器的posthandle方法

java 复制代码
mappedHandler.applyPostHandle(processedRequest, response, mv);

void applyPostHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    // i的初始值是拦截器集合中的最后一个拦截器的索引
    for(int i = this.interceptorList.size(); i >= 0; i--) {
        HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i);
        interceptor.postHandle(request, response, this.handler,mv);    
    }
}

反序执行所有拦截器的aftercompletion方法

java 复制代码
if(mappedHandler != null){
	mappedHandler.triggerAfterCompletion(request, response, (Exception)null);      
}

void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response) throws Exception {
    // i的初始值是最后一个prehandle方法返回的是false的某个拦截器之前的拦截器的索引
    for(int i = this.interceptorIndex; i >= 0; i--) {
        HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i);
        interceptor.afterCompletion(request, response, this.handler,ex);    
    }
}
相关推荐
程序员南飞2 小时前
ps aux | grep smart_webrtc这条指令代表什么意思
java·linux·ubuntu·webrtc
弥琉撒到我2 小时前
微服务swagger解析部署使用全流程
java·微服务·架构·swagger
一颗花生米。2 小时前
深入理解JavaScript 的原型继承
java·开发语言·javascript·原型模式
问道飞鱼2 小时前
Java基础-单例模式的实现
java·开发语言·单例模式
ok!ko6 小时前
设计模式之原型模式(通俗易懂--代码辅助理解【Java版】)
java·设计模式·原型模式
2402_857589367 小时前
“衣依”服装销售平台:Spring Boot框架的设计与实现
java·spring boot·后端
吾爱星辰7 小时前
Kotlin 处理字符串和正则表达式(二十一)
java·开发语言·jvm·正则表达式·kotlin
哎呦没8 小时前
大学生就业招聘:Spring Boot系统的架构分析
java·spring boot·后端
编程、小哥哥8 小时前
netty之Netty与SpringBoot整合
java·spring boot·spring
IT学长编程9 小时前
计算机毕业设计 玩具租赁系统的设计与实现 Java实战项目 附源码+文档+视频讲解
java·spring boot·毕业设计·课程设计·毕业论文·计算机毕业设计选题·玩具租赁系统