1 前言
昨天新接了一个需要,“拦截 XXX,然后 OOO”,好吧,说白了就是要用拦截器干点事(实现一个具体的功能)。之前,也在网络上搜了很多关于Interceptor
的文章,但感觉内容都大同小异,而且知识点零零散散,不太方便阅读。因此,正好借此机会,整理一篇关于拦截器的文章,在此分享给大家,以供大家参考阅读。
2 拦截器
2.1 概念
Java 里的拦截器是动态拦截 action 调用的对象。它提供了一种机制可以使开发者可以定义在一个 action 执行的前后执行的代码,也可以在一个 action 执行前阻止其执行,同时也提供了一种可以提取 action 中可重用部分的方式。在AOP(Aspect-Oriented Programming,面向切面编程)中拦截器用于在某个方法或字段被访问之前进行拦截,然后在之前或之后加入某些操作。
2.2 原理
拦截器 Interceptor 的拦截功能是基于 Java 的动态代理来实现的,具体可以参考博文“ 用 Java 实现拦截器 Interceptor 的拦截功能 ”,也可以通过阅读 Spring 源代码来了解更为权威的实现细节。
3 实现方法
在 Spring 框架之中,咱们要想实现拦截器的功能,主要通过两种途径,第一种是实现HandlerInterceptor
接口,第二种是实现WebRequestInterceptor
接口。接下来,咱们分别详细的介绍两者的实现方法。
3.1 HandlerInterceptor 接口
在HandlerInterceptor
接口中,定义了 3 个方法,分别为preHandle()
、postHandle()
和afterCompletion()
,咱们就是通过复写这 3 个方法来对用户的请求进行拦截处理的。因此,咱们可以通过直接实现HandlerInterceptor
接口来实现拦截器的功能。不过在 Spring 框架之中,其还提供了另外一个接口和一个抽象类,实现了对HandlerInterceptor
接口的功能扩展,分别为:AsyncHandlerInterceptor
和HandlerInterceptorAdapter
.
对于AsyncHandlerInterceptor
接口,其在继承HandlerInterceptor
接口的同时,又声明了一个新的方法afterConcurrentHandlingStarted()
;而HandlerInterceptorAdapter
抽象类,则是更进一步,在其继承AsyncHandlerInterceptor
接口的同时,又复写了preHandle
方法。因此,AsyncHandlerInterceptor
更像是一个过渡的接口。
在实际应用中,咱们一般都是通过实现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 渲染了对应的视图之后执行,这个方法的主要作用是用于进行资源清理的工作。
接下来,咱们在看看以上接口和抽象类的具体代码:
HandlerInterceptor
接口:
package org.springframework.web.servlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public interface HandlerInterceptor {
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception;
void postHandle(
HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception;
void afterCompletion(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception;
}
AsyncHandlerInterceptor
接口:
package org.springframework.web.servlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public interface AsyncHandlerInterceptor extends HandlerInterceptor {
void afterConcurrentHandlingStarted(
HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception;
}
HandlerInterceptorAdapter
抽象类:
package org.springframework.web.servlet.handler;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.AsyncHandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
/**
* Abstract adapter class for the HandlerInterceptor interface,
* for simplified implementation of pre-only/post-only interceptors.
*
* @author Juergen Hoeller
* @since 05.12.2003
*/
public abstract class HandlerInterceptorAdapter implements AsyncHandlerInterceptor {
/**
* This implementation always returns {@code true}.
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
/**
* This implementation is empty.
*/
public void postHandle(
HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception {
}
/**
* This implementation is empty.
*/
public void afterCompletion(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
/**
* This implementation is empty.
*/
public void afterConcurrentHandlingStarted(
HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
}
}
如上面的代码所示,其实在HandlerInterceptor
和AsyncHandlerInterceptor
中还有很多的代码注释,只是博主感觉太多了,就将其全部删除啦!如果大家对这些注释感兴趣的话,可以自行查看源代码。下面,咱们以继承HandlerInterceptorAdapter
抽象类为例进行演示:
package com.hit.interceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
impo