SpringMVC源码分析 - DispatcherServlet初始化过程

继承体系

从 DispatcherServlet 继承体系来看(蓝色部分),DispatcherServlet 继承自 FrameworkServlet,而 FrameworkServlet 又继承自 HttpServletBean ,最终 HttpSevletBean 继承了 HttpServlet 。通过这一步步继承封装之后,才构成了如今的 DispatcherSevlet 架构基础。

下面将自上到下来说明 DispatcherServlet 的初始化过程。

HttpServlet

HttpServletBean 继承自 Servlet 架构中的 HttpServlet 类,并重写了init()方法。

Servlet 生命周期从创建到销毁的过程中,有三个重要的方法:

  • init() - 负责初始化 Servlet 对象。在 Servlet 生命周期中只会调用一次。
  • service() - 负责响应客户的请求。每次服务器接收到一个 Servlet 请求时,服务器会产生一个新的线程并调用服务。service 方法中两个参数,分别是 ServletRequest 和 ServletResponse,用于传递 http 请求和回写。
  • destory() - 负责销毁 Servlet 对象。在 Servlet 生命周期中只会调用一次。

从 Servlet 的生命周期可知,在 init()方法中,我们可以进行初始化工作,HttpServletBean 正是也做了这样的工作。源码如下所示:

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
30
31
32
33
@Override
public final void init() throws ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Initializing servlet '" + getServletName() + "'");
}

// Set bean properties from init parameters.
// 加载 Servlet 的配置文件(一般指 web.xml)
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw); // 上面做了这么多的工作,到这里却是一个空方法,而它的子类都没有去重写这个方法,个人认为这是想让开发者自定义如何管理 Servlet 配置吧
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
if (logger.isErrorEnabled()) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
}
throw ex;
}
}

// Let subclasses do whatever initialization they like.
// 交由子类(FrameworkServlet)来进行其特有的初始化工作
initServletBean();

if (logger.isDebugEnabled()) {
logger.debug("Servlet '" + getServletName() + "' configured successfully");
}
}

FrameworkServlet

FrameworkServlet 继承自 HttpServletBean,实现了initServletBean()方法。FrameworkServlet 在继承体系结构中,在 Servlet 与 SpringMVC 起到了承上启下的作用,它负责初始化 WebApplicationContext,还负责重写了 Servlet 生命周期中另外两个重要方法——service()destory(),并改写了doGet()doPost()等 http 方法,统一调用processHandler()方法来处理所有 http 请求。

ApplicationContext 是 Spring 的核心,相当于 Spring 环境中的上下文。而在WebApplicationContext 继承自 ApplicationContext,充当了在 Web 环境中使用 Spring 的上下文。在 Web 环境中,WebApplicationContext 实例需要 ServletContext,即它必须拥有 Web 容器才能够完成启动的工作。

下面重点讲initServletBean()方法,源码如下所示:

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
@Override
protected final void initServletBean() throws ServletException {
// do sth
try {
// 初始化 WebApplicationContext
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
}
catch (ServletException ex) {
this.logger.error("Context initialization failed", ex);
throw ex;
}
catch (RuntimeException ex) {
this.logger.error("Context initialization failed", ex);
throw ex;
}
}

// initServletBean()转而调用了initWebApplicationContext(),所以重点工作在这里
protected WebApplicationContext initWebApplicationContext() {
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;

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()) {
if (cwac.getParent() == null) {
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
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();
}
if (wac == null) {
// No context instance is defined for this servlet -> create a local one
wac = createWebApplicationContext(rootContext);
}

if (!this.refreshEventReceived) {
// DispatcherSevlet 初始化工作的入口就在这里!
onRefresh(wac);
}

// do sth
return wac;
}

DispatcherServlet

在进行下一步代码分析之前,先看下 DispatcherSevrlet 的静态代码块部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";

static {
// Load default strategy implementations from properties file.
// This is currently strictly internal and not meant to be customized
// by application developers.
// 加载所有默认配置,用于后面的初始化工作
try {
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
catch (IOException ex) {
throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
}
}

DispatcherServlet.properties配置文件中定义了DispatcherServlet各组件中的配置实现形式,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.

org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator

org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver

org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

回到正题,在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
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}

protected void initStrategies(ApplicationContext context) {
// 初始化多媒体解析器
initMultipartResolver(context);
// 初始化位置解析器
initLocaleResolver(context);
// 初始化主题解析器
initThemeResolver(context);
// 初始化 HandlerMappings
initHandlerMappings(context);
// 初始化 HandlerAdapters
initHandlerAdapters(context);
// 初始化异常处理解析器
initHandlerExceptionResolvers(context);
// 初始化请求到视图名转换器
initRequestToViewNameTranslator(context);
// 初始化视图解析器
initViewResolvers(context);
// 初始化 FlashMapManager
initFlashMapManager(context);
}

下面以initHandlerMappings()方法为例,分析如何加载 HandlerMapping。

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
30
31
32
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;

if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
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);
}
}
else {
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}

// Ensure we have at least one HandlerMapping, by registering
// a default HandlerMapping if no other mappings are found.
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isDebugEnabled()) {
logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
}
}
}

上面的源码注释解释的十分清楚了,值得注意一点的是,为了确保 DispatcherServlet 中至少有一个 HandlerMapping,它会从上面所述的默认配置项中加载所有默认组件,如 HandlerMapping 默认组件为 BeanNameUrlHandlerMapping、RequestMappingHandlerMapping。


参考资料:

servlet清晰理解

WebApplicationContext初始化

Spring MVC之DispatcherServlet初始化