Spring MVC 中整合拦截器Interceptor

版权声明:本博客所有内容采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可 https://blog.csdn.net/Soinice/article/details/83342954

目录

Interceptor 简介

Interceptor 实现方法

HandlerInterceptor 接口

HandlerInterceptor 接口

AsyncHandlerInterceptor 接口

HandlerInterceptorAdapter 抽象类

WebRequestInterceptor 接口

WebRequestInterceptor 接口

WebRequestInterceptorAdapter 抽象类

​​AsyncWebRequestInterceptor 接口

AbstractInterceptor 抽象类

配置拦截器


Interceptor 简介

关于Interceptor的一些简介以及概念,可以直接访问上一篇文章:https://blog.csdn.net/Soinice/article/details/83341457

Interceptor 实现方法

在 Spring MVC中,咱们要想实现拦截器的功能,主要通过两种途径,

第一种是实现HandlerInterceptor接口,

第二种是实现WebRequestInterceptor接口。

接下来,咱们分别详细的介绍两者的实现方法。

HandlerInterceptor 接口

HandlerInterceptor 接口

在HandlerInterceptor接口中,定义了 3 个方法,分别为preHandle()、postHandle()和afterCompletion(),咱们就是通过复写这 3 个方法来对用户的请求进行拦截处理的。因此,咱们可以通过直接实现HandlerInterceptor接口来实现拦截器的功能。

在实际应用中,咱们一般都是通过实现HandlerInterceptor接口或者继承HandlerInterceptorAdapter抽象类,复写preHandle()、postHandle()和afterCompletion()这 3 个方法来对用户的请求进行拦截处理的。

下面,咱们就详细介绍这个 3 个方法。

  • preHandle(HttpServletRequest request, HttpServletResponse response, Object handle)方法,该方法在请求处理之前进行调用。SpringMVC 中的 Interceptor 是链式调用的,在一个应用中或者说是在一个请求中可以同时存在多个 Interceptor 。每个 Interceptor 的调用会依据它的声明顺序依次执行,而且最先执行的都是 Interceptor 中的 preHandle 方法,所以可以在这个方法中进行一些前置初始化操作或者是对当前请求做一个预处理,也可以在这个方法中进行一些判断来决定请求是否要继续进行下去。该方法的返回值是布尔值 Boolean 类型的,当它返回为 false 时,表示请求结束,后续的 Interceptor 和 Controller 都不会再执行;当返回值为 true 时,就会继续调用下一个 Interceptor 的 preHandle 方法,如果已经是最后一个 Interceptor 的时候,就会是调用当前请求的 Controller 中的方法。
  • postHandle(HttpServletRequest request, HttpServletResponse response, Object handle, ModelAndView modelAndView)方法,通过 preHandle 方法的解释咱们知道这个方法包括后面要说到的 afterCompletion 方法都只能在当前所属的 Interceptor 的 preHandle 方法的返回值为 true 的时候,才能被调用。postHandle 方法在当前请求进行处理之后,也就是在 Controller 中的方法调用之后执行,但是它会在 DispatcherServlet 进行视图返回渲染之前被调用,所以咱们可以在这个方法中对 Controller 处理之后的 ModelAndView 对象进行操作。postHandle 方法被调用的方向跟 preHandle 是相反的,也就是说,先声明的 Interceptor 的 postHandle 方法反而会后执行。这和 Struts2 里面的 Interceptor 的执行过程有点类型,Struts2 里面的 Interceptor 的执行过程也是链式的,只是在 Struts2 里面需要手动调用 ActionInvocation 的 invoke 方法来触发对下一个 Interceptor 或者是 action 的调用,然后每一个 Interceptor 中在 invoke 方法调用之前的内容都是按照声明顺序执行的,而 invoke 方法之后的内容就是反向的。
  • afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex)方法,也是需要当前对应的 Interceptor 的 preHandle 方法的返回值为 true 时才会执行。因此,该方法将在整个请求结束之后,也就是在 DispatcherServlet 渲染了对应的视图之后执行,这个方法的主要作用是用于进行资源清理的工作。

不过在 Spring 框架之中,其还提供了另外一个接口和一个抽象类,实现了对HandlerInterceptor接口的功能扩展,分别为:AsyncHandlerInterceptor接口和HandlerInterceptorAdapter抽象类.

AsyncHandlerInterceptor 接口

对于AsyncHandlerInterceptor接口,其在继承HandlerInterceptor接口的同时,又声明了一个新的方法afterConcurrentHandlingStarted()

HandlerInterceptorAdapter 抽象类

HandlerInterceptorAdapter抽象类,则是更进一步,在其继承AsyncHandlerInterceptor接口的同时,又复写了preHandle方法。因此,AsyncHandlerInterceptor更像是一个过渡的接口。

WebRequestInterceptor 接口

WebRequestInterceptor 接口

在WebRequestInterceptor接口中也定义了 3 个方法,同HandlerInterceptor接口完全相同,咱们也是通过复写这 3 个方法来用户的请求进行拦截处理的。而且这 3 个方法都传递了同一个参数 WebRequest,那么这个 WebRequest 到底是什么呢?其实这个 WebRequest 是 Spring 中定义的一个接口,它里面的方法定义跟 HttpServletRequest 类似,在WebRequestInterceptor中对 WebRequest 进行的所有操作都将同步到 HttpServletRequest 中,然后在当前请求中依次传递。

接下来,咱们主要讲一下WebRequestInterceptor接口的 3 个函数:

  • preHandle(WebRequest request)方法,该方法在请求处理之前进行调用,也就是说,其会在 Controller 中的方法调用之前被调用。这个方法跟 HandlerInterceptor 中的 preHandle 不同,主要区别在于该方法的返回值是void 类型的,也就是没有返回值,因此我们主要用它来进行资源的准备工作,比如我们在使用 Hibernate 的时候,可以在这个方法中准备一个 Hibernate 的Session 对象,然后利用 WebRequest 的 setAttribute(name, value, scope) 把它放到 WebRequest 的属性中。在这里,进一步说说 setAttribute 方法的第三个参数 scope ,该参数是一个Integer 类型的。在 WebRequest 的父层接口 RequestAttributes 中对它定义了三个常量,分别为: 
    • SCOPE_REQUEST ,它的值是 0,表示只有在 request 中可以访问。
    • SCOPE_SESSION,它的值是1,如果环境允许的话,它表示的是一个局部的隔离的 session,否则就代表普通的 session,并且在该 session 范围内可以访问。
    • SCOPE_GLOBAL_SESSION,它的值是 2,如果环境允许的话,它表示的是一个全局共享的 session,否则就代表普通的 session,并且在该 session 范围内可以访问。
  • postHandle(WebRequest request, ModelMap model)方法,该方法在请求处理之后,也就是在 Controller 中的方法调用之后被调用,但是会在视图返回被渲染之前被调用,所以可以在这个方法里面通过改变数据模型 ModelMap 来改变数据的展示。该方法有两个参数,WebRequest 对象是用于传递整个请求数据的,比如在 preHandle 中准备的数据都可以通过 WebRequest 来传递和访问;ModelMap 就是 Controller 处理之后返回的 Model 对象,咱们可以通过改变它的属性来改变返回的 Model 模型。
  • afterCompletion(WebRequest request, Exception ex)方法,该方法会在整个请求处理完成,也就是在视图返回并被渲染之后执行。因此可以在该方法中进行资源的释放操作。而 WebRequest 参数就可以把咱们在 preHandle 中准备的资源传递到这里进行释放。Exception 参数表示的是当前请求的异常对象,如果在 Controller 中抛出的异常已经被 Spring 的异常处理器给处理了的话,那么这个异常对象就是是 null

接下来,咱们在看看以上接口和抽象类的具体代码:

WebRequestInterceptorAdapter 抽象类

在 Spring 框架之中,还提供了一个和WebRequestInterceptor接口长的很像的抽象类,那就是:WebRequestInterceptorAdapter,其实现了AsyncHandlerInterceptor接口,并在内部调用了WebRequestInterceptor接口。

AsyncWebRequestInterceptor 接口

AbstractInterceptor 抽象类

除了上面所讲的内容,咱们还可以通过继承 Struts2 框架提供的AbstractInterceptor抽象类来实现拦截的功能。如果咱们在深入一点研究,会发现AbstractInterceptor实现了Interceptor接口,而Interceptor接口又继承了Serializable接口。

可以点击链接查看上一篇文章:https://blog.csdn.net/Soinice/article/details/83341457

配置拦截器

在前面,咱们用了很大篇幅的内容讲述了拦截器如何实现,因此,我相信大家实现拦截器已经没有问题啦!接下来,咱们在看看,如何在 XML 文件中配置拦截器,使咱们的拦截器生效。

在配置拦截器之前,有 4 个名称的概念需要大家先了解一下,分别为:Join Point、Pointcut、Advice和Advisor.

  • Join Point,表示“连接点”,它是程序运行中的某个阶段点,比如方法的调用、异常的抛出等;
  • Advice,表示“通知”,它是某个连接点所采用的处理逻辑,也就是向连接点注入的代码;
  • Pointcut,表示“切入点”,它是“连接点”的集合,是程序中需要注入 Advice 的位置的集合,指明 Advice 要在什么样的条件下才能被触发;
  • Advisor,它是 Pointcut 和 Advice 的配置器,包括 Pointcut 和 Advice,是将 Advice 注入程序中 Pointcut 位置的代码。

接下来,给出 XML 配置文件的声明:

<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-3.0.xsd  
     http://www.springframework.org/schema/context  
     http://www.springframework.org/schema/context/spring-context-3.0.xsd  
     http://www.springframework.org/schema/mvc  
     http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">  

在 XML 文件的头部声明完之后,咱们就可以在 Spring 的配置文件中就可以使用mvc标签啦!而在mvc标签中有一个名为mvc:interceptors的标签,该标签就是用于声明 Spring 拦截器的。下面,给出一个配置示例:

<mvc:interceptors>  
    <!-- 使用 bean 定义一个 Interceptor,直接定义在 mvc:interceptors 下面的 Interceptor 将拦截所有的请求 -->  
    <bean class="com.imenger.interceptor.WrongCodeInterceptor"/>  
    <mvc:interceptor>  
        <mvc:mapping path="/demo/hello.do"/>  
        <!-- 定义在 mvc:interceptor 下面的 Interceptor,表示对特定的请求进行拦截 -->  
        <bean class="com.imenger.interceptor.LoginInterceptor"/>  
    </mvc:interceptor>  
</mvc:interceptors>  

在 Spring 的XML 配置文件中,咱们可以通过mvc:interceptors标签声明一系列的拦截器,例如:

<mvc:interceptors>
        <bean class="com.hit.interceptor.ContextInterceptor"/>
        <bean class="com.hit.interceptor.LoginInterceptor"/>
        <bean class="com.hit.interceptor.WrongCodeInterceptor"/>
</mvc:interceptors>

如上所示,这些拦截器就够成了一个拦截器链,或者称之为拦截器栈。而这些拦截器的执行顺序是按声明的先后顺序执行的,即:先声明的拦截器先执行,后声明的拦截器后执行。在mvc:interceptors标签下声明interceptor标签主要有两种方式:

  • 直接定义一个 Interceptor 实现类的 bean 对象,使用这种方式声明的 Interceptor 拦截器将会对所有的请求进行拦截;
  • 使用mvc:interceptor标签进行声明,使用这种方式进行声明的 Interceptor 可以通过mvc:mapping子标签来定义需要进行拦截的请求路径。

此外,由于拦截器是 AOP 编程思想的典型应用,也就意味着咱们可以“切”到具体的“面”进行某些操作。例如

<bean id="WrongCodeInterceptor" class="com.imenger.interceptor.WrongCodeInterceptor">
        <property name="userName" value="user-module"></property>
</bean>

<bean id="loginInterceptor" class="com.imenger.interceptor.LoginInterceptor">
    <property name="excludePackages">
       <list>
          <value>com.hit.user.exception</value>
          <value>com.hit.order.exception</value>
       </list>
    </property>
</bean>

<aop:config>
    <aop:advisor advice-ref="WrongCodeInterceptor" pointcut="execution(* com.hit.*.demo..*.*(..)) || execution(* com.imenger.*.demo..*.*(..)) " />
    <aop:advisor advice-ref="loginInterceptor" pointcut="execution(* com.imenger.*.demo..*.*(..))" />
</aop:config>

如上所示,咱们实现了切入到“面”进行特定的拦截功能,其中pointcut表示“切入点”,advisor表示要注入到pointcut的代码。大家可能会对pointcut中的*符合有所疑惑,它是“通配符”,表示可以匹配该位置上的任何名称。当然,如果咱们要想使用aop标签,就得先在配置文件中就得进行声明啦!

猜你喜欢

转载自blog.csdn.net/Soinice/article/details/83342954