在开始之前,我们还是回过头看一眼 web.xml 的配置。代码如下:
HttpServletBean ,负责将 ServletConfig 设置到当前 Servlet 对象中。类上的简单注释如下:
FrameworkServlet ,负责初始化 Spring Servlet WebApplicationContext 容器。类上的简单注释如下:
DispatcherServlet ,负责初始化 Spring MVC 的各个组件,以及处理客户端的请求。类上的简单注释如下:
每一层的 Servlet 实现类,执行对应负责的逻辑。
下面,我们逐个类来进行解析。
HttpServletBean
实现 EnvironmentCapable、EnvironmentAware 接口,继承 HttpServlet 抽象类,负责将 ServletConfig 集成到 Spring 中。当然,HttpServletBean 自身也是一个抽象类。
1. 构造方法
environment 属性,相关的方法,代码如下:
为什么 environment 属性,能够被自动注入呢?答案是 实现了EnvironmentAware 接口。
requiredProperties 属性,必须配置的属性的集合。可通过 #addRequiredProperty(String property) 方法,添加到其中。代码如下:
2. init
#init() 方法,负责将 ServletConfig 设置到当前 Servlet 对象中。代码如下:
<1> 处,解析 Servlet 配置的
代码简单,实现两方面的逻辑:<1> 处,遍历 ServletConfig 的初始化参数集合,添加到 ServletConfigPropertyValues 中;<2> 处,判断要求的属性是否齐全。如果不齐全,则抛出 ServletException 异常。
然而,目前子类并没有任何实现。
此处有配置了 contextConfigLocation 属性,那么通过 <2.4> 处的逻辑,会反射设置到 FrameworkServlet.contextConfigLocation 属性。代码如下:
<3> 处,调用 #initServletBean() 方法,子类来实现,实现自定义的初始化逻辑。目前,FrameworkServlet 实现类该方法。代码如下:
FrameworkServlet
实现 ApplicationContextAware 接口,继承 HttpServletBean 抽象类,负责初始化 Spring Servlet WebApplicationContext 容器。同时,FrameworkServlet 自身也是一个抽象类。
1. 构造方法
FrameworkServlet 的属性还是非常多,我们还是只看部分的关键属性。代码如下:
其中,contextClass 属性,创建的 WebApplicationContext 类型,默认为 DEFAULT_CONTEXT_CLASS 。代码如下:
又是我们熟悉的 XmlWebApplicationContext 类。在上一篇文章的 ContextLoader.properties 配置文件中,我们已经看到咯。
方式一:通过构造方法,代码如下:
通过方法参数 webApplicationContext 。
方式二:因为实现 ApplicationContextAware 接口,也可以 Spring 注入。代码如下:
方式三:见 #findWebApplicationContext() 方法。
方式四:见 #createWebApplicationContext(WebApplicationContext parent) 方法。
2. initServletBean
#initServletBean() 方法,进一步初始化当前 Servlet 对象。实际上,重心在初始化 Servlet WebApplicationContext 容器。代码如下:
3. initWebApplicationContext
#initWebApplicationContext() 方法,初始化 Servlet WebApplicationContext 对象。代码如下:
protected WebApplicationContext initWebApplicationContext() {
// <1> 获得根 WebApplicationContext 对象
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
// <2> 获得 WebApplicationContext wac 变量
WebApplicationContext wac = null;
// 第一种情况,如果构造方法已经传入 webApplicationContext 属性,则直接使用
if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it
wac = this.webApplicationContext;
// 如果是 ConfigurableWebApplicationContext 类型,并且未激活,则进行初始化
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) { // 未激活
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
// 设置 wac 的父 context 为 rootContext 对象
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent -> set
// the root application context (if any; may be null) as the parent
cwac.setParent(rootContext);
}
// 配置和初始化 wac
configureAndRefreshWebApplicationContext(cwac);
}
}
}
// 第二种情况,从 ServletContext 获取对应的 WebApplicationContext 对象
if (wac == null) {
// No context instance was injected at construction time -> see if one
// has been registered in the servlet context. If one exists, it is assumed
// that the parent context (if any) has already been set and that the
// user has performed any initialization such as setting the context id
wac = findWebApplicationContext();
}
// 第三种,创建一个 WebApplicationContext 对象
if (wac == null) {
// No context instance is defined for this servlet -> create a local one
wac = createWebApplicationContext(rootContext);
}
// <3> 如果未触发刷新事件,则主动触发刷新事件
if (!this.refreshEventReceived) {
// Either the context is not a ConfigurableApplicationContext with refresh
// support or the context injected at construction time had already been
// refreshed -> trigger initial onRefresh manually here.
synchronized (this.onRefreshMonitor) {
onRefresh(wac);
}
}
// <4> 将 context 设置到 ServletContext 中
if (this.publishContext) {
// Publish the context as a servlet context attribute.
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}
return wac;
}
<1> 处,调用 WebApplicationContextUtils#getWebApplicationContext((ServletContext sc) 方法,获得 Root WebApplicationContext 对象,
这就是在 Root WebApplicationContext 容器中初始化的呀。代码如下:
而这个是在Root WebApplicationContext 容器初始化的时候进行设置的:
org.springframework.web.context.ContextLoader#initWebApplicationContext
<2> 处,获得 WebApplicationContext wac 变量。下面,会分成三种情况。
========== 第一种情况 ==========
如果构造方法已经传入 webApplicationContext 属性,则直接使用。实际上,就是我们在 1. 构造方法 提到的 Servlet WebApplicationContext 容器的第一、二种方式。
实际上,这块代码和 ContextLoader#initWebApplicationContext(ServletContext servletContext) 的中间段是一样的。除了 #configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) 的具体实现代码不同。详细解析,见 4. configureAndRefreshWebApplicationContext 。
========== 第二种情况 ==========
这种情况,就是我们在 4.1 构造方法 提到的 Servlet WebApplicationContext 容器的第三种方式。
如果此处 wac 还是为空,则调用 #findWebApplicationContext() 方法,从 ServletContext 获取对应的 WebApplicationContext 对象。代码如下:
========== 第三种情况 ==========
这种情况,就是我们在 「4.1 构造方法」 提到的 Servlet WebApplicationContext 容器的第四种方式。
如果此处 wac 还是为空,则调用 #createWebApplicationContext(WebApplicationContext parent) 方法,创建一个 WebApplicationContext 对象。代码如下:
详细解析,见 4. configureAndRefreshWebApplicationContext。
========== END ==========
<3> 处,如果未触发刷新事件,则调用 #onRefresh(ApplicationContext context) 主动触发刷新事件。详细解析,见 5. onRefresh 中。
<4> 处,如果 publishContext 为 true 时,则将 context 设置到 ServletContext 中。
4. configureAndRefreshWebApplicationContext
#configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) 方法,配置和初始化 wac 。代码如下:
5. onRefresh
#onRefresh(ApplicationContext context) 方法,当 Servlet WebApplicationContext 刷新完成后,触发 Spring MVC 组件的初始化。代码如下:
这是一个空方法,具体的实现,在子类 DispatcherServlet 中。代码如下:
#onRefresh() 方法,有两种方式被触发:
详细解析,见 5. SourceFilteringListener 。
6. SourceFilteringListener
SourceFilteringListener实现了ApplicationListener接口,重写了onApplicationEvent方法,关键就是这个方法。
调用的是delegate.onApplicationEvent(event); 这里的delegate指的其实就是构造函数传进来的ContextRefreshListener。
所以最终调用的是org.springframework.web.servlet.FrameworkServlet.ContextRefreshListener#onApplicationEvent()。
这里,把refreshEventReceived设置成了true,回到了 5. onRefresh 。
以上,就是Servlet WebApplicationContext 容器启动过程。