• 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()在控制器方法执行之后,且模型数据都被渲染到视图上后执行
    // 将当前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");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

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

    • 通过ref或bean标签可以设置拦截器但是不能设置拦截规则,此时默认拦截DispatcherServlet处理的所有的请求
    • 通过mvc:interceptor标签可以设置拦截器同时设置拦截的规则即拦截哪些请求或放行哪些请求
    • 使用view-controller标签配置的视图控制器对应的跳转页面的请求只要符合拦截器拦截的规则也会被拦截
    标签名描述
    bean标签的class属性根据全类名告诉SpringMVC哪个类是拦截器
    ref标签的bean属性引用IoC容器中的某个bean的Id作为拦截器,所以需要保证容器中注册了对应的bean
    mvc:mapping设置需要拦截的请求路径
    /**表示拦截所有请求,/* 表示拦截上下文路径后的一层路径的请求
    mvc:exclude-mapping设置排除即不需要拦截的请求路径
    
    <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>
            
            <bean class="com.atguigu.mvc.interceptors.FirstInterceptor">bean>
            
            <ref bean="firstInterceptor">ref>
            
            <mvc:interceptor>
                
            	<mvc:mapping path="/**"/>
                
                <mvc:exclude-mapping path="/"/>
                
                <ref bean="firstInterceptor">ref>
            mvc:interceptor>
        mvc:interceptors>
    beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

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

    <a th:href="@{/testInterceptor}">测试拦截器a>
    
    • 1
    @Controller
    public class TestController {
        @RequestMapping("/testInterceptor")
        @ResponseBody
        public String testInterceptor(){
            return "success";
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    FirstInterceptor-->preHandle
    success
    FirstInterceptor-->postHandle
    FirstInterceptor-->afterCompletion
    
    • 1
    • 2
    • 3
    • 4

    配置多个拦截器

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

    在这里插入图片描述

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

    // 将当前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");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

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

    
    <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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

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

    Controller
    public class TestController {
        @RequestMapping("/testInterceptor")
        @ResponseBody
        public String testInterceptor(){
            return "success";
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    FirstInterceptor-->preHandle
    SecondInterceptor-->preHandle
    success
    FirstInterceptor-->afterCompletion
    
    • 1
    • 2
    • 3
    • 4

    拦截器执行顺序的原理

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

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

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

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

    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);    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

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

    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);    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
  • 相关阅读:
    CCF BDCI 返乡发展人群预测
    APP中RN页面热更新流程-ReactNative源码分析
    Java集合篇:Map接口、Map接口的实现类、Collections集合工具类
    【进阶篇】Java 项目中对使用递归的理解分享
    通付盾Web3专题 | SharkTeam:2023年加密货币犯罪分析报告
    【阿旭机器学习实战】【9】随机梯度下降(SGD)进行乳腺癌良恶性预测,并与逻辑斯蒂回归预测结果进行对比
    【软件测试】01 -- 软件生命周期、软件开发模型
    三分类网络的物理意义是什么?
    Go 语言高级网络编程
    XSS(Cross-site Script,跨站脚本)漏洞笔记
  • 原文地址:https://blog.csdn.net/qq_57005976/article/details/133790911