上一讲中说了《Tomcat的过滤器链 是如何实现的》,今天来看下 Spring的拦截器链是如何实现的。
从位置上来说 过滤器是属于Servlet容器级别的规范,拦截器是Spring自身的东西,一个请求 进入Tomcat,必先经过过滤器链,再调用Servlet方法,才能到达Spring,Spring MVC必须也得遵守Servlet规范才能被Tomcat调到。
在原生面向Servlet编程中(最早期通过Web.xml配置,后来使用Servlet注解配置),每个Servlet都是有一个url映射的,这样每个请求都会分配到指定的Servlet上进行处理。
而在Spring MVC 中 这种方式被打破,实际上接收Tomcat请求的只有一个Servlet,就是org.springframework.web.servlet.DispatcherServlet。
这个Servlet干的事情是进一步分发请求到我们编写的 Controller类(由此知道SpringMVC中的Controller类和Servlet完全没有关系,它就是普通的类 ,url映射等各种注解,都是Spring框架自身的东西,是为了DispatcherServlet分发时有据可依),那么在到达Controller之前和处理业务之后就可以做很多事情了,这些事情就由Spring自由发挥了,其中拦截器就是Spring的杰作之一。
使用Spring MVC 写个拦截器的demo,打个断点,观察下调用链
下面重点看下DispatcherServlet是如何与 HandlerExecutionChain配合完成拦截器的功能的
//DispatcherServlet中关键方法
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
//出现了 HandlerExecutionChain
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
// HandlerExecutionChain的初始化
//(根据配置,每个请求需要经历的过滤器可以不同,里面还包括了对应的Controller信息)
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
//执行过滤器前置方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
//业务处理
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
//执行过滤器后置方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
关键地方
看下HandlerExecutionChain的实现(只抽取了关键方法)
public class HandlerExecutionChain {
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
//通过for循环调用所有的拦截器
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
}
return true;
}
/**
* Apply postHandle methods of registered interceptors.
*/
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
//通过for循环调用所有的拦截器,注意是倒序执行的
for (int i = interceptors.length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(request, response, this.handler, mv);
}
}
}
/**
* Trigger afterCompletion callbacks on the mapped HandlerInterceptors.
* Will just invoke afterCompletion for all interceptors whose preHandle invocation
* has successfully completed and returned true.
*/
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)
throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
//注意是倒序执行,并且只执行 preHandler成功的拦截器,interceptorIndex变量的作用体现出来了
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
try {
interceptor.afterCompletion(request, response, this.handler, ex);
}
catch (Throwable ex2) {
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}
}
}
看完后发现只是循环调用了一下所有拦截器的方法(注意正序和倒序问题),下面老吕把这种实现方式 抽取出来 写个demo运行一下。
代码目录:
/**
* @Project fighting-core
* @Description 请求分发器
* @Author lvaolin
* @Date 2021/12/26 下午2:25
*/
public class DispatcherServlet {
public void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerExecutionChain handlerExecutionChain = getHandlerExecutionChain();
//应用前置处理器
if (!handlerExecutionChain.applyPreHandle(request, response)) {
return;
}
//处理业务
String helloResponse = handlerExecutionChain.getHandler().hello(request.getName());
//应用后置处理器
handlerExecutionChain.applyPostHandle(request, response, null);
System.out.println("响应到前端:'"+helloResponse+"'");
handlerExecutionChain.triggerAfterCompletion(request,response,null);
}
private HandlerExecutionChain getHandlerExecutionChain() {
HandlerExecutionChain handlerExecutionChain = new HandlerExecutionChain();
handlerExecutionChain.setHandler(new MyController());
handlerExecutionChain.addInterceptor(new MyInterceptor1());
handlerExecutionChain.addInterceptor(new MyInterceptor2());
handlerExecutionChain.addInterceptor(new MyInterceptor3());
return handlerExecutionChain;
}
}
/**
* @Project fighting-core
* @Description 拦截器链
* @Author lvaolin
* @Date 2021/12/26 下午2:25
*/
@Data
public class HandlerExecutionChain {
//目标 Controller
private MyController handler;
/**
* 拦截器集合
*/
private HandlerInterceptor[] interceptors;
/**
* 拦截器集合 与 interceptors 相互转换
*/
private List<HandlerInterceptor> interceptorList;
//用来记录preHandler成功的拦截器个数的,在triggerAfterCompletion 方法中将要用到
//因为triggerAfterCompletion只会触发preHandler执行成功的拦截器
private int interceptorIndex = -1;
public HandlerExecutionChain() {
}
public HandlerExecutionChain(MyController handler, HandlerInterceptor... interceptors) {
this.handler = handler;
this.interceptors = interceptors;
}
public MyController getHandler() {
return this.handler;
}
public void addInterceptor(HandlerInterceptor interceptor) {
initInterceptorList().add(interceptor);
}
public void addInterceptor(int index, HandlerInterceptor interceptor) {
initInterceptorList().add(index, interceptor);
}
public void addInterceptors(HandlerInterceptor... interceptors) {
if (!ObjectUtils.isEmpty(interceptors)) {
CollectionUtils.mergeArrayIntoCollection(interceptors, initInterceptorList());
}
}
private List<HandlerInterceptor> initInterceptorList() {
if (this.interceptorList == null) {
this.interceptorList = new ArrayList<>();
if (this.interceptors != null) {
// An interceptor array specified through the constructor
CollectionUtils.mergeArrayIntoCollection(this.interceptors, this.interceptorList);
}
}
this.interceptors = null;
return this.interceptorList;
}
/**
* Return the array of interceptors to apply (in the given order).
* @return the array of HandlerInterceptors instances (may be {@code null})
*/
public HandlerInterceptor[] getInterceptors() {
if (this.interceptors == null && this.interceptorList != null) {
this.interceptors = this.interceptorList.toArray(new HandlerInterceptor[0]);
}
return this.interceptors;
}
/**
* 执行过滤器集合的preHandle方法
* Apply preHandle methods of registered interceptors.
* @return {@code true} if the execution chain should proceed with the
* next interceptor or the handler itself. Else, DispatcherServlet assumes
* that this interceptor has already dealt with the response itself.
*/
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
//正序执行
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
//记录preHandler成功的最大下标
this.interceptorIndex = i;
}
}
return true;
}
/**
* 执行过滤器集合的postHandle方法(倒序执行)
* Apply postHandle methods of registered interceptors.
*/
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv)
throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
//注意这个拦截器执行的先后顺序是和preHandle相反的,倒序执行
for (int i = interceptors.length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(request, response, this.handler, mv);
}
}
}
/**
* 执行过滤器集合的afterCompletion方法(倒序执行)
* Trigger afterCompletion callbacks on the mapped HandlerInterceptors.
* Will just invoke afterCompletion for all interceptors whose preHandle invocation
* has successfully completed and returned true.
*/
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)
throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
//注意这个拦截器执行的先后顺序是和preHandle相反的,并且只执行 preHandle成功的过滤器
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
try {
interceptor.afterCompletion(request, response, this.handler, ex);
}
catch (Throwable ex2) {
ex2.printStackTrace();
}
}
}
}
}
/**
* @Project fighting-core
* @Description 模拟Spring Controller
* @Author lvaolin
* @Date 2021/12/26 下午2:25
*/
public class MyController {
@RequestMapping("/hello")
String hello(String name){
System.out.println("-业务处理开始-");
System.out.println("-业务处理结束-");
return "hello "+name;
}
}
/**
* @Project fighting-core
* @Description 我的拦截器
* @Author lvaolin
* @Date 2021/12/26 下午2:25
*/
public class MyInterceptor1 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle分布式日志追踪");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle分布式日志追踪后置处理");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion分布式日志追踪结束后处理");
}
}
public class MyInterceptor2 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if ("大黄鸭".equals(request.getName())) {
System.out.println("preHandle登录鉴权成功:"+request.getName());
return true;
}else{
System.out.println("preHandle登录鉴权失败:"+request.getName());
return false;
}
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle登录鉴权后置处理");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion登录鉴权结束后处理");
}
}
public class MyInterceptor3 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle购买验证");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle购买验证后置处理");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion购买验证结束后处理");
}
}
/**
* @Project fighting-core
* @Description 上下文模拟
* @Author lvaolin
* @Date 2021/12/26 下午2:28
*/
public class Main {
public static void main(String[] args) {
HttpServletRequest httpServletRequest = new HttpServletRequest();
HttpServletResponse httpServletResponse = new HttpServletResponse();
DispatcherServlet dispatcherServlet = new DispatcherServlet();
//拦截验证成功的例子
try {
httpServletRequest.setName("大黄鸭");
dispatcherServlet.doDispatch(httpServletRequest,httpServletResponse);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("--------------------------------------------");
//拦截验证失败的例子
try {
httpServletRequest.setName("派大星");
dispatcherServlet.doDispatch(httpServletRequest,httpServletResponse);
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行效果
可以看到拦截器模式实现起来比较简单,但是相当实用,我们平时开发中可以多考虑下这个模式。
代码参考老吕的github:
https://github.com/lvaolin/fighting-base/tree/main/src/main/java/com/dhy/designpatterns/chainOfResponsibility/interceptor