(八)Spring MVC的上下文在Web容器中的啓動

Web環境下的MVC

Spring IoC是一個獨立的模塊,並不能直接在Web容器中發揮做用。java

因此要在Web容器中使用IoC容器,須要爲Spring IoC設計一個啓動過程,並把IoC容器導入進來web

Web容器的啓動過程一方面處理Web容器的啓動,另外一方面將IoC容器載入到web環境中並將其初始化this

Spring MVC是創建在IoC容器的基礎上的,在導入IoC容器後才能創建MVCspa

上下文在Web容器中的啓動過程

在常見的web.xml中須要配置一個DispatcherServlet類型的servlet和一個ContextLoaderListener類型的listener設計

Spring MVC經過這兩個類在Web容器中創建MVC,並將建立好的容器放到ServletContextcode

ContextLoaderListener用於實現Spring IoC的啓動,建立IoC容器做爲"根容器"xml

DispatcherServlet建立另外一個IoC容器,並與根容器搭建雙親容器,完成MVC的創建對象

ContextLoaderListener調用方法contextInitialized()實現IoC容器的啓動get

DispatcherServlet調用父類的init()方法建立IoC容器和搭建雙親容器,以搭建好的IoC容器爲基礎創建MVCservlet

建立根上下文

initWebApplicationContext()方法的實如今ContextLoaderListener的父類ContextLoader

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
    // 若是ServletContext已經存在根容器,說明已經建立過根容器,就不須要再執行下面的流程
    if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
        throw ...
    }

    try {
        if (this.context == null) {
            // 建立容器
            this.context = createWebApplicationContext(servletContext);
        }
        if (this.context instanceof ConfigurableWebApplicationContext) {
            ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
            // 若是容器沒有被初始化過就執行如下流程
            if (!cwac.isActive()) {
                if (cwac.getParent() == null) {
                    // Spring5之後此方法直接返回null值
                    ApplicationContext parent = loadParentContext(servletContext);
                    cwac.setParent(parent);
                }
                // 執行容器的refresh()方法刷新容器
                configureAndRefreshWebApplicationContext(cwac, servletContext);
            }
        }
        // 將容器做爲根容器。以指定常量爲key,容器爲value將其添加到ServletContext中
        servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

        ClassLoader ccl = Thread.currentThread().getContextClassLoader();
        if (ccl == ContextLoader.class.getClassLoader()) {
            currentContext = this.context;
        }
        else if (ccl != null) {
            currentContextPerThread.put(ccl, this.context);
        }

        return this.context;
    }
    catch (Error err) {
        throw err;
    }
}

在DispatcherServlet中建立IoC容器

init() —— initServletBean() —— initWebApplicationContext()

initWebApplicationContext()方法的實如今DispatcherServlet的父類FrameworkServlet

protected WebApplicationContext initWebApplicationContext() {
    // 從ServletContext中獲取根容器
    WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    
    WebApplicationContext wac = null;

    // 默認this.webApplicationContext==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);
                }
                // 啓動容器
                configureAndRefreshWebApplicationContext(cwac);
            }
        }
    }
    if (wac == null) {
        wac = findWebApplicationContext();
    }
    if (wac == null) {
        // 若是wac仍是爲null就建立容器,並將根容器設置爲父容器
        wac = createWebApplicationContext(rootContext);
    }

    if (!this.refreshEventReceived) {
        // 搭建MVC,初始化Spring MVC的九大組件
        onRefresh(wac);
    }

    // 獲取 由DispatcherServlet建立的容器名,以kv方式放進ServletContext中
    if (this.publishContext) {     
        String attrName = getServletContextAttributeName();
        getServletContext().setAttribute(attrName, wac);
    }

    return wac;
}
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
    Class<?> contextClass = getContextClass();

    if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
        throw ...
    }
    
    ConfigurableWebApplicationContext wac =
        (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

    wac.setEnvironment(getEnvironment());
    
    // 設置雙親容器
    wac.setParent(parent);
    String configLocation = getContextConfigLocation();
    if (configLocation != null) {
        wac.setConfigLocation(configLocation);
    }
    
    /** 配置並刷新容器
      * 在刷新容器時,會爲容器建立一個BeanFactory對象。
      * 調用DefaultListableBeanFactory的構造方法時會嘗試獲取父容器並將父容器自己或父容器持有的BeanFactory做爲此BeanFactory的parentBeanFactory
      這個parentBeanFactory會在getBean中被用到(在獲取bean前,先嚐試從父工廠中獲取bean)
    **/
    configureAndRefreshWebApplicationContext(wac);

    return wac;
}
相關文章
相關標籤/搜索