1 Star 0 Fork 0

cgrs572/SpringMvcDemo

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README

SpringMVC拦截器(interceptor)

  • 定义

    • SpringMVC拦截器(interceptor)类似于Servlet开发中所使用的过滤器(Filter),用于对处理器进行预处理和后处理
    • 拦截器是AOP思想的具体体现
  • 在实际应用中,会将拦截器(interceptor)按一定顺序联结成一条链,该链被称为拦截器链(Interceptor Chain)

    • 在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用
  • 拦截器(Interceptor)和过滤器(Filter)的区别

    区别 过滤器 拦截器
    适用范围 是servlet规范中的一部分,任何JavaWeb工程都可以使用 是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才能使用
    拦截范围 在url-pattern中配置了/*后,会对所有要访问的资源进行拦截 <mvc:mapping path=""/>中配置了/**之后,也会对所有资源进行拦截,但是可以通过<mvc:exclude-mapping path=""/>标签排除不需要拦截的资源
  • 流程

    ![image-20210805180846313](Z:\aDrive-177\02.java\【黑马程序员】JavaEE就业课 V12.5\04阶段四:热门框架\02第二章:2-SpringMVC\day02-SpringMVC\02-讲义\assets\image-20210805180846313.png)

  • 步骤

    • 创建实现HandlerInterceptor接口的实现类,并重写它的三个方法
    • 在SpringMVC的核心配置文件中配置拦截器
    • 测试拦截器的拦截效果

快速入门

  • 注意:

    • 此处仅弄一个简单环境来测试拦截器,所以准备工作均省略,可详见快速入门以及SpringMVC数据响应部分内容,完整框架如图所示

      <img alt="image-20241127154532718">

    • 以上图中还未配置拦截器,所以运行后前面能接收到响应如图所示,现要求进行拦截器配置

      <img alt="image-20241127161120181">

  • Step1: 创建一个与三层架构包同级的interceptor包并在该包下创建一个实现HandlerInterceptor接口的拦截器类,代码如下

    package at.guigu.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 MyInterceptor implements HandlerInterceptor {
        // 在原始方法(即目标方法)执行之前执行
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("preHandle Running...");
            return false;
        }
    
        // 在原始方法(即目标方法)执行之后,视图对象返回之前执行
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            System.out.println("postHandle Running...");
        }
    
        // 在整个请求完成之后执行(即在原始方法执行完并且视图对象也以返回之后执行)
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            System.out.println("afterCompletion Running...");
        }
    }
    
  • Step2: 在SpringMVC的核心配置文件中配置拦截器

    <!--配置拦截器-->
    <mvc:interceptors>
        <mvc:interceptor>
            <!--定义拦截器的作用范围:此处表示拦截所有请求路径-->
            <mvc:mapping path="/**"/>
            <!--定义拦截器的具体实现类,class属性值为对应实现类的全限定名-->
            <bean class="at.guigu.interceptor.MyInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>
    
    • SpringMVC核心配置文件完整代码如下

      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:context="http://www.springframework.org/schema/context"
             xmlns:mvc="http://www.springframework.org/schema/mvc"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://www.springframework.org/schema/beans
             http://www.springframework.org/schema/beans/spring-beans.xsd
             http://www.springframework.org/schema/context
             http://www.springframework.org/schema/context/spring-context.xsd
             http://www.springframework.org/schema/mvc
             http://www.springframework.org/schema/mvc/spring-mvc.xsd">
          <!--mvc的注解驱动-->
          <mvc:annotation-driven/>
      
          <!--配置Controller层的注解的组件扫描-->
          <context:component-scan base-package="at.guigu.controller"></context:component-scan>
          <!--等同于
          <context:component-scan base-package="at.guigu">
              type指定要扫描的内容为注解,expression指定要扫描的对应注解的全限定名
              只扫描at.guigu包下有@Controller注解的类
              <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
          </context:component-scan>
          -->
          <!--配置内部资源视图解析器-->
          <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
              <!--将InternalResourceViewResolver类中的视图名称前缀属性prefix的值设为/jsp/-->
              <property name="prefix" value="/jsp/"></property>
              <!--将InternalResourceViewResolver类中的视图名称后缀属性suffix的值设为.jsp-->
              <property name="suffix" value=".jsp"></property>
          </bean>
          <!--配置静态资源的路径映射,让 Spring MVC 可以处理静态文件-->
          <mvc:default-servlet-handler/>
          <!--配置拦截器-->
          <mvc:interceptors>
              <mvc:interceptor>
                  <!--定义拦截器的作用范围:此处表示拦截所有请求路径-->
                  <mvc:mapping path="/**"/>
                  <!--定义拦截器的具体实现类,class属性值为对应实现类的全限定名-->
                  <bean class="at.guigu.interceptor.MyInterceptor"/>
              </mvc:interceptor>
          </mvc:interceptors>
      </beans>
      

      运行后截图如下

      <img alt="image-20241127161337767">

  • Step3: 测试拦截器拦截效果

    • 在以上运行截图中,只执行了preHandle方法是因为它的返回值为true表示请求可以交给Controller来执行原始方法;反之则请求被拦截

    • 此时若将其返回值改true,代码如下

      package at.guigu.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 MyInterceptor implements HandlerInterceptor {
          // 在原始方法(即目标方法)执行之前执行
          @Override
          public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
              System.out.println("preHandle Running...");
              return true;
          }
      
          // 在原始方法(即目标方法)执行之后,视图对象返回之前执行
          @Override
          public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
              System.out.println("postHandle Running...");
          }
      
          // 在整个请求完成之后执行(即在原始方法执行完并且视图对象也以返回之后执行)
          @Override
          public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
              System.out.println("afterCompletion Running...");
          }
      }
      

      此时的运行截图如下

      <img alt="image-20241127162013217">

HandlerInterceptor接口详解

  • HandlerInterceptor接口方法

    HandlerInterceptor接口方法 解释
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception 在请求到达Controller之前执行(即在在原始方法(即目标方法)执行之前执行),用于执行预处理逻辑。
    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception 在Controller处理完请求之后,视图渲染之前执行(即在原始方法(目标方法)执行之后,视图对象返回之前执行)
    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception 在整个请求完成之后执行(即在原始方法执行完并且视图对象也以返回之后执行)。注意:即使在Controller或视图渲染过程中抛出异常,该方法也会执行
  • 方法对应参数及返回值详解(相同参数不在重复解释)

    • preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)——前置处理方法
      • HttpServletRequest :请求对象
      • HttpServletResponse :响应对象
      • handler :被调用的处理器对象,本质上是一个方法对象,对反射技术中的Method对象进行了再包装
      • return true :表示请求可以交给Controller来执行原始方法。(当为拦截链环境时,会将请求交给下一个Interceptor的前置处理方法,直到所有前置处理方法均通过后,会交给Controller来执行原始方法)
      • return false :表示请求被拦截,Controller控制器和拦截链上后续的Interceptor都不会继续执行
    • postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView)——后置处理方法
      • ModelAndView :如果处理器执行完成具有返回结果,可以读取到对应数据与页面信息,并进行跳转
      • 调用该方法的前提是前置处理方法的返回值为true
    • afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex)——完成后处理方法
      • Exception : 如果处理器执行过程中出现异常对象,可以针对异常情况进行单独处理
      • 调用该方法的前提是前置处理方法的返回值为true
  • preHandle方法经常用于执行预处理逻辑,比如:

    • 验证用户身份或权限。

    • 检查请求参数是否合法。

    • 记录日志或统计请求次数。

    • 当返回值为false时请求被拦截,后续逻辑(如Controller处理或postHandleafterCompletion方法)不会执行(可详见快速入门)。此时可以直接设置响应,例如返回一个错误状态码或转发或重定向到其他页面。示例如下

      • 在web项目核心目录(即webapp)下的jsp文件夹中创建error.jsp页面,代码如下,用于拦截示例
      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      <html>
          <head>
              <title>Title</title>
          </head>
          <body>
              <h1>Error...</h1>
          </body>
      </html>
      
      • MyInterceptor类的代码更改如下

        package at.guigu.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 MyInterceptor implements HandlerInterceptor {
            // 在原始方法(即目标方法)执行之前执行
            @Override
            public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
                System.out.println("preHandle Running...");
                String str = request.getParameter("param");
                if ("yes".equals(str)) {
                    return true;
                } else {
                    request.getRequestDispatcher("/jsp/error.jsp").forward(request, response);
                    return false;
                }
            }
        
            // 在原始方法(即目标方法)执行之后,视图对象返回之前执行
            @Override
            public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
                System.out.println("postHandle Running...");
            }
        
            // 在整个请求完成之后执行(即在原始方法执行完并且视图对象也以返回之后执行)
            @Override
            public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
                System.out.println("afterCompletion Running...");
            }
        }
        

        当param请求参数值不是yes

        <img alt="image-20241127163734877">

        当param请求参数值是yes

        <img alt="image-20241127163949308">

  • postHandle方法用于对ModelAndView进行修改,例如:

    • 添加全局数据到视图中(如页面标题、用户信息)。

    • 根据Controller的处理结果动态调整视图数据。

    • 注意:如果Controller方法没有返回ModelAndView(如使用@ResponseBodyRestController),则此方法可能不会被调用。

    • MyInterceptor类的代码更改如下

      package at.guigu.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 MyInterceptor implements HandlerInterceptor {
          // 在原始方法(即目标方法)执行之前执行
          @Override
          public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
              System.out.println("preHandle Running...");
              String str = request.getParameter("param");
              if ("yes".equals(str)) {
                  return true;
              } else {
                  request.getRequestDispatcher("/jsp/error.jsp").forward(request, response);
                  return false;
              }
          }
      
          // 在原始方法(即目标方法)执行之后,视图对象返回之前执行
          @Override
          public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
              modelAndView.addObject("username", "李四");
              System.out.println("postHandle Running...");
          }
      
          // 在整个请求完成之后执行(即在原始方法执行完并且视图对象也以返回之后执行)
          @Override
          public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
              System.out.println("afterCompletion Running...");
          }
      }
      

      <img alt="image-20241127164527813">

拦截器配置详解

  • 在快速入门中配置的是单个拦截器,在SpringMVC的核心配置文件中对应的代码如下

    <!--配置拦截器-->
    <mvc:interceptors>
        <mvc:interceptor>
            <!--定义拦截器的作用范围:此处表示拦截所有请求路径-->
            <mvc:mapping path="/**"/>
            <!--定义拦截器的具体实现类,class属性值为对应实现类的全限定名-->
            <bean class="at.guigu.interceptor.MyInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>
    
  • 用到的标签

    标签 解释
    <mvc:interceptors> 配置拦截器
    <mvc:interceptors>内嵌标签 解释
    <mvc:interceptor> 配置单个拦截器
    <mvc:interceptor>内嵌标签 解释
    <mvc:mapping path/> 定义拦截器的作用范围。当path属性为/**时,代表拦截所有请求路径;当为/xxx/**时,代表拦截xxx下的所有资源
    <bean class/> 定义拦截器的具体实现类,class属性值为对应实现类的全限定名

配置多个拦截器(拦截器链)步骤

环境准备等工作可见快速入门,此处以配置两个拦截器为例

  • Step1:interceptor包下创建第二个实现HandlerInterceptor接口的拦截器类,并重写其中的方法,代码如下

    package at.guigu.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 MyInterceptorTwo implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("preHandle Running222...");
            return true;
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            System.out.println("postHandle Running222...");
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            System.out.println("afterCompletion Running222...");
        }
    }
    
  • Step2:

  • 在SpringMVC的核心配置文件中配置拦截器

    <!--配置拦截器-->
    <mvc:interceptors>
        <!--配置第一个拦截器实现类-->
        <mvc:interceptor>
            <!--定义拦截器的作用范围:此处表示拦截所有请求路径-->
            <mvc:mapping path="/**"/>
            <!--定义拦截器的具体实现类,class属性值为对应实现类的全限定名-->
            <bean class="at.guigu.interceptor.MyInterceptor"/>
        </mvc:interceptor>
        <!--配置第二个拦截器实现类-->
        <mvc:interceptor>
            <!--定义拦截器的作用范围:此处表示拦截所有请求路径-->
            <mvc:mapping path="/**"/>
            <!--定义拦截器的具体实现类,class属性值为对应实现类的全限定名-->
            <bean class="at.guigu.interceptor.MyInterceptorTwo"/>
        </mvc:interceptor>
    </mvc:interceptors>
    
    • SpringMVC核心配置文件完整代码如下

      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:context="http://www.springframework.org/schema/context"
             xmlns:mvc="http://www.springframework.org/schema/mvc"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://www.springframework.org/schema/beans
             http://www.springframework.org/schema/beans/spring-beans.xsd
             http://www.springframework.org/schema/context
             http://www.springframework.org/schema/context/spring-context.xsd
             http://www.springframework.org/schema/mvc
             http://www.springframework.org/schema/mvc/spring-mvc.xsd">
          <!--mvc的注解驱动-->
          <mvc:annotation-driven/>
      
          <!--配置Controller层的注解的组件扫描-->
          <context:component-scan base-package="at.guigu.controller"></context:component-scan>
          <!--等同于
          <context:component-scan base-package="at.guigu">
              type指定要扫描的内容为注解,expression指定要扫描的对应注解的全限定名
              只扫描at.guigu包下有@Controller注解的类
              <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
          </context:component-scan>
          -->
          <!--配置内部资源视图解析器-->
          <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
              <!--将InternalResourceViewResolver类中的视图名称前缀属性prefix的值设为/jsp/-->
              <property name="prefix" value="/jsp/"></property>
              <!--将InternalResourceViewResolver类中的视图名称后缀属性suffix的值设为.jsp-->
              <property name="suffix" value=".jsp"></property>
          </bean>
          <!--配置静态资源的路径映射,让 Spring MVC 可以处理静态文件-->
          <mvc:default-servlet-handler/>
          <!--配置拦截器-->
          <mvc:interceptors>
              <!--配置第一个拦截器实现类-->
              <mvc:interceptor>
                  <!--定义拦截器的作用范围:此处表示拦截所有请求路径-->
                  <mvc:mapping path="/**"/>
                  <!--定义拦截器的具体实现类,class属性值为对应实现类的全限定名-->
                  <bean class="at.guigu.interceptor.MyInterceptor"/>
              </mvc:interceptor>
              <!--配置第二个拦截器实现类-->
              <mvc:interceptor>
                  <!--定义拦截器的作用范围:此处表示拦截所有请求路径-->
                  <mvc:mapping path="/**"/>
                  <!--定义拦截器的具体实现类,class属性值为对应实现类的全限定名-->
                  <bean class="at.guigu.interceptor.MyInterceptorTwo"/>
              </mvc:interceptor>
          </mvc:interceptors>
      </beans>
      

      <img alt="image-20241127171146964">

马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/cgrs572/spring-mvc-demo.git
git@gitee.com:cgrs572/spring-mvc-demo.git
cgrs572
spring-mvc-demo
SpringMvcDemo
master

搜索帮助