Spring5源码解析-Spring Web中的处理程序执行链
Spring的DispatcherServlet假如缺少几个关键元素将无法分派请求。其中最重要的一个是处理程序执行链。
在这篇文章中,我们把注意力放在处理程序执行链之上。老规矩,第一部分将介绍这个概念。第二部分把目光引入到Spring执行链的世界中。在最后一部分,我们将分析如何在Spring中利用之前自定义DispatcherServlet中实现一个自定义的处理程序执行链。
什么是Spring中的处理程序执行链?
Spring中的处理程序执行链是一种由处理程序映射和处理程序拦截器(简单点说就是由谁来处理,处理之前和之后应该干点啥)组成的责任链设计模式。处理器映射器用于将当前请求与其专用的controller进行匹配。拦截器是用来在一些调度动作(如controller解析,视图渲染等)之前和之后所调用的对象。
我们所说的一个处理程序执行链是dispatcher servlet
用来处理接收到的请求的一组元素。需要说的是,所有执行链调用都由dispatcher servlet
类来进行。其实执行链只是一种容器(见源码):
- 定义处理程序映射和拦截器
- 定义在某些时刻应用所应该调度的方法(如处理程序适配器适配之后,
controller
的方法调用之后等等)
HandlerExecutionChain类
处理程序执行链由org.springframework.web.servlet.HandlerExecutionChain类表示。它的主要包含两个私有字段:Object handler 和 HandlerInterceptor[] interceptors,它们被用在请求的调度过程中。第一个包含用于查找处理程序适配器实例的处理程序对象。第二个是包含拦截器的数组,用来应用于处理过的请求(这里这么说是因为这是一条执行链,一个接一个来对这个请求进行处理)。
在DispatcherServlet
类中,HandlerExecutionChain
的查找通过protected HandlerExecutionChain getHandler(HttpServletRequest request)完成。它遍历所有可用的处理程序映射,并返回能够处理请求的第一个处理程序。
在DispatcherServlet
与HandlerExecutionChain
实例中要完成的第二件事是应用拦截器的前后调用。这是由DispatcherServlet的方法,如applyPreHandle
,applyPostHandle
,applyAfterConcurrentHandlingStarted
和triggerAfterCompletion
(关于后两者我会在后面专门写由Java并发编程到线程池到forkjoin到nio到netty这个系列来具体讲述的)。
1 |
|
org.springframework.web.servlet.DispatcherServlet
1 | /** |
自定义处理程序执行链
为了说明处理程序执行链的使用,我们将从关于Spring DispatcherServlet生命周期的文章中拿到我们自定义的dispatcher servlet
类,并向其添加一个自定义的处理程序执行链。但是,无须深挖并使用HandlerExecutionChain
中的所有Spring
的类,我们来创建一个新的对象(DumberHandlerExecutionChain
),添加两个方法来调用拦截器,并在DispatcherServlet
的类中使用它。请看下面编写的代码:
1 | // we start directly by doService method which handles incoming request |
只需要改变3行代码。第一个是DumberHandlerExecutionChain
实例的定义,替换掉HandlerExecutionChain
。第二个更改是applyPreHandler
和applyPostHandler
方法。这段代码这样来看的话好理解吧。我们来看看DumberHandlerExecutionChain类的定义:
1 | public class DumberHandlerExecutionChain extends HandlerExecutionChain { |
它们是前面提到的两种方法:preHandle
和postHandle
。两者很相似。他们首先迭代所有可用的拦截器。区别在于第一个调用拦截器的preHandle
方法和第二个拦截器的postHandle
方法。第二个区别是结果。如果所有拦截器正确完成了操作,preHandle
将返回true。postHandle
不返回任何东西(这里和HandlerExecutionChain
源码内相应的方法实现大致一样,但是做了逻辑上的简单处理达到咱们想要的效果即可)。
但这两种方法并不是这个类的核心。它最重要的地方是调用这个父类的构造函数:
1 | // 1. Invoked directly by super(chain) call |
由上可以看到,通过handler instanceof
检查,我们可以在dispatcher servlet
中本地生成HandlerExecutionChain
。我们不需要查找产生HandlerExecutionChain
实例的处理程序映射(例如:org.springframework.web.servlet.handler.AbstractHandlerMapping
或org.springframework.web.servlet.handler.AbstractUrlHandlerMapping
的实现类)并覆盖重写现有代码。而不是使用这些复杂的步骤,我们只需简单地将HandlerExecutionChain
的实例传递给我们自定义的执行链类的构造函数即可。
完成上面的工作,接下来,你可以在日志中看到以下信息:
1 | Overriden constructor DumberHandlerExecutionChain invoked |
在本文中,我们分析了在Spring 中dispatcher servlet中处理程序执行链的概念。而且已经知道,它不仅包含处理程序映射(在查找处理程序适配器之后用,源码有标注注释),而且在不同的时间点会调用拦截器。接着,我们详细分析了HandlerExecutionChain
类。里面有两个主要的私有字段,一个是一个处理程序,另一个是拦截器数组。在最后一部分,我们通过写一个我们自定义的处理程序执行链,以能够在我们自定义处理程序适配器的操作之前和之后调用拦截器。