DispatcherServlet 初始化过程

警告
本文最后更新于 2020-12-15,文中内容可能已过时。

spring源码系列文章,示例代码的中文注释,均是 copy 自 https://gitee.com/wlizhi/spring-framework

链接中源码是作者从 github 下载,并以自身理解对核心流程及主要节点做了详细的中文注释。


1 初始化入口

在服务器容器启动时,通过SPI机制,加载 ServletContainerInitializer 后,调用 WebApplicationInitializer 的 onStartup(),创建了父子容器和 DispatcherServlet。

在 Servlet 创建后,会调用其 init() 方法进行一些初始化操作,DispatcherServlet 就是如此。

在 DispatcherServlet 的 init()中,会经过以下的方法调用链:

init() -> initServletBean() -> initWebApplicationContext() -> configureAndRefreshWebApplicationContext() -> addApplicationListener() -> refresh() -> onApplicationEvent() -> onRefresh() -> initStrategies()

在 initStrategies() 中,进行了 DispatcherServlet 的初始化操作。

ApplicationContext的监听器注册及refresh()

在 initWebApplicationContext() 这个方法节点,完成了 configureAndRefreshWebApplicationContext() 的调用。

源码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {

    protected WebApplicationContext initWebApplicationContext() {
		WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		WebApplicationContext wac = null;

		// TODO 注解方式激活MVC功能时,会走到这里
		if (this.webApplicationContext != null) {
			// A context instance was injected at construction time -> use it
			wac = this.webApplicationContext;
			if (wac instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
				if (!cwac.isActive()) {
					// 设置父容器,这个父容器时从servletContext中获取的。
					// 父容器是在 ContextLoader.initWebApplicationContext(),调用完refresh()后设置到servletContext中的。
					if (cwac.getParent() == null) {
						cwac.setParent(rootContext);
					}
					// TODO 配置、刷新WebApplicationContext,调用DispatcherServlet的init()
					configureAndRefreshWebApplicationContext(cwac);
				}
			}
		}
		// 省略非关键代码...

		return wac;
	}
}

首先获取父容器,如果父容器存在,在子容器中设置父容器引用。调用 configureAndRefreshWebApplicationContext(),内部调用了 refresh()、以及上下文刷新后的监听器注册。

在 refresh() 后,通过监听器,回调到 onRefresh()。

源码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {

    protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
		// 设置了ApplicationContext的id,代码省略...

		wac.setServletContext(getServletContext());
		wac.setServletConfig(getServletConfig());
		wac.setNamespace(getNamespace());
		// TODO 通过监听器的方式初始化Servlet,将在WebApplicationContext.refresh()调用之后执行ContextRefreshListener
		wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
		
        //省略非关键代码...

		// 这里调用了核心方法 refresh()
		wac.refresh();
	}
}

首先将 ServletContext 及其配置对象设置到 ApplicationContext 中,然后添加监听器,最后调用 refresh()。

这个监听器是一个 ContextRefreshedEvent 事件。即容器刷新后事件。实际上,会先执行 refresh()。在 refresh() 之后,会回调到监听器的代码逻辑。

ContextRefreshListener 源码如下:

1
2
3
4
5
6
7
8
private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        // TODO 重点:上下文刷新事件
        FrameworkServlet.this.onApplicationEvent(event);
    }
}

DispatcherServlet初始化

在 refresh() 后,会执行以下流程:

onApplicationEvent() -> onRefresh() -> initStrategies()

initStrategies() 源码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class DispatcherServlet extends FrameworkServlet {

    protected void initStrategies(ApplicationContext context) {
		// 初始化MultipartResolver,用于处理文件上传服务,如果有文件上传,
		// 那么就会将当前的HttpServletRequest包装成DefaultMultipartHttpServletRequest,
		// 并且将每个上传的内容封装成CommonsMultipartFile对象。需要在dispatcherServlet-servlet.xml中配置文件上传解析器。
		initMultipartResolver(context);
		// 用于处理应用的国际化问题,本地化解析策略。
		initLocaleResolver(context);
		// 用于定义一个主题。
		initThemeResolver(context);
		// TODO handlerMappings的初始化
		initHandlerMappings(context);
		// TODO handlerAdapters的初始化
		initHandlerAdapters(context);
		// TODO 异常处理器的初始化,当Handler处理出错后,会通过此将错误日志记录在log文件中,默认实现类是SimpleMappingExceptionResolver。
		initHandlerExceptionResolvers(context);
		// 将指定的ViewName按照定义的RequestToViewNameTranslators替换成想要的格式。
		initRequestToViewNameTranslator(context);
		// 用于将View解析成页面。
		initViewResolvers(context);
		// 用于生成FlashMap管理器。
		initFlashMapManager(context);
	}
}

如上面源码中注释,这里初始化了一系列的内容,重点看一下 initHandlerMappings()、initHandlerAdapters()、initHandlerExceptionResolvers()。其他的初始化方法类似。


initHandlerMappings源码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
public class DispatcherServlet extends FrameworkServlet {

    private void initHandlerMappings(ApplicationContext context) {
		this.handlerMappings = null;

		if (this.detectAllHandlerMappings) {
			logger.debug("detectAllHandlerMappings --> true");
			// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
			// 获取到IOC容器中所有的HandlerMapping实例映射
			Map<String, HandlerMapping> matchingBeans =
					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
			if (!matchingBeans.isEmpty()) {
				this.handlerMappings = new ArrayList<>(matchingBeans.values());
				// We keep HandlerMappings in sorted order.
				AnnotationAwareOrderComparator.sort(this.handlerMappings);
			}
		}
		
        // 省略非关键代码...
	}
}

从容器中找到所有的 HandlerMapping 类型的实例映射,由于前面流程中已经调用过 refresh() 了,且通过 @EnableWebMvc,已经注入了一些列 mvc 相关支持的组件,其中就包括 RequestMappingHandlerMapping。

这里获取到的一个 map,键值是 beanName,值就是对应的实例。

最终,将这个 map 封装到 DispatcherServlet 的成员变量 handlerMappings 中。


initHandlerAdapters() 源码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
public class DispatcherServlet extends FrameworkServlet {

	private void initHandlerAdapters(ApplicationContext context) {
		this.handlerAdapters = null;

		if (this.detectAllHandlerAdapters) {
			// Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.
			// 从IOC容器中获取所有的HandlerAdapter实例
			Map<String, HandlerAdapter> matchingBeans =
					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
			if (!matchingBeans.isEmpty()) {
				this.handlerAdapters = new ArrayList<>(matchingBeans.values());
				// We keep HandlerAdapters in sorted order.
				AnnotationAwareOrderComparator.sort(this.handlerAdapters);
			}
		}
		// 省略非关键代码...
	}
}

与 initHandlerMappings() 类似,从容器获取到 HandlerAdapter 类型的实例映射 map,设置到成员变量 handlerAdapters。


initHandlerExceptionResolvers() 源码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
public class DispatcherServlet extends FrameworkServlet {

private void initHandlerExceptionResolvers(ApplicationContext context) {
		this.handlerExceptionResolvers = null;

		if (this.detectAllHandlerExceptionResolvers) {
			// 获取所有的异常处理器
			// Find all HandlerExceptionResolvers in the ApplicationContext, including ancestor contexts.
			Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils
					.beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);
			if (!matchingBeans.isEmpty()) {
				this.handlerExceptionResolvers = new ArrayList<>(matchingBeans.values());
				// We keep HandlerExceptionResolvers in sorted order.
				AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);
			}
		}
		// 省略非关键代码...
	}
}

同样是类似的操作,获取到容器中 HandlerExceptionResolver 类型的 bean 实例映射,将其设置到成员变量 handlerExceptionResolvers。

像 initLocaleResolver() 处理国际化问题,非多语言的项目用不到。由于目前开发基本都是前后端分离,后台仅以接口方式响应数据,所以 initViewResolvers() 视图解析基本也用不到了。

其他的初始化操作,与此类似。都是将一些功能支持的类实例,封装到 DispatcherServlet 对应成员变量中。