DispatcherServlet创建流程

1.前言

在前面我们通过继承HttpServlet。覆写doGet和doPost方法,在web.xml配置相关的servlet就可以实现相关的web服务功能。

在Spring中,DispatcherServlet是Spring MVC的核心组件,他会被注册到Servlet Web容器(例如Tomcat)中,接收请求,然后做请求分发,调用Controller方法处理请求,接收响应返回给客户端。

2.DispatcherServlet

在早期版本的Spring MVC中,我们发现我们自己不用再extends HttpServlet,覆写doGetdoPost方法。因为在Spring中,它已经使用DispatcherServlet帮我们处理了。使用web.xml文件配置DispatcherServlet:

1
2
3
4
5
6
7
8
9
10
11
12
13
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/dispatcher-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>1

还记得前面讲Servlet的时候:

1
2
3
4
5
6
7
8
<servlet>
<servlet-name>FirstServlet</servlet-name>
<servlet-class>com.example.MyHttpServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>FirstServlet</servlet-name>
<url-pattern>/first</url-pattern>
</servlet-mapping>

这是Servlet规范的要求,所有的Java Web服务都需要这样配置,进行Servlet注册。我们发现只是将自定义的servlet替换成Spring给我们提供的DispatcherServlet。

2.1 DispatcherServlet继承体系

先看看DispatcherServlet相关类的继承关系:

类似如XXXAware型的接口,表示对XXX可以进行感知,通俗解释就是:如果在某个类中想要使用spring的一些东西,就可以通过实现XXXAware接口来告诉Spring,Spring看到后就能给你传送过来,而接收的方式就是通过实现该接口的唯一方法setXXX()。

EnvironmentCapable:(Capable-有能力的)顾名思义,该接口表示具有Environment的能力,也就是可以提供Environment,该接口唯一的方法就是getEnvironment(),它将返回一个Environment对象

在HttpServletBean中的Environment里面封装了ServletContext、ServletConfig、JndiProperty、系统环境变量和系统属性,这里都封装到了其propertySources属性下。在实际开发中,当web容器初始化后,在web.xml中对DispatcherServlet设置的init-param就会封装到Environment里面:

我们注意到HttpServletBean继承了HttpServlet,所以可以得知DispatcherServlet本质也是一个servlet,也遵循servlet相关规范。

3. DispatcherServlet创建流程

1
2
3
4
5
6
7
8
9
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/dispatcher-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

3.1 反射实例化Servlet

我们在web.xml文件配置相关的Servlet,当服务器启动的时候:

  • web服务器会读取web.xml文件。并根据 <servlet> 元素的配置实例化指定的 Servlet 类。

  • 实例化 DispatcherServlet

    • 服务器调用 Class.forName("org.springframework.web.servlet.DispatcherServlet") 加载 DispatcherServlet 类。
    • 使用 DispatcherServlet 的无参构造函数创建其实例。

    先调用父类 FrameworkServlet 的构造方法,再调用自己的构造方法;

1
2
3
4
5
6
7
8
9
10
11
12
public class DispatcherServlet extends FrameworkServlet {    

public DispatcherServlet() {
super();
setDispatchOptionsRequest(true);
}
}

public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
public FrameworkServlet() {
}
}

3.2 调用 init 方法

Servlet接口定义的是一套处理网络请求的规范,所有实现Servlet的类,都需要实现它那五个方法,其中最主要的是两个生命周期方法 init()destroy(),还有一个处理请求的service()

当Servlet第一次被请求时,Servlet容器就会开始调用init方法来初始化一个Servlet对象出来,但是这个方法在后续请求中不会在被Servlet容器调用只能调用一次,调用这个方法时,Servlet容器会传入一个ServletConfig对象进来从而对Servlet对象进行初始化,因此创建Servlet实例后,服务器调用 DispatcherServletinit 方法进行初始化。

3.2.1 HttpServletBean-init()

该init() 由HttpServletBean实现:

1
2
3
4
5
6
7
@Override
public final void init() throws ServletException {
... ...
initServletBean();
... ...

}

3.2.2 FrameworkServlet-initServletBean()

initServletBean方法在HttpServletBean是一个空方法,由FrameworkServlet实现:

1
2
3
4
5
6
7
8
9
@Override
protected final void initServletBean() throws ServletException {
... ...
try {
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
}
... ...
}

这里的核心代码只有2句,一句用于初始化WebApplicationContext,另一句用于初始化FrameworkServlet。

3.2.3 FrameworkServlet-initWebApplicationContext()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
protected WebApplicationContext initWebApplicationContext() {
... ...
WebApplicationContext wac = null;
//获取wac
... ...

//只有当webApplicationcontext是通过第二种方法设置的时候才会走这一段代码
if (!this.refreshEventReceived) {
onRefresh(wac);
}

// 将WebApplicationContext保存到servletcontext中
if (this.publishContext) {
// Publish the context as a servlet context attribute.
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
...
}

return wac;
}

initWebApplicationContext共做了三件事:

  1. 获取spring的根容器rootContext
  2. 设置WebApplicationContext并根据情况调用onRefresh方法
  3. 将WebApplicationContext设置到ServletContext中

不管通过哪种方式调用,onRefresh()方法肯定且只会调用一次,而且dispatchServlet正是通过重写这个模板方法来实现初始化的*

3.2.4 DispatchServlet-onRefresh()

onRefresh方法在FrameworkServlet是一个空方法,由DispatchServlet实现:

1
2
3
4
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}

3.2.5 DispatchServlet-initStrategies()

(Strategies-战略;策略;规划)方法非常简单,目的就是初始化springmvc的9大组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
protected void initStrategies(ApplicationContext context) {
//初始化上传文件解析器
initMultipartResolver(context);
     //初始化本地解析器
initLocaleResolver(context);
     //主题处理器
initThemeResolver(context);
     //映射处理器
initHandlerMappings(context);
     //处理适配器
initHandlerAdapters(context);
     //异常处理器
initHandlerExceptionResolvers(context);
     //请求到视图名的翻译器
initRequestToViewNameTranslator(context);
     //视图解析器
initViewResolvers(context);
     //初始化FlashManager
initFlashMapManager(context);
}

initHandlerMappings,initHandlerAdapters,这俩货是不是在哪里见到过,没错。就是SpringMVC请求流程中的一环。所以HandlerMappings,HandlerAdapters实在此处创建的。至此,DispatchServlet的init方法就执行完成了,然后启动整个SpringMVC容器。


博客说明

文章所涉及的资料来自互联网整理和个人总结,意在于个人学习和经验汇总,不用于任何的商业用途。如有侵权,请联系本人删除。谢谢!


DispatcherServlet创建流程
https://nanchengjiumeng123.top/2024/01/20/framework/spring/Spring MVC/2.DispatcherServlet创建流程/
作者
Yang Xin
发布于
2024年1月20日
许可协议