SpringMVC(二):DispatcherServlet对SpringMVC的初始化

在SpringMVC项目中除了spring容器(根上下文),DispatcherServlet也会建立一个自己的IoC容器来持有springmvc的bean对象,在建立这个容器时,会从ServletContext中获取根上下文作为自己的双亲上下文,它可获取双亲上下文中的bean,DispatcherServlet的IoC容器也会存到ServletContext中。

DispatcherServlet是一个servlet,回想servlet的生命周期,servlet在初始化的时候调用的是init方法,那么就从init方法开始看一下DispatcherServlet对SpringMVC的初始化。

继承关系图:

1.HttpServletBean

HttpServletBean是DispatcherServlet的父类,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
public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware {

public final void init() throws ServletException {
if(this.logger.isDebugEnabled()) {
this.logger.debug("Initializing servlet '" + this.getServletName() + "'");
}
//获取servlet初始化参数
PropertyValues pvs = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties);
if(!pvs.isEmpty()) {
try {
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(this.getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.getEnvironment()));
this.initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
} catch (BeansException var4) {
if(this.logger.isErrorEnabled()) {
this.logger.error("Failed to set bean properties on servlet '" + this.getServletName() + "'", var4);
}

throw var4;
}
}
// 初始化,在FrameworkServlet中实现
this.initServletBean();
if(this.logger.isDebugEnabled()) {
this.logger.debug("Servlet '" + this.getServletName() + "' configured successfully");
}
}

}

HttpServletBean除了获取servlet初始化参数,还调用了initServletBean方法,进行具体的初始化,这个方法是在FrameworkServlet中实现的。

2.FrameworkServlet

FrameworkServlet同样是DispatcherServlet的父类。

DispatcherServlet的IoC容器的初始化

在initServletBean方法中,调用了initWebApplicationContext方法来初始化IoC容器,在初始化容器的方法中:

  1. 首先从ServletContext中获取了根上下文,然后判断DispatcherServlet的容器wac是否为空,如果不为空就将根上下文设置为双亲上下文,然后调用configureAndRefreshWebApplicationContext方法,完成容器的初始化。

  2. 如果容器为空,就调用createWebApplicationContext创建容器,默认使用的是XmlWebApplicationContext,创建容器的时候将根上下文作为参数传入,创建之后同样将根上下文设置为双亲上下文。

  3. DispatcherServlet的容器也会被设置到SerletContext中,使用的属性名是和
    当前Servlet的名称。

  4. 在createWebApplicationContext创建容器的方法中,最后调用了configureAndRefreshWebApplicationContext方法,在该方法中调用了容器的refresh方法,之后就与spring IoC容器的初始化过程一致了。

之后,SpringMVC的上下文就建立起来了,意味着DispatcheServlet拥有自己的Bean定义空间,为使用各个独立的XML文件来配置MVC中的各个bean创建了条件。

SpringMVC的初始化

在initWebApplicationContext方法中,在容器创建完成后,有一步是调用this.onRefresh方法,这个方法是在DispatcherSerlet中实现的,在该方法中完成了SpringMVC的初始化。

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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
protected final void initServletBean() throws ServletException {
this.getServletContext().log("Initializing Spring FrameworkServlet '" + this.getServletName() + "'");
if(this.logger.isInfoEnabled()) {
this.logger.info("FrameworkServlet '" + this.getServletName() + "': initialization started");
}

long startTime = System.currentTimeMillis();

try {
// 初始化容器
this.webApplicationContext = this.initWebApplicationContext();
this.initFrameworkServlet();
} catch (RuntimeException | ServletException var5) {
this.logger.error("Context initialization failed", var5);
throw var5;
}

if(this.logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
this.logger.info("FrameworkServlet '" + this.getServletName() + "': initialization completed in " + elapsedTime + " ms");
}

}
// 初始化容器
protected WebApplicationContext initWebApplicationContext() {
// 获取根上下文
WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
// DispatchServlet的容器
WebApplicationContext wac = null;
// 如果不为空
if(this.webApplicationContext != null) {
wac = this.webApplicationContext;
if(wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)wac;
if(!cwac.isActive()) {
if(cwac.getParent() == null) {
// 将根上下文设置为当前容器的双亲
cwac.setParent(rootContext);
}

this.configureAndRefreshWebApplicationContext(cwac);
}
}
}

if(wac == null) {
wac = this.findWebApplicationContext();
}

if(wac == null) {
// 如果dispatchServlet的容器为空,创建容器
wac = this.createWebApplicationContext(rootContext);
}

if(!this.refreshEventReceived) {
// 在DispatcherServlet中实现了该方法,完成springMVC的初始化
this.onRefresh(wac);
}

if(this.publishContext) {
String attrName = this.getServletContextAttributeName();
// 将创建好的容器设置到ServletContext
this.getServletContext().setAttribute(attrName, wac);
if(this.logger.isDebugEnabled()) {
this.logger.debug("Published WebApplicationContext of servlet '" + this.getServletName() + "' as ServletContext attribute with name [" + attrName + "]");
}
}

return wac;
}


protected WebApplicationContext createWebApplicationContext(@Nullable WebApplicationContext parent) {
return this.createWebApplicationContext((ApplicationContext)parent);
}

// 创建容器
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
// 获取ContextClass
Class<?> contextClass = this.getContextClass();
if(this.logger.isDebugEnabled()) {
this.logger.debug("Servlet with name '" + this.getServletName() + "' will try to create custom WebApplicationContext context of class '" + contextClass.getName() + "', using parent context [" + parent + "]");
}

if(!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Fatal initialization error in servlet with name '" + this.getServletName() + "': custom WebApplicationContext class [" + contextClass.getName() + "] is not of type ConfigurableWebApplicationContext");
} else {
//创建容器,默认使用的是XmlWebApplicationContext
ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);
wac.setEnvironment(this.getEnvironment());
// 这里同样将根上下文作为当前容器的双亲上下文
wac.setParent(parent);
String configLocation = this.getContextConfigLocation();
if(configLocation != null) {
wac.setConfigLocation(configLocation);
}
// 配置并刷新容器
this.configureAndRefreshWebApplicationContext(wac);
return wac;
}
}

// 配置并初始化容器
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
if(ObjectUtils.identityToString(wac).equals(wac.getId())) {
if(this.contextId != null) {
wac.setId(this.contextId);
} else {
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(this.getServletContext().getContextPath()) + '/' + this.getServletName());
}
}
// 设置ServletContext
wac.setServletContext(this.getServletContext());
wac.setServletConfig(this.getServletConfig());
wac.setNamespace(this.getNamespace());
wac.addApplicationListener(new SourceFilteringListener(wac, new FrameworkServlet.ContextRefreshListener(null)));
ConfigurableEnvironment env = wac.getEnvironment();
if(env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment)env).initPropertySources(this.getServletContext(), this.getServletConfig());
}

this.postProcessWebApplicationContext(wac);
this.applyInitializers(wac);
// 调用容器的refresh方法
wac.refresh();
}
WebApplicationContextUtils

取得根上下文的过程是在WebApplicationContextUtils中实现的。在getWebApplicationContext方法中,先调用WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE获取根上下文在ServletContext中的属性名,然后从ServletContext中获取根上下文,转换为WebApplicationContext返回。

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
public abstract class WebApplicationContextUtils {
private static final boolean jsfPresent = ClassUtils.isPresent("javax.faces.context.FacesContext", RequestContextHolder.class.getClassLoader());

public WebApplicationContextUtils() {
}

//获取根上下文
public static WebApplicationContext getRequiredWebApplicationContext(ServletContext sc) throws IllegalStateException {
// 获取根上下文
WebApplicationContext wac = getWebApplicationContext(sc);
if(wac == null) {
throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener registered?");
} else {
return wac;
}
}

@Nullable
public static WebApplicationContext getWebApplicationContext(ServletContext sc) {
// 从servlet中获取根上下文
return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
}

@Nullable
public static WebApplicationContext getWebApplicationContext(ServletContext sc, String attrName) {
Assert.notNull(sc, "ServletContext must not be null");
// 从servlet中根据属性名获取根上下文
Object attr = sc.getAttribute(attrName);
if(attr == null) {
return null;
} else if(attr instanceof RuntimeException) {
throw (RuntimeException)attr;
} else if(attr instanceof Error) {
throw (Error)attr;
} else if(attr instanceof Exception) {
throw new IllegalStateException((Exception)attr);
} else if(!(attr instanceof WebApplicationContext)) {
throw new IllegalStateException("Context attribute is not of type WebApplicationContext: " + attr);
} else {
// 返回根上下文
return (WebApplicationContext)attr;
}
}
}

3.DispatcherServlet

在FrameworkServlet的configureAndRefreshWebApplicationContext方法中,最后一步是调用onRefresh方法,这个方法是在DispatchServlet中实现的。

在onRefresh方法中,调用了initStrategies方法,这个方法中启动了SpringMVC框架的初始化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class DispatcherServlet extends FrameworkServlet {
protected void onRefresh(ApplicationContext context) {
this.initStrategies(context);
}

protected void initStrategies(ApplicationContext context) {
this.initMultipartResolver(context);
this.initLocaleResolver(context);
this.initThemeResolver(context);
// 初始化HanlderMappings
this.initHandlerMappings(context);
this.initHandlerAdapters(context);
this.initHandlerExceptionResolvers(context);
this.initRequestToViewNameTranslator(context);
this.initViewResolvers(context);
this.initFlashMapManager(context);
}
}

以HandlerMapping为例,HandlerMapping是为HTTP请求寻找对应的Controller控制器,在HandlerMapping初始化过程中,从IoC容器中获取在配置文件中配置好的,以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
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
// 导入所有的HandlerMapping Bean,默认从所有的容器中获取
if(this.detectAllHandlerMappings) {
Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if(!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList(matchingBeans.values());
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
} else {
try {
// 根据名称从当前的容器中获取
HandlerMapping hm = (HandlerMapping)context.getBean("handlerMapping", HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
} catch (NoSuchBeanDefinitionException var3) {
;
}
}
// 如果没有找到handlerMapping,设置默认的handlerMapping
if(this.handlerMappings == null) {
this.handlerMappings = this.getDefaultStrategies(context, HandlerMapping.class);
if(this.logger.isDebugEnabled()) {
this.logger.debug("No HandlerMappings found in servlet '" + this.getServletName() + "': using default");
}
}

}

参考:

spring技术内幕:深入解析spring架构与设计原理

Spring版本:5.0.5