SpringMVC之源碼分析--啓動過程

Spring MVC 核心類

類的繼承關係
Spring MVC前端控制器DispatcherServlet-->FrameworkServlet-->HttpServletBean-->HttpServlet
前端

HttpServlet

HttpServlet是Servlet規範中的核心類,實現Servlet接口,繼承此類用於處理用戶請求。web

HttpServletBean

HttpServletBean主要配置servlet中初始化參數。繼承HttpServlet,並實現無參的init()方法,用於設置在web.xml中配置的contextConfigLocation屬性,此屬性指定Spring MVC的配置文件地址,默認爲WEB-INF/[servlet-name]-servlet.xml,源碼以下:spring

/**
 * DispatcherServlet第一次加載時調用init方法
 */
@Override
public final void init() throws ServletException {
    // 省略日誌...
    // 獲取在web.xml配置的初始化參數<init-param>,並將其設置到DispatcherServlet中
    PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
    if (!pvs.isEmpty()) {
        try {
            BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
            ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
            bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
            initBeanWrapper(bw);
            bw.setPropertyValues(pvs, true);
        }
        catch (BeansException ex) {
            if (logger.isErrorEnabled()) {
                logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
            }
            throw ex;
        }
    }
    // 調用子類(FrameworkServlet)進行初始化
    // 模版方法,此方法在HttpServletBean自己是空的,可是由於調用方法的對象是DispatcherServlet
    // 因此優先在DispatcherServlet找,找不到再去父類找,最後在FrameworkServlet找到
    initServletBean();
    if (logger.isDebugEnabled()) {
        logger.debug("Servlet '" + getServletName() + "' configured successfully");
    }
}

總結HttpServletBean的做用:編程

  • 獲取web.xml的中配置DispatcherServlet的初始化參數,存放到一個參數容器ServletConfigPropertyValues中
  • 根據傳進來的this建立BeanWrapper,本質上它就是DispatcherServlet
  • 經過bw.setPropertyValues(pvs, true),把參數設置到bw(即DispatcherServlet)裏面去
  • 最後調用子類的initServletBean()

FrameworkServlet

FrameworkServlet主要建立WebApplicationContext上下文,重寫了HttpServletBean的initServletBean()方法。app

一、initServletBean

該方法只有兩句關鍵代碼,其做用一個是調用initWebApplicationContext()方法初始化WebApplicationContext上下文,另外一個是調用子類方法initFrameworkServlet()方法,源碼以下:ide

@Override
protected final void initServletBean() throws ServletException {
    getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
    // 省略日誌...
    long startTime = System.currentTimeMillis();
    try {
        // 初始化WebApplicationContext,並調用子類(DispatcherServlet)的onRefresh(wac)方法
        this.webApplicationContext = initWebApplicationContext();
        initFrameworkServlet();
    }
    catch (ServletException | RuntimeException ex) {
        this.logger.error("Context initialization failed", ex);
        throw ex;
    }
    if (this.logger.isInfoEnabled()) {
        long elapsedTime = System.currentTimeMillis() - startTime;
        this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
                elapsedTime + " ms");
    }
}

二、initWebApplicationContext

protected WebApplicationContext initWebApplicationContext() {
    // 獲取root WebApplicationContext,即web.xml中配置的listener(ContextLoaderListener)
    WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    WebApplicationContext wac = null;
    // 判斷容器是否由編程式傳入(便是否已經存在了容器實例),存在的話直接賦值給wac,給springMVC容器設置父容器
    // 最後調用刷新函數configureAndRefreshWebApplicationContext(wac),做用是把Spring MVC配置文件的配置信息加載到容器中去
    if (this.webApplicationContext != null) {
        // context上下文在構造是注入
        wac = this.webApplicationContext;
        if (wac instanceof ConfigurableWebApplicationContext) {
            ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
            if (!cwac.isActive()) {
                // context沒有被refreshed,提供一些諸如設置父context、設置應用context id等服務
                if (cwac.getParent() == null) {
                    cwac.setParent(rootContext);
                }
                configureAndRefreshWebApplicationContext(cwac);
            }
        }
    }
    // 在ServletContext中尋找是否有Spring MVC容器,初次運行是沒有的,Spring MVC初始化完畢ServletContext就有了Spring MVC容器
    if (wac == null) {
        wac = findWebApplicationContext();
    }
    // 當wac既沒有沒被編程式註冊到容器中的,也沒在ServletContext找獲得,此時就要新建一個Spring MVC容器
    if (wac == null) {
        // 若是沒有WebApplicationContext則建立
        wac = createWebApplicationContext(rootContext);
    }
    // 到這裏Spring MVC容器已經建立完畢,接着真正調用DispatcherServlet的初始化方法onRefresh(wac)
    // 此處還是模板模式的應用
    if (!this.refreshEventReceived) {
        onRefresh(wac);
    }
    // 將Spring MVC容器存放到ServletContext中去,方便下次取出來
    if (this.publishContext) {
        // Publish the context as a servlet context attribute.
        String attrName = getServletContextAttributeName();
        getServletContext().setAttribute(attrName, wac);
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
                    "' as ServletContext attribute with name [" + attrName + "]");
        }
    }
    return wac;
}

三、createWebApplicationContext

protected WebApplicationContext createWebApplicationContext(@Nullable WebApplicationContext parent) {
    return createWebApplicationContext((ApplicationContext) parent);
}
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
    Class<?> contextClass = getContextClass();
    if (this.logger.isDebugEnabled()) {
        this.logger.debug("Servlet with name '" + 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 '" + getServletName() +
                "': custom WebApplicationContext class [" + contextClass.getName() +
                "] is not of type ConfigurableWebApplicationContext");
    }
    // 實例化容器
    ConfigurableWebApplicationContext wac =
            (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
    // 設置容器環境
    wac.setEnvironment(getEnvironment());
    // 設置父容器
    wac.setParent(parent);
    // 加載Spring MVC的配置信息,如:bean注入、註解、掃描等等
    String configLocation = getContextConfigLocation();
    if (configLocation != null) {
        wac.setConfigLocation(configLocation);
    }
    // 刷新容器,根據Spring MVC配置文件完成初始化操做
    configureAndRefreshWebApplicationContext(wac);
    return wac;
}

總結FrameworkServlet的做用:函數

  • 建立Spring MVC的容器,根據配置文件實例化裏面各類bean,並將之與spring的容器進行關聯
  • 把建立出來的Spring MVC容器存放到ServletContext中
  • 經過模板方法模式調用子類DispatcherServlet的onRefresh()方法

DispatcherServlet

DispatcherServlet是Spring MVC核心,它是J2EE規範前端控制器的實現,負責攔截用戶請求,並解析請求進行轉發。學習

@Override
protected void onRefresh(ApplicationContext context) {
    initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
    initMultipartResolver(context); // 文件上傳解析
    initLocaleResolver(context); // 本地解析
    initThemeResolver(context); //主題解析
    initHandlerMappings(context); // URL請求映射
    initHandlerAdapters(context); // 初始化Controller類
    initHandlerExceptionResolvers(context); // 異常解析
    initRequestToViewNameTranslator(context); 
    initViewResolvers(context); // 視圖解析
    initFlashMapManager(context);
}

總結

  • 容器啓動時,加載web.xml部署描述文件,掃描到並找到DispatcherServlet核心控制器
  • 調用HttpServletBean的init()方法,把DispatcherServlet初始化參數設置到DispatcherServlet中,並調用子類FrameworkServlet的initServletBean()方法
  • FrameworkServlet的initServletBean()建立Spring MVC容器並初始化,而且和Spring父容器進行關聯,使得Spring MVC容器能訪問Spring容器中定義的bean,以後調用子類DispatcherServlet的onRefresh()方法
  • DispatcherServlet的onRefresh(ApplicationContext context)對DispatcherServlet的策略組件進行初始化

最後建立了qq羣方便你們交流,可掃描加入,同時也可加我qq:276420284,共同窗習、共同進步,謝謝!ui

相關文章
相關標籤/搜索