DispatcherServlet前端控制器源码解析

100 阅读7分钟

主要剖析DispatcherServlet(前端控制器 )初始化的过程,还有DispatcherServlet执行主流程

一、 前端控制器初始化

DispatcherServlet初始化做了两件事情: 1.获得了一个 SpringMVC 的 ApplicationContext容器 2.注册了 SpringMVC的 九大组件

image.png

1.1 初始化SpringMVC容器

前端控制器DispatcherServlet是SpringMVC的入口,也是SpringMVC的大脑,主流程的工作都是在此完成的,DispatcherServlet 本质是个Servlet,当配置了 load-on-startup 时,会在服务器启动时就执行创建和执行初始化init方法,每次请求都会执行service方法

1、找一下init方法,发现DispatcherServlet类中没有,就去找父类(FrameworkServlet类),FrameworkServlet类的父类(HttpServletBean类),直到HttpServletBean类中有一个init方法 2、找到init方法,看到调用了一个initServletBean方法,点进去看看

image.png

3、initServletBean方法如下所示,发现啥也没有,说明是子类FrameworkServlet类实现的

image.png

4、在FrameworkServlet类中找到initServletBean方法,然后发现有下面一条语句,获取web环境下的Spring容器

image.png

5、点进去看看,发现创建了一个Spring的容器

image.png

还是此方法,再往下看,把Spring容器的引用作为参数放入了下面的方法中

传入Spring容器有什么作用?

将Spring容器的引用设置为SpringMVC的一个属性,通过这个地方体现出一个父子容器的关系

image.png

image.png

父子容器有什么作用?

当SpringMVC在获取Bean的时候,首先会从自己的容器中获取,如果自己容器没有的话会使用parent找到父容器,也就是Spring容器,再从里面看看有没有对应的Bean

Spring中能不能获取到SpringMVC中的Bean?

不能,因为SpringMVC是子容器

image.png

1.2 注册了 SpringMVC的 九大组件 没太屡明白141-SpringMVC框架-组件原理剖析-前端控制器初始化-注册九大组件_哔哩哔哩_bilibili

当我们把SpringMVC容器创建出来后,会执行到下面标红的语句

configureAndRefreshWebApplicationContext,配置和刷新SpringMVC容器

image.png

继续往下走,如果没有的话就进行创建

image.png

点进上图中的方法,一直点到下图

发现也有configureAndRefreshWebApplicationContext方法的调用

image.png

经过上面两个过程,不管是新创建的SpringMVC容器还是只有就有的SpringMVC容器,都会执行configureAndRefreshWebApplicationContext方法

点进方法configureAndRefreshWebApplicationContext

发现此方法中有一个refresh方法

image.png

点进refresh方法,发现最后调用了一个finishRefresh方法,完成刷新

image.png

点进去finishRefresh方法看一下,此方法中发布了一个事件

我们设置上这个事件,事件对应的监听都会执行

image.png

此时再回到FrameworkServlet类

找到下面这个方法,是一个监听器,监听的东西就是泛型ContextRefreshedEvent,也就是我们发布的事件的类型(上图)

这段FrameworkServlet.this.onApplicationEvent(event);代码会执行

为什么会被执行?

在另一个地方会发布事件 1 image.png

其中在FrameworkServlet类中有一个监听器就是监听上面的事件的

监听到后这段FrameworkServlet.this.onApplicationEvent(event);代码会执行

image.png

看一下onApplicationEvent方法,里面有一个onRefresh方法,

image.png

看一下onRefresh方法,但是内部什么也没写,说明是子类进行实现

image.png

看一下FrameworkServlet类的子类DispatcherServlet类中的onRefresh方法

image.png

最后完成注册SpringMVC九大组件

image.png

1.3 处理器映射器初始化细节 注册了九个组件,我们可以选一个我们比较熟悉的initHandlerMappings进行查看

这一步的操作就是看看Spring容器之中有没有类型是HandlerMapping的组件,如果有的话,matchingBeans参数就不是空了

image.png

那此时就进不去下面这个if判断了,就不会加载默认配置文件中的组件

image.png

打断点之后发现,为什么会有四个HandlerMapping类型的参数?

image.png

我们之前配置了一个注解@EnableWebMVC

image.png

或者说spring-mvc.xml文件中的

image.png

MVC注解驱动的作用有很多,会帮我们向SpringMVC中注入一些组件,其中HandlerMapping就是在这个地方注册的

上面这几句话的操作都是@EnableWebMVC注解帮我们完成的

如果我们把@EnableWebMVC注解注释掉调后,matchingBeans参数的大小就是0,最终会加载默认配置文件中的组件,就是下图框起来的

image.png

四个HandlerMapping类型的组件,我们点开看一个

但是我们只手动配置了一个拦截器,为什么会显示两个呢?

Spring本身提供了两个

image.png

再看一下MappingRegistery参数

image.png

随便点进去一个看看

image.png

二、前端控制器执行主流程 当服务器启动时,DispatcherServlet 会执行初始化操作,接下来,每次访问都会执行service方法,我们先宏观的看一下执行流程,在去研究源码和组件执行细节

image.png

2.1 定位doDispatcher方法 在DispatcherServlet类中找service方法,但是没有,那就找他爹FrameworkServlet类,发现有Service方法,但是此方法不是最原生的,最原生的方法是ServletRequest参数

image.png

再找FrameworkServlet类的爹HttpServletBean类,但是没有Service方法

再找HttpServlet类,发现有Service方法,并且是原生的(参数前面没有http)

image.png

上图中的service方法又调用了下面的service方法(这个service进行重载了)

内部根据请求方式,看看是调用dopost还是doGet

image.png

我们可以看一下doPost方法,但是此方法左边有一个小标志,说明已经被子类覆盖了

image.png

点一下小标志,然后进入到FrameworkServlet类中,并看到doPost方法

image.png

doPost方法中又调用了一个processRequest方法

image.png

processRequest方法中又调用了doService方法,但是我们发现doService是一个抽象方法,我们需要找到对应的实现

image.png

看一下doService抽象方法的具体实现,就到了DispatcherServlet类

doService方法中调用了另外一个方法doDispatch

image.png

看一下doDispatch方法,最核心的主流程就在这里

2.2 验证HandlerExecutionChain 从DispatcherServlet类中的doDispatch开始找

此方法中有一个参数HandlerExecutionChain,这个参数内部包括Interceptor、目标对象

此方法中 this.getHandler(processedRequest)的调用对HandlerExecutionChain的参数mappedHandler进行初始化

image.png

我们看一下getHandler方法是什么,如下所示

如果参数handlerMappings不是空,就对其进行循环

image.png

那handlerMappings参数是什么呢?点进去看看

发现就是最终装HandlerMapping的集合

HandlerMapping的的填充在前面1.3进行讲解了

image.png

再回到getHandler方法,其中遍历集合的时候又调用了mapping.getHandler(request)方法

image.png

我们再看一下mapping.getHandler方法是干嘛的

image.png

找对应的实现,选择第一个

image.png

然后再这个类中对应的实现又调用了getHandlerExecutionChain方法

image.png

再看一下getHandlerExecutionChain方法

image.png

最终这个chain参数就返回到doDispatcher方法,如下标红的位置

image.png

上面的过程,就是下图中标红的地方

image.png

2.3 HandlerAdapter执行目标方法 doDispatcher方法还没有完成,继续往下看

再往下走会调用getHandlerAdapter方法

image.png

继续往下走,会执行拦截器的preHandle前置方法

image.png

执行目标方法

image.png

执行后置方法

image.png

然后我们发现执行前置方法和后置方法的时候并不是HandlerAdapter对象执行的

但是执行目标方法的时候是HandlerAdapter对象执行的

然后我们可以看一下handle方法,执行目标方法

发现没有实现,我们看一下子类的实现

image.png

选择下图中的第一个

image.png

顺着截图向下走

image.png

image.png

最终到了下面这个地方

image.png

继续执行,会进入到下面标红的方法中

image.png

继续点进去

image.png

image.png

到了下面这个地方,看一下参数

上图的参数就是在我们访问时对应的参数

image.png

再点进doInvoke方法

image.png

然后发现了执行method.invoke(this.getBean(), args)方法

反射代码,最终通过反射执行目标方法

image.png