• Spring MVC 中的拦截器的使用“拦截器基本配置” 和 “拦截器高级配置”


    1. Spring MVC 中的拦截器的使用“拦截器基本配置” 和 “拦截器高级配置”

    @


    2. 拦截器

    拦截器(Interceptor) 类似于过滤器(Filter)

    Spring MVC 的拦截器作用是在请求到达控制器之前或之后进行拦截,可以对请求和响应进行一些特定的处理。

    拦截器可以用于很多场景下:

    1. 登录验证:对于需要登录才能访问的地址,使用拦截器可以判断用户是否已登录,如果未登录,则跳转到登录页面。
    2. 权限校验:根据用户权限对部分网址进行访问控制,拒绝未经授权的用户访问。
    3. 请求日志:记录请求信息,例如:请求地址,请求参数,请求时间等,用于排查问题和性能优化。
    4. 更改响应:可以对响应的内容进行修改,例如:添加头信息,调整响应内容格式等。

    拦截器和过滤器的区别在于它们的作用层面不同:

    • 过滤器更注重在请求和响应的流程中进行处理,可以修改请求和响应的内容,例如:设置编码和字符集,请求头,状态码等。
    • 拦截器则更加侧重于对控制器进行前置或后置处理,在请求到达控制器之前或之后进行特定的操作,例如:打印日志,权限验证等。

    Filter、Servlet、Interceptor、Controller的执行顺序:

    在这里插入图片描述

    3. Spring MVC 中的拦截器的创建和基本配置

    3.1 定义拦截

    实现org.springframework.web.servlet.HandlerInterceptor 接口,共有三个方法可以进行选择性的实现:

    在这里插入图片描述

    • preHandle( ):处理器方法调用之前执行。只有该方法有返回值,返回值是布尔类型,true 表示放行,false 表示拦截
    • postHandle( ):处理器方法调用之后执行。
    • afterCompletion( ):渲染完成后执行。

    3.2 拦截器基本配置

    第一步:编写拦截器,该拦截器要实现org.springframework.web.servlet.HandlerInterceptor 接口

    在这里插入图片描述

    package com.rainbowsea.springmvc.interceptors;
    
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    import org.springframework.stereotype.Component;
    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("Interceptor1's preHandle!");
            return true;
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            System.out.println("Interceptor1's postHandle!");
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            System.out.println("Interceptor1's afterCompletion!");
        }
    }
    
    

    在 Spring MVC 中拦截器的基本配置有两种方式:

    • 第一种方式是:通过 xml 进行配置
    • 第二种方式是:通过 @Component 注解 + xml 文件进行配置

    第一种方式:通过 xml 进行配置

    需要注意的是:这个基本配置,默认情况下是拦截所有请求的。

    在 springmvc.xml 文件中进行如下配置:

    <mvc:interceptors>
        <bean class="com.powernode.springmvc.interceptors.Interceptor1"/>
    mvc:interceptors>
    

    在这里插入图片描述

    
    <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/cache http://www.springframework.org/schema/cache/spring-cache.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
    
        
        <context:component-scan
                base-package="com.rainbowsea.springmvc.controller,com.rainbowsea.springmvc.interceptors">context:component-scan>
    
        
        <bean id="thymeleafViewResolver" class="org.thymeleaf.spring6.view.ThymeleafViewResolver">
            
            <property name="characterEncoding" value="UTF-8"/>
            
            <property name="order" value="1"/>
            
            <property name="templateEngine">
                <bean class="org.thymeleaf.spring6.SpringTemplateEngine">
                    
                    <property name="templateResolver">
                        <bean class="org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver">
                            
                            <property name="prefix" value="/WEB-INF/templates/"/>
                            
                            <property name="suffix" value=".html"/>
                            
                            <property name="templateMode" value="HTML"/>
                            
                            <property name="characterEncoding" value="UTF-8"/>
                        bean>
                    property>
                bean>
            property>
        bean>
    
        
            <mvc:interceptors>
        
                <bean class="com.rainbowsea.springmvc.interceptors.Interceptor1">bean>
            mvc:interceptors>
    beans>
    

    编写对应的 Controller 控制器进行测试:

    在这里插入图片描述

    package com.rainbowsea.springmvc.controller;
    
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    @Controller  // 交给 Spring IOC 容器管理
    public class IndexController {
    
    
        @RequestMapping("/index")
        public String toIndex() {
            System.out.println("IndexController#toIndex()  ---> 处理器方法执行了");
            return "index";
        }
    
    
        @RequestMapping("ok")
        public String toOK() {
            System.out.println("IndexController#OK() ---> 处理器方法执行了");
            return "ok";
        }
    }
    
    

    运行测试:

    在这里插入图片描述

    在这里插入图片描述

    第二种方式是:通过 @Component 注解 + xml 文件进行配置

    注意:同样的,对于这种基本配置来说,拦截器是拦截所有请求的。

    第二种方式的前提:

    1. 前提1:包扫描,在 spring mvc 中配置组件扫描

    在这里插入图片描述

    1. 前提2:使用 @Component 注解进行对 编写的拦截器类进行标注即可。
      在这里插入图片描述
    1. 两个前提都搞定了,就可以在 spring mvc.xml 文件中进行配置了。
    2. 在这里插入图片描述
    <mvc:interceptors>
        <ref bean="interceptor1"/>
    mvc:interceptors>
    

    运行测试:

    在这里插入图片描述

    3.3 拦截器的高级配置

    采用以上基本配置方式,拦截器是拦截所有请求路径的。如果要针对某些路径进行拦截,某些路径不拦截,某些路径拦截,可以采用高级配置:在 spring mvc.xml 文件当中进行配置

    在这里插入图片描述

    以上的配置表示,除 /ok 请求路径之外,剩下的路径全部拦截。

    
    <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/cache http://www.springframework.org/schema/cache/spring-cache.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
    
        
        <context:component-scan
                base-package="com.rainbowsea.springmvc.controller,com.rainbowsea.springmvc.interceptors">context:component-scan>
    
        
        <bean id="thymeleafViewResolver" class="org.thymeleaf.spring6.view.ThymeleafViewResolver">
            
            <property name="characterEncoding" value="UTF-8"/>
            
            <property name="order" value="1"/>
            
            <property name="templateEngine">
                <bean class="org.thymeleaf.spring6.SpringTemplateEngine">
                    
                    <property name="templateResolver">
                        <bean class="org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver">
                            
                            <property name="prefix" value="/WEB-INF/templates/"/>
                            
                            <property name="suffix" value=".html"/>
                            
                            <property name="templateMode" value="HTML"/>
                            
                            <property name="characterEncoding" value="UTF-8"/>
                        bean>
                    property>
                bean>
            property>
        bean>
    
        
        <mvc:interceptors>
            <mvc:interceptor>
                
                <mvc:mapping path="/**"/>
                
                <mvc:exclude-mapping path="/ok"/>
                
                
                
                <ref bean="interceptor1">ref>
            mvc:interceptor>
    
        mvc:interceptors>
    beans>
    

    运行测试:

    在这里插入图片描述

    4. Spring MVC中多个拦截器的执行顺序

    这里我们为了探究,多个拦截器存在的时候的执行顺序,我们创建 3 个 拦截器。如下:

    4.1 如果所有拦截器 preHandle( ) 方法 都返回 true时,多个拦截器的的执行顺序

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    配置多个拦截器

    在这里插入图片描述

        <mvc:interceptors>
    
            
            <ref bean="interceptor1">ref>
            <ref bean="interceptor2">ref>
            <ref bean="interceptor3">ref>
        mvc:interceptors>
    
    
    <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/cache http://www.springframework.org/schema/cache/spring-cache.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
    
        
        <context:component-scan
                base-package="com.rainbowsea.springmvc.controller,com.rainbowsea.springmvc.interceptors">context:component-scan>
    
        
        <bean id="thymeleafViewResolver" class="org.thymeleaf.spring6.view.ThymeleafViewResolver">
            
            <property name="characterEncoding" value="UTF-8"/>
            
            <property name="order" value="1"/>
            
            <property name="templateEngine">
                <bean class="org.thymeleaf.spring6.SpringTemplateEngine">
                    
                    <property name="templateResolver">
                        <bean class="org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver">
                            
                            <property name="prefix" value="/WEB-INF/templates/"/>
                            
                            <property name="suffix" value=".html"/>
                            
                            <property name="templateMode" value="HTML"/>
                            
                            <property name="characterEncoding" value="UTF-8"/>
                        bean>
                    property>
                bean>
            property>
        bean>
    
        <mvc:interceptors>
    
            
            <ref bean="interceptor1">ref>
            <ref bean="interceptor2">ref>
            <ref bean="interceptor3">ref>
        mvc:interceptors>
    beans>
    

    如果所有拦截器 preHandle 都返回 true

    在这里插入图片描述

    按照 springmvc.xml文件中配置的顺序,自上而下调用 preHandle:

    在这里插入图片描述

    在这里插入图片描述

    4.2 如果其中一个拦截器 preHandle ( ) 方法,返回 false,多个拦截器的的执行顺序

    Interceptor3 拦截器中的 preHandle()方法返回 false。其他两个拦截器返回 true.

    在这里插入图片描述

    规则:只要有一个拦截器preHandle返回false,任何postHandle都不执行。但返回false的拦截器的前面的拦截器按照逆序执行afterCompletion

    在这里插入图片描述

    1. 只要有一个拦截器preHandle()方法,返回false,则任何拦截器的 postHandle()方法都不执行。但返回 false 的拦截器的前面的拦截器按照逆序执行afterCompletion

    2. 返回 false 拦截器,拦截住了,则其中的 Controllor控制器不执行了,其中的 postHandle

      一个也不会执行。而对应的 afterCompletion()方法,的执行是按照配置拦截器(自上而下)的倒序执行,但其中返回 false 的拦截器中的 afterCompletion()方法不会被执行

    3. 只要有一个拦截器preHandle返回false,任何postHandle都不执行。但返回false的拦截器的前面的拦截器按照逆序执行afterCompletion。只要有一个拦截器preHandle返回false,任何postHandle都不执行。但返回false的拦截器的前面的拦截器按照逆序执行afterCompletion

    5. 补充:源码分析

    5.1 方法执行顺序的源码分析

    public class DispatcherServlet extends FrameworkServlet {
        protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
            // 调用所有拦截器的 preHandle 方法
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }
            // 调用处理器方法
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
            // 调用所有拦截器的 postHandle 方法
            mappedHandler.applyPostHandle(processedRequest, response, mv);
            // 处理视图
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
        }
    
        private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
    			@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
    			@Nullable Exception exception) throws Exception {
            // 渲染页面
            render(mv, request, response);
            // 调用所有拦截器的 afterCompletion 方法
            mappedHandler.triggerAfterCompletion(request, response, null);
        }
    }
    

    5.2 拦截与放行的源码分析

    public class DispatcherServlet extends FrameworkServlet {
        protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
            // 调用所有拦截器的 preHandle 方法
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                // 如果 mappedHandler.applyPreHandle(processedRequest, response) 返回false,以下的return语句就会执行
                return;
            }
        }
    }
    
    public class HandlerExecutionChain {
        boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    		for (int i = 0; i < this.interceptorList.size(); i++) {
    			HandlerInterceptor interceptor = this.interceptorList.get(i);
    			if (!interceptor.preHandle(request, response, this.handler)) {
    				triggerAfterCompletion(request, response, null);
                    // 如果 interceptor.preHandle(request, response, this.handler) 返回 false,以下的 return false;就会执行。
    				return false;
    			}
    			this.interceptorIndex = i;
    		}
    		return true;
    	}
    }
    

    5.3 DispatcherServlet 和 HandlerExecutionChain 的部分源码:

    public class DispatcherServlet extends FrameworkServlet {
        protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
            // 按照顺序执行所有拦截器的preHandle方法
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }
            // 执行处理器方法
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
            // 按照逆序执行所有拦截器的 postHanle 方法
            mappedHandler.applyPostHandle(processedRequest, response, mv);
            // 处理视图
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
        }
    
        private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
    			@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
    			@Nullable Exception exception) throws Exception {
            // 渲染视图
            render(mv, request, response);
            // 按照逆序执行所有拦截器的 afterCompletion 方法
            mappedHandler.triggerAfterCompletion(request, response, null);
        }
    }
    
    public class HandlerExecutionChain {
        // 顺序执行 preHandle
        boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
            for (int i = 0; i < this.interceptorList.size(); i++) {
                HandlerInterceptor interceptor = this.interceptorList.get(i);
                if (!interceptor.preHandle(request, response, this.handler)) {
                    // 如果其中一个拦截器preHandle返回false
                    // 将该拦截器前面的拦截器按照逆序执行所有的afterCompletion
                    triggerAfterCompletion(request, response, null);
                    return false;
                }
                this.interceptorIndex = i;
            }
            return true;
    	}
        // 逆序执行 postHanle
        void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {
            for (int i = this.interceptorList.size() - 1; i >= 0; i--) {
                HandlerInterceptor interceptor = this.interceptorList.get(i);
                interceptor.postHandle(request, response, this.handler, mv);
            }
    	}
        // 逆序执行 afterCompletion
    	void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {
    		for (int i = this.interceptorIndex; i >= 0; i--) {
    			HandlerInterceptor interceptor = this.interceptorList.get(i);
    			try {
    				interceptor.afterCompletion(request, response, this.handler, ex);
    			}
    			catch (Throwable ex2) {
    				logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
    			}
    		}
    	}
    }
    

    6. 总结:

    1. 实现org.springframework.web.servlet.HandlerInterceptor 接口,共有三个方法可以进行选择性的实现:

      • preHandle( ):处理器方法调用之前执行。只有该方法有返回值,返回值是布尔类型,true 表示放行,false 表示拦截
      • postHandle( ):处理器方法调用之后执行。
      • afterCompletion( ):渲染完成后执行。
    2. 在 Spring MVC 中拦截器的基本配置有两种方式:

      • 第一种方式是:通过 xml 进行配置
      • 第二种方式是:通过 @Component 注解 + xml 文件进行配置
      • 对于这种基本配置来说,拦截器是拦截所有请求的。
    3. 拦截器的高级配置:采用以上基本配置方式,拦截器是拦截所有请求路径的。如果要针对某些路径进行拦截,某些路径不拦截,某些路径拦截,可以采用高级配置:在 spring mvc.xml 文件当中进行配置

    4. Spring MVC中多个拦截器的执行顺序:

      1. 如果所有拦截器 preHandle( ) 方法 都返回 true时,多个拦截器的的执行顺序:

        1. 按照 springmvc.xml文件中配置的顺序,自上而下调用 preHandle:

          在这里插入图片描述

    1. 如果其中一个拦截器 preHandle ( ) 方法,返回 false,多个拦截器的的执行顺序

      1. 只要有一个拦截器preHandle()方法,返回false,则任何拦截器的 postHandle()方法都不执行。但返回 false 的拦截器的前面的拦截器按照逆序执行afterCompletion
    2. 拦截器源码分析。

    7. 最后:

    “在这个最后的篇章中,我要表达我对每一位读者的感激之情。你们的关注和回复是我创作的动力源泉,我从你们身上吸取了无尽的灵感与勇气。我会将你们的鼓励留在心底,继续在其他的领域奋斗。感谢你们,我们总会在某个时刻再次相遇。”

    在这里插入图片描述

  • 相关阅读:
    【手把手】教你玩转SpringCloud Alibaba之Seata
    Coder OSS Enterprise 2.3.3 Crack
    java117-list迭代器和包含方法
    Android 10.0 app全屏系统属性控制上滑是否显示虚拟导航栏和状态栏
    Java —— 抽象类和接口
    外卖项目03---分类管理业务开发
    电力电子转战数字IC20220718-19day51-52——TLM通信
    MySQL的增删查改(第一话)
    深入解析Kotlin类与对象:构造、伴生、单例全面剖析
    Kubernetes:(九)coredns(浪不动了)
  • 原文地址:https://www.cnblogs.com/TheMagicalRainbowSea/p/18303437