Spring MVC(一)Servlet 2.x 規範在 Spring MVC 中的應用

Spring MVC(一)Servlet 2.x 規範在 Spring MVC 中的應用

Spring 系列目錄(http://www.javashuo.com/article/p-kqecupyl-bm.html)html

《Servlet 2.x 規範》:http://www.javashuo.com/article/p-dggyaqqw-dn.htmljava

Servlet 容器在啓動時會調用 ServletContextListener 的 contextInitialized() 方法。同時 Servlet 在初始化時會執行 init 方法。Spring MVC 正是在這兩個過程當中建立 Root WebApplicationContext 和 Servlet WebApplicationContext 容器。web

WebApplicationContext

1、ContextLoaderListener

ContextLoaderListener

ContextLoaderListener 的 contextInitialized 只是簡單的調用了一個其父類 ContextLoader 的 initWebApplicationContext 方法,建立了一個 Root 容器。spring

// 建立 Root WebApplicationContext
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
    if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
        throw new IllegalStateException(
                "Cannot initialize context because there is already a root application context present");
    }
    try {
        // 1. 建立 Root WebApplicationContext
        if (this.context == null) {
            this.context = createWebApplicationContext(servletContext);
        }
        // 2. 配置 Root WebApplicationContext
        if (this.context instanceof ConfigurableWebApplicationContext) {
            ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
            if (!cwac.isActive()) {
                if (cwac.getParent() == null) {
                    ApplicationContext parent = loadParentContext(servletContext);
                    cwac.setParent(parent);
                }
                configureAndRefreshWebApplicationContext(cwac, servletContext);
            }
        }
        servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
        
        // 3. 保存 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 (RuntimeException | Error ex) {
    }
}

2、DispatcherServlet

類繼承關係:DispatcherServlet -> FrameworkServlet -> HttpServletBean -> HttpServlet -> Servlet

初始化 DispatcherServlet 時會調用 Servlet 的 init() 方法。spring-mvc

// HttpServletBean#init
@Override
public final void init() throws ServletException {
    // 省略...
    initServletBean();
}

// FrameworkServlet#initServletBean
@Override
protected final void initServletBean() throws ServletException {
    this.webApplicationContext = initWebApplicationContext();
    initFrameworkServlet();
}

至此終於看到 Servlet WebApplicationContext 的建立了。mvc

protected WebApplicationContext initWebApplicationContext() {
    // 1. 獲取父容器
    WebApplicationContext rootContext =
            WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    WebApplicationContext wac = null;

    // 2. 子容器已經存在則設置其父容器並啓動
    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);
            }
        }
    }

    // 3. 不存在則建立一個新子容器
    if (wac == null) {
        wac = findWebApplicationContext();
    }
    if (wac == null) {
        wac = createWebApplicationContext(rootContext);
    }

    if (!this.refreshEventReceived) {
        onRefresh(wac);
    }
    if (this.publishContext) {
        String attrName = getServletContextAttributeName();
        getServletContext().setAttribute(attrName, wac);
    }
    return wac;
}

天天用心記錄一點點。內容也許不重要,但習慣很重要!app

相關文章
相關標籤/搜索