spring-webmvc应用中有几个ApplicationCo

2019-04-04  本文已影响0人  dracula337435

1. 先给结论,一父多子

一个web.xml会对应1个,其中每一个DispatcherServlet会对应一个,且后者对应ApplicationContextparent均为前者

org.springframework.web.servlet.DispatcherServletjavadoc

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)函数中,转交给父类ContextLoaderWebApplicationContext initWebApplicationContext(ServletContext servletContext)函数,如下:

@Override
public void contextInitialized(ServletContextEvent event) {
    initWebApplicationContext(event.getServletContext());
}

ContextLoaderinitWebApplicationContext方法的时序图为

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);
    }

    //略
}

可见,这段代码做的主要是

  1. 得到WebApplicationContext类型的rootContext变量,得到的方式与章节2结尾处放入的方式相对应,从同样位置(ServletContext)用同样名字(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE)取
  2. 尝试多种方法初始化DispatcherServlet对应的WebApplicationContext,并将其parent设置为rootContext
上一篇下一篇

猜你喜欢

热点阅读