spring-webmvc应用中有几个ApplicationCo
1. 先给结论,一父多子
一个web.xml
会对应1个,其中每一个DispatcherServlet
会对应一个,且后者对应ApplicationContext
的parent
均为前者
见org.springframework.web.servlet.DispatcherServlet
的javadoc
A web application can define any number of DispatcherServlets. Each servlet will operate in its own namespace, loading its own application context with mappings, handlers, etc. Only the root application context as loaded by ContextLoaderListener, if any, will be shared.
2.一父的初始化
ContextLoaderListener的类图ContextLoaderListener
中回调,在void contextInitialized(ServletContextEvent event)
函数中,转交给父类ContextLoader
的WebApplicationContext initWebApplicationContext(ServletContext servletContext)
函数,如下:
@Override
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}
ContextLoader
的initWebApplicationContext
方法的时序图为
此方法中可见
// 略
this.context = createWebApplicationContext(servletContext);
// 略
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
可见这里初始了一个root
,放入ServletContext
中,后文中还要取出
3.多子的初始化
DispatcherServlet的类图Servlet
接口的public void init(ServletConfig config) throws ServletException
方法在web应用启动时被回调,方法实现所在类为GenericServlet
,代码如下:
@Override
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
其中public void init() throws ServletException
专用于被覆盖,方法实现所在类为HttpServletBean
,代码如下:
@Override
public final void init() throws ServletException {
//略
// Let subclasses do whatever initialization they like.
initServletBean();
}
其中protected void initServletBean() throws ServletException
方法实现所在类为FrameworkServlet
,代码片段如下:
@Override
protected final void initServletBean() throws ServletException {
getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
if (logger.isInfoEnabled()) {
logger.info("Initializing Servlet '" + getServletName() + "'");
}
long startTime = System.currentTimeMillis();
try {
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
}
catch (ServletException | RuntimeException ex) {
logger.error("Context initialization failed", ex);
throw ex;
}
// 略一段日志
if (logger.isInfoEnabled()) {
logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
}
}
题外话,这里有spring-mvc打出的标志性日志:
INFO 15873 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
INFO 15873 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
INFO 15873 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 7 ms
继续探究初始化,本类中protected WebApplicationContext 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()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
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);
}
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);
}
//略
}
可见,这段代码做的主要是
- 得到
WebApplicationContext
类型的rootContext
变量,得到的方式与章节2结尾处放入的方式相对应,从同样位置(ServletContext
)用同样名字(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
)取 - 尝试多种方法初始化
DispatcherServlet
对应的WebApplicationContext
,并将其parent
设置为rootContext