Spring Mvc拦截器

一、什么是拦截器?

拦截器(Interceptor)是 Spring MVC 提供的一种强大的功能组件。可以对用户请求进行拦截,并在请求进入控制器(Controller)之前、控制器处理完请求后、甚至是渲染视图后,执行一些指定的操作。在 Spring MVC 中,拦截器的作用与 Servlet 中的过滤器类似,它主要用于拦截用户请求并做相应的处理,例如通过拦截器,我们可以执行权限验证、记录请求信息日志、判断用户是否已登录等操作。Spring MVC 拦截器设计的是可插拔式的设计,所以需要某一拦截器,只需在配置文件中启用该拦截器即可;如果不需要这个拦截器,则只要在配置文件中取消应用该拦截器即可。

二、如何定义拦截器

在 Spring MVC 项目中使用拦截器,首先就需要对拦截器类进行定义,Spring MVC 在 org.springframework.web.servlet 包中提供了一个 HandlerInterceptor 接口,该接口包含 3 个方法,如下表。

方法名返回值说明
preHandle ()  boolean 该方法在控制器方法之前执行,其返回值用来表示是否中断后续操作。
  • 返回值为 true 时,表示继续向下执行;
  • 返回值为 false 时,表示中断后续的操作。
postHandle () void 该方法会在控制器方法调用之后,解析视图之前执行。通过此方法对请求域中的模型(Model)数据和视图做出进一步的修改。
afterCompletion () void 该方法会在整个请求完成后,即视图渲染结束之后执行。通过该方法可以实现资源清理、日志记录等操作

通过实现 HandlerInterceptor 接口,重写其方法,来实现对拦截器类的定义,示例代码如下。

package com.augus.interceptor;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("执行了MyInterceptor下的preHandle()");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("执行了MyInterceptor下的postHandle()");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("执行了MyInterceptor下的afterCompletion()");
    }
}

三、配置拦截器

在定义完拦截器后,需要在 Spring MVC 的配置文件中使用 <mvc:interceptors> 标签及其子标签对拦截器进行配置,这样这个拦截器才会生效。
在 Spring MVC 的配置文件中,<mvc:interceptors> 标签用于定义一组拦截器,其包含多个常用的子标签。

标签一级子标签二级子标签说明
<mvc:interceptors> <bean> - <mvc:interceptors> 标签的子标签,用于定义一个全局拦截器,对所有的请求进行拦截。
<ref> - <mvc:interceptors> 标签的子标签,用于定义一个全局拦截器的引用,对所有的请求进行拦截。

<mvc:interceptors> 标签的 <ref> 子标签不能单独使用,它需要与 <bean> 标签(<mvc:interceptors> 标签内或 <mvc:interceptors>标签外)或 @Component 等注解配合使用,以保证<ref> 标签配置的拦截器是 Spring IOC 容器中的一个组件。
<mvc:interceptor>  <mvc:mapping> <mvc:interceptor> 标签用于定义一个指定拦截路径的拦截器。

<mvc:mapping> 标签则是<mvc:interceptor> 的子标签,用来配置拦截器拦截的路径,该路径则是通过其属性“path”中定义的。例如,path 的属性值为“/**”时,表示拦截所有请求;而“/hello”则表示拦截路径为“/hello”的请求。
<mvc:exclude-mapping> <mvc:exclude-mapping> 为<mvc:interceptor> 的子标签,用来配置不需要被拦截器拦截的路径。
<bean> <bean> 为<mvc:interceptor> 的子标签,用来定义一个指定了拦截路径的拦截器。

 如何通过上述标签来对拦截器进行定义。

1. 通过 <bean> 子标签配置全局拦截器

在 SpringMVC.xml 的配置文件中,通过 <mvc:interceptors> 标签及其子标签 <bean> ,将我们自定义的拦截器配置成了一个全局拦截器。该拦截器会对项目内所有的请求进行拦截,配置代码如下。

<!--配置拦截器-->
    <mvc:interceptors>
        <bean class="com.augus.interceptor.MyInterceptor"></bean> 
</mvc:interceptors>

2. 通过 <ref> 子标签配置全局拦截器

可以在 <mvc:interceptors> 标签中通过子标签 <ref> 定义一个全局拦截器引用,对所有的请求进行拦截。

<!--将自定义的拦截器放到 spring ioc 容器中-->
<bean id="interceptor"class="com.augus.interceptor.MyInterceptor"></bean> 
<!--配置拦截器--> 
<mvc:interceptors> 
  <!--通过 ref 配置全局拦截器--> 
  <ref bean="interceptor"></ref> 
</mvc:interceptors>

需要注意的是<mvc:interceptors> 标签的 <ref> 子标签不能单独使用,它需要与 <bean> 标签(<mvc:interceptors> 标签内或<mvc:interceptors>标签外)或 @Component 等注解配合使用,以保证 <ref> 标签配置的拦截器是Spring IOC 容器中的组件。

3. 通过<mvc:interceptor>子标签对拦截路径进行配置

也可以在 Spring MVC 的配置文件中通过 <mvc:interceptors> 标签的子标签 <mvc:interceptor>,对拦截器拦截的请求路径进行配置,示例配置如下。

<!--配置拦截器-->
    <mvc:interceptors>
        <!--拦截器1-->
        <mvc:interceptor>
            <!--配置拦截器拦截的请求路径-->
            <mvc:mapping path="/testHello"/>
            <!--配置连接器不需要请求拦截的请求路径-->
            <mvc:exclude-mapping path="/login"/>
            <mvc:exclude-mapping path="/"/>
            <!--定义在这里,表示拦截器只对指定路径的请求进行拦截-->
            <bean id="interceptor" class="com.augus.interceptor.MyInterceptor"></bean>
        </mvc:interceptor>
</mvc:interceptors>

在 <mvc:interceptor> 中,子元素必须按照上述代码的配置顺序进行编写,即 <mvc:mapping> → <mvc:exclude-mapping> → <bean> 的顺序,否则就会报错。其次,以上这三种配置拦截器的方式,我们可以根据自身的需求以任意的组合方式进行配置,以实现在 <mvc:interceptors> 标签中定义多个拦截器的目的。

四、拦截器的执行流程

拦截器的执行流程如下图所示。

拦截器处理流程的步骤如下:

  1. 当请求的路径与拦截器拦截的路径相匹配时,程序会先执行拦截器类(MyInterceptor)的 preHandle() 方法。若该方法返回值为 true,则继续向下执行 Controller(控制器)中的方法,否则将不再向下执行;
  2. 控制器方法对请求进行处理;
  3. 调用拦截器的 postHandle() 方法,此时我们可以对请求域中的模型(Model)数据和视图做出进一步的修改;
  4. 通过 DispatcherServlet 的 render() 方法对视图进行渲染;
  5. 调用拦截器的 afterCompletion () 方法,完成资源清理、日志记录等工作。

五、案例演示

1.在com.augus下创建 interceptor包存放定义的拦截器,

在interceptor包下创建 MyInterceptor类,代码如下:

package com.augus.interceptor;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("执行了MyInterceptor下的preHandle()");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("执行了MyInterceptor下的postHandle()");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("执行了MyInterceptor下的afterCompletion()");
    }
}

2.在controller包中进行如下操作

下创建InterceptorController类,代码如下:

package com.augus.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class InterceptorController {

    @RequestMapping("/testHello")
    public String testHello(){
        System.out.println("这是InterceptorController下的testHello");
        return "success";
    }
}

3.在 Spring MVC 的配置文件: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.augus.controller"/>

    <mvc:annotation-driven/>

    <!-- 配置 Thymeleaf 视图解析器 -->
    <!--配置视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--配置前置解析器-->
        <property name="prefix" value="/WEB-INF/templates/"/>
        <!--配置后置解析器-->
        <property name="suffix" value=".jsp"/>
    </bean>

    <!--配置静态资源放行-->
    <!--<mvc:resources mapping="/js/**" location="/WEB-INF/js/"/>-->
    <mvc:resources mapping="/upload/**" location="/upload/"/>

    <!--文件上传组件
    id属性的值必须为multipartResolver
    -->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!--设置上传文件的默认编码格式-->
        <property name="defaultEncoding" value="UTF-8"/>
        <!--在这里控制上传文件的大小  不推荐
        这时候没办法给前端返回提示信息,推荐在controller中配置-->
        <!--<property name="maxUploadSize" value="10240000000"></property>-->
    </bean>

    <!--配置拦截器-->
    <mvc:interceptors>
        <!--配置拦截器1-->
        <mvc:interceptor>
            <!--配置拦截器的请求路径-->
            <mvc:mapping path="/testHello"/>
            <!--配置连接器不需要请求拦截的请求路径-->
            <mvc:exclude-mapping path="/login"/>
            <mvc:exclude-mapping path="/"/>
            <!--配置使用那个类进行拦截-->
            <bean id="interceptor" class="com.augus.interceptor.MyInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>
</beans>

4. 在 webapp/WEB-INF 目录下,新建一个 templates 目录,并在该目录下新建一个 success.html,代码如下。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>访问成功!!!</h1>
</body>
</html>

5. 部署到 Tomact 中并启动,启动项目

使用浏览器访问http://localhost:8080/ssm02_war_exploded/testHello,结果如下图

六、多个拦截器时的执行流程

实例项目中通常都不会只有一个拦截器,会定义许多不同的拦截器来实现不同的功能。在程序运行期间,拦截器的执行是有一定的顺序的,该顺序与拦截器在配置文件中定义的顺序有关。假设某个项目中包含两个不同的拦截器:Interceptor1 和 Interceptor2,它们在配置文件中定义的顺序为:Interceptor1 → Interceptor2。多个拦截器的执行流程如下图:

如上图当存在多个拦截器同时工作时,它们的 preHandle() 方法会按照拦截器在配置文件中的配置顺序执行,但它们的 PostHandle() 和 afterCompletion() 方法则会按照配置顺序的反序执行。
当其中的一个preHandle方法返回为false后,按配置顺序,后面的preHandle方法都不会运行了,而控制器和所有的后置方法postHandle也不会再运行。执行过preHandle方法且该方法返回为true的拦截器的完成方法(afterCompletion)会按照配置的逆序运行。 

1.在interceptor 包中,除了示例 1 中创建的 MyInterceptor 外,再创建两个拦截器类:MyInterceptor2 和 MyInterceptor3。

package com.augus.interceptor;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class MyInterceptor2 implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("执行了MyInterceptor2下的preHandle()");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("执行了MyInterceptor2下的postHandle()");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("执行了MyInterceptor2下的afterCompletion()");
    }
}
package com.augus.interceptor;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class MyInterceptor3 implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("执行了MyInterceptor3下的preHandle()");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("执行了MyInterceptor3下的postHandle()");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("执行了MyInterceptor3下的afterCompletion()");
    }
}

2. 修改 Spring MVC 配置文件 springMVC.xml 的内容,在 <mvc:interceptors> 标签中增加 MyInterceptor2 和 MyInterceptor3 的配置,代码如下。

<?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.augus.controller"/>

    <mvc:annotation-driven/>

    <!-- 配置 Thymeleaf 视图解析器 -->
    <!--配置视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--配置前置解析器-->
        <property name="prefix" value="/WEB-INF/templates/"/>
        <!--配置后置解析器-->
        <property name="suffix" value=".jsp"/>
    </bean>

    <!--配置静态资源放行-->
    <!--<mvc:resources mapping="/js/**" location="/WEB-INF/js/"/>-->
    <mvc:resources mapping="/upload/**" location="/upload/"/>

    <!--文件上传组件
    id属性的值必须为multipartResolver
    -->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!--设置上传文件的默认编码格式-->
        <property name="defaultEncoding" value="UTF-8"/>
        <!--在这里控制上传文件的大小  不推荐
        这时候没办法给前端返回提示信息,推荐在controller中配置-->
        <!--<property name="maxUploadSize" value="10240000000"></property>-->
    </bean>

    <!--配置拦截器-->
    <mvc:interceptors>
        <!--拦截器1-->
        <mvc:interceptor>
            <!--配置拦截器拦截的请求路径-->
            <mvc:mapping path="/testHello"/>
            <!--配置连接器不需要请求拦截的请求路径-->
            <mvc:exclude-mapping path="/login"/>
            <mvc:exclude-mapping path="/"/>
            <!--定义在这里,表示拦截器只对指定路径的请求进行拦截-->
            <bean id="interceptor" class="com.augus.interceptor.MyInterceptor"></bean>
        </mvc:interceptor>

        <!--连接器2-->
        <mvc:interceptor>
            <!--配置连接器的请求路径-->
            <mvc:mapping path="/**"/>
            <!--配置连接器不需要请求拦截的请求路径-->
            <mvc:exclude-mapping path="/login"/>
            <mvc:exclude-mapping path="/"/>
            <!--定义在这里,表示拦截器只对指定路径的请求进行拦截-->
            <bean class="com.augus.interceptor.MyInterceptor2"></bean>
        </mvc:interceptor>

        <!--连接器3-->
        <mvc:interceptor>
            <!--配置连接器的请求路径-->
            <mvc:mapping path="/**"/>
            <!--配置连接器不需要请求拦截的请求路径-->
            <mvc:exclude-mapping path="/login"/>
            <mvc:exclude-mapping path="/"/>
            <!--定义在这里,表示拦截器只对指定路径的请求进行拦截-->
            <bean class="com.augus.interceptor.MyInterceptor3"></bean>
        </mvc:interceptor>

    </mvc:interceptors>
    
</beans>

3. 重启 Tomcat 服务器

并使用浏览器访问 http://localhost:8080/ssm02_war_exploded/testHello,控制台输出如下。

从上面执行结果可以得出:由于这三个拦截器在 Spring MVC 的配置文件中是按照 MyInterceptor → MyInterceptor2 → MyInterceptor3 的顺序配置的,因此这三个拦截器的执行顺序也是 MyInterceptor → MyInterceptor2 → MyInterceptor3;

posted @ 2019-10-28 10:43  酒剑仙*  阅读(627)  评论(0编辑  收藏  举报