国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

SpringMVC源碼解析——DispatcherServlet初始化

這篇具有很好參考價(jià)值的文章主要介紹了SpringMVC源碼解析——DispatcherServlet初始化。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

在Spring中,ContextLoaderListener只是輔助功能,用于創(chuàng)建WebApplicationContext類型的實(shí)例,而真正的邏輯實(shí)現(xiàn)其實(shí)是在DispatcherServlet中進(jìn)行的,DispatcherServlet是實(shí)現(xiàn)Servlet接口的實(shí)現(xiàn)類。Servlet是一個(gè)JAVA編寫的程序,此程序是基于HTTP協(xié)議的,在服務(wù)端運(yùn)行的(如Tomcat),是按照Servlet規(guī)范編寫的一個(gè)JAVA類。主要是處理客戶端的請求并將其結(jié)果發(fā)送到客戶端。Servlet的生命周期是由Servlet的容器來控制的,它可以分為三個(gè)階段:初始化、運(yùn)行和銷毀。

1、初始化階段
  • Servlet容器加載Servlet類,把Servlet類的.class文件中的數(shù)據(jù)讀入到內(nèi)存中。
  • Servlet容器創(chuàng)建一個(gè)ServletConfig對象,ServletConfig對象包含了Servlet的初始化配置信息。
  • Servlet容器創(chuàng)建一個(gè)Servlet對象。
  • Servlet容器調(diào)用Servlet對象的init方法進(jìn)行初始化。
2、運(yùn)行階段

當(dāng)Servlet容器收到一個(gè)請求時(shí),Servlet容器會針對這個(gè)請求創(chuàng)建ServletRequest和ServletResponse對象,然后調(diào)用service方法。并把這兩個(gè)參數(shù)傳遞給service方法,service方法通過ServletRequest對象獲取請求的信息,并處理該請求。再通過ServletResponse對象生成這個(gè)請求的響應(yīng)結(jié)果。然后銷毀ServletRequest和ServletResponse對象,不管這個(gè)請求時(shí)GET還是POST提交的,最終這個(gè)請求都會由service來處理。

3、銷毀階段

當(dāng)web應(yīng)用被終止時(shí),Servlet容器會先調(diào)用Servlet對象的destroy方法,然后再銷毀Servlet對象,同時(shí)也會銷毀與Servlet對象相關(guān)聯(lián)的ServletConfig對象。我們可以在destroy方法的實(shí)現(xiàn)中,釋放Servlet所占用的資源,如關(guān)閉數(shù)據(jù)庫連接,關(guān)閉文件輸入輸出流等。

Servlet的框架是由兩個(gè)JAVA包組成:javax.servlet和javax.servlet.http。在javax.servlet包中定義了所有的servlet類都必須實(shí)現(xiàn)或擴(kuò)展的通用接口和類,在javax.servlet.http包中定義了采用HTTP通信協(xié)議的HttpServlet類。

servlet被設(shè)計(jì)成請求驅(qū)動,servlet的請求可能包含多個(gè)數(shù)據(jù)項(xiàng),當(dāng)web容器接受到某個(gè)servlet請求時(shí),servlet把請求封裝成一個(gè)HttpServletRequest對象,然后把對象傳給servlet的對應(yīng)的服務(wù)方法。

HTTP的請求方式包括delete、get、options、post、put和trace,在HttpServlet類中分別提供了相應(yīng)的服務(wù)方法,它們是doDelete、doGet、doOptions、doPost、doPut和doTrace。

DispatcherServlet的初始化

在servlet初始化階段會調(diào)用其init方法,所以我們首先要查看DispatcherServlet中是否重寫了init方法。DispatcherServlet類相關(guān)的結(jié)構(gòu)圖如下:

SpringMVC源碼解析——DispatcherServlet初始化,hive,hadoop,數(shù)據(jù)倉庫

我們在HttpServletBean中找到了該方法:

/**
 * 將配置參數(shù)映射到這個(gè)servlet的bean屬性,并調(diào)用子類的初始化方法。
 * @throws ServletException 如果bean屬性無效(或缺少必需的屬性),或者子類的初始化失敗。
 */
@Override
public final void init() throws ServletException {

    // 從初始化參數(shù)設(shè)置bean屬性。
    PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
    if (!pvs.isEmpty()) {
        try {
            // 將當(dāng)前的這個(gè)Servlet類轉(zhuǎn)換為一個(gè)BeanWrapper,從而能夠以Spring的方式來對init-param的值進(jìn)行注入。
            BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
            ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());

            // 注冊自定義屬性編輯器,一旦遇到Resource類型的屬性將會使用ResourceEditor進(jìn)行解析
            bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));

            // 空實(shí)現(xiàn),留給子類覆蓋
            initBeanWrapper(bw);
            bw.setPropertyValues(pvs, true);
        }
        catch (BeansException ex) {
            if (logger.isErrorEnabled()) {
                logger.error("無法設(shè)置 servlet '" + getServletName() + "' 的 bean 屬性", ex);
            }
            throw ex;
        }
    }

    // 允許子類執(zhí)行它們喜歡的任何初始化操作。
    initServletBean();
}

函數(shù)DispatcherServlet的初始化過程主要是通過將當(dāng)前的Servlet類型實(shí)例轉(zhuǎn)換為BeanWapper類型實(shí)例,以便使用Spring中提供的注入功能進(jìn)行對應(yīng)屬性的注入。這些屬性如contextAttribute、contextClass、nameSpace、contextConfigLocation等,都可以在web.xml文件中以初始化參數(shù)的方式配置在Servlet的聲明中。DispatcherServlet繼承自FrameworkServlet,F(xiàn)rameworkServlet類上包含對應(yīng)的同名屬性,Spring會保證這些參數(shù)被注入到對應(yīng)的值中。屬性注入主要是包含以下幾個(gè)步驟。

1、封裝及驗(yàn)證初始化參數(shù)

ServletConfigPropertyValues除了封裝屬性外還有對屬性驗(yàn)證的功能。

		/**
		 * 創(chuàng)建新的ServletConfigPropertyValues。
		 * @param config 我們將使用它從ServletConfig中獲取屬性值
		 * @param requiredProperties 我們需要的屬性集合,對于這些我們不能使用默認(rèn)值
		 * @throws ServletException 如果缺少任何必需的屬性
		 */
		public ServletConfigPropertyValues(ServletConfig config, Set<String> requiredProperties)
				throws ServletException {

		    Set<String> missingProps = (!CollectionUtils.isEmpty(requiredProperties) ?
		            new HashSet<>(requiredProperties) : null);

		    Enumeration<String> paramNames = config.getInitParameterNames();
		    while (paramNames.hasMoreElements()) {
		        String property = paramNames.nextElement();
		        Object value = config.getInitParameter(property);
		        addPropertyValue(new PropertyValue(property, value));
		        if (missingProps != null) {
		            missingProps.remove(property);
		        }
		    }

		    // 如果我們?nèi)匀蝗鄙賹傩?,則失敗。
		    if (!CollectionUtils.isEmpty(missingProps)) {
		        throw new ServletException(
		                "從ServletConfig初始化Servlet '" + config.getServletName() +
		                "'失敗;缺少以下必需的屬性:" +
		                StringUtils.collectionToDelimitedString(missingProps, ", "));
		    }
		}

這個(gè)函數(shù)創(chuàng)建一個(gè)ServletConfigPropertyValues對象,從給定的ServletConfig中獲取屬性值,并將其添加到PropertyValues中。如果requiredProperties中存在缺失的屬性,則拋出ServletException異常。

2、將當(dāng)前Servlet實(shí)例轉(zhuǎn)化成BeanWapper實(shí)例

PropertyAccessorFactory.forBeanPropertyAccess是Spring中提供的工具方法,主要是用于將指定實(shí)例轉(zhuǎn)化為Spring中可以處理的BeanWapper類型的實(shí)例。

3、注冊相對于Resource的屬性編輯器

這里使用屬性編輯器的目的是在對當(dāng)前實(shí)例(DispatcherServlet)屬性注入過程中一旦遇到Resource類型的屬性就會使用ResourceEditor去解析。

4、屬性注入

BeanWapper為Spring中的方法,支持Spring的自動注入。其實(shí)我們最常用的屬性注入無非是contextAttribute、contextClass、nameSpace、contextConfigLocation等。

5、ServletBean的初始化

在ContextLoaderListener加載的時(shí)候已經(jīng)創(chuàng)建了WebApplicationContext實(shí)例,而在這個(gè)函數(shù)中最重要的就是對這個(gè)實(shí)例進(jìn)行進(jìn)一步的補(bǔ)充初始化。

繼續(xù)查看initServletBean(),父類覆蓋了HttpServletBean中的initServletBean函數(shù),源碼如下:

/**
 * 重寫HttpServletBean類的方法,在設(shè)置完所有bean屬性后調(diào)用。創(chuàng)建該Servlet的Web應(yīng)用上下文。
 */
@Override
protected final void initServletBean() throws ServletException {
    getServletContext().log("初始化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("上下文初始化失敗", ex);
        throw ex;
    }

    if (logger.isDebugEnabled()) {
        String value = this.enableLoggingRequestDetails ?
                "可能導(dǎo)致潛在敏感數(shù)據(jù)的不安全記錄顯示" :
                "已掩蓋以防止對潛在敏感數(shù)據(jù)的不安全記錄";
        logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
                "': 請求參數(shù)和標(biāo)頭將被 " + value);
    }

    if (logger.isInfoEnabled()) {
        logger.info("完成初始化需要 " + (System.currentTimeMillis() - startTime) + " ms");
    }
}

這個(gè)函數(shù)會調(diào)用initWebApplicationContext用于創(chuàng)建并初始化WebApplicationContext實(shí)例,initFrameworkServlet()函數(shù)不做任何實(shí)現(xiàn),可以在子類中進(jìn)行擴(kuò)展。

WebApplicationContext的初始化

initWebApplicationContext函數(shù)的主要工作就是創(chuàng)建并刷新WebApplicationContext實(shí)例并對Servlet功能所使用的變量進(jìn)行初始化。initWebApplicationContext函數(shù)的源碼如下:

	/**
	 * 初始化并發(fā)布該servlet的WebApplicationContext。
	 * <p>實(shí)際創(chuàng)建上下文的工作委托給{@link #createWebApplicationContext}方法。
	 * 子類可以重寫該方法。
	 * @return WebApplicationContext實(shí)例
	 * @see #FrameworkServlet(WebApplicationContext)
	 * @see #setContextClass
	 * @see #setContextConfigLocation
	 */
	protected WebApplicationContext initWebApplicationContext() {
	    WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
	    WebApplicationContext wac = null;
	    
	    if (this.webApplicationContext != null) {
	        // 在構(gòu)造函數(shù)中注入了上下文實(shí)例 -> 使用它
	        wac = this.webApplicationContext;
	        if (wac instanceof ConfigurableWebApplicationContext cwac && !cwac.isActive()) {
	            // 上下文尚未刷新 -> 提供服務(wù),如設(shè)置父上下文、設(shè)置applicationContextId等
	            if (cwac.getParent() == null) {
	                // 注入父上下文時(shí)未指定明確的父上下文 -> 將根application上下文(如果有的話)設(shè)置為父上下文
	                cwac.setParent(rootContext);
	            }
                // 配置并刷新WebApplicationContext實(shí)例
	            configureAndRefreshWebApplicationContext(cwac);
	        }
	    }
	    
	    if (wac == null) {
	        // 在構(gòu)造函數(shù)中沒有注入上下文實(shí)例 -> 檢查servlet context中是否存在一個(gè)注冊的上下文。
	        // 如果存在,則假定父上下文(如果有的話)已經(jīng)設(shè)置,并且用戶已經(jīng)進(jìn)行了任何初始化,例如設(shè)置上下文id
	        wac = findWebApplicationContext();
	    }
	    
	    if (wac == null) {
	        // 為這個(gè)servlet沒有定義上下文實(shí)例 -> 創(chuàng)建一個(gè)本地上下文
	        wac = createWebApplicationContext(rootContext);
	    }
	    
	    if (!this.refreshEventReceived) {
	        // 該上下文不是支持刷新的ConfigurableApplicationContext或者在構(gòu)造函數(shù)中注入的上下文已經(jīng)刷新 ->
	        // 在這里手動觸發(fā)onRefresh方法。
	        synchronized (this.onRefreshMonitor) {
	            onRefresh(wac);
	        }
	    }
	    
	    if (this.publishContext) {
	        // 將上下文發(fā)布為servlet context attribute。
	        String attrName = getServletContextAttributeName();
	        getServletContext().setAttribute(attrName, wac);
	    }
	    
	    return wac;
	}

這個(gè)函數(shù)用于初始化和發(fā)布WebApplicationContext。它首先從servlet上下文中獲取根應(yīng)用程序上下文,然后根據(jù)需要創(chuàng)建和配置應(yīng)用程序上下文實(shí)例。如果已經(jīng)存在一個(gè)應(yīng)用程序上下文實(shí)例,則直接使用它。如果沒有,則根據(jù)需要創(chuàng)建一個(gè)本地應(yīng)用程序上下文。最后,將應(yīng)用程序上下文發(fā)布為servlet上下文屬性,并返回該上下文實(shí)例。

對于initWebApplicationContext函數(shù)中的初始化工作主要包含幾個(gè)部分。

1、通過構(gòu)造函數(shù)的注入對WebApplicationContext進(jìn)行初始化

當(dāng)進(jìn)入initWebApplicationContext函數(shù)后通過判斷this.webApplicationContext !=null后,便可以確定this.webApplicationContext是否是通過構(gòu)造函數(shù)來初始化的。

2、通過contextAttribute進(jìn)行初始化

通過在web.xml文件中配置的servlet參數(shù)contextAttribute來查找ServletContext中對應(yīng)的屬性,默認(rèn)為WebApplicationContext.class .getName()+".ROOT"。也就是在ContextLoaderListener加載時(shí)會創(chuàng)建WebApplicationContext實(shí)例,并將實(shí)例以WebApplicationContext.class.getName()+".ROOT"為key放入ServletContext中,當(dāng)然我們也可以重寫初始化邏輯使用自己創(chuàng)建的WebApplicationContext,并在servlet的配置中通過初始化參數(shù)contextAttribute指定key。

	/**
	 * 從配置了名稱的`ServletContext`屬性中獲取一個(gè)`WebApplicationContext`。
	 * 在該 servlet 初始化(或調(diào)用)之前,`WebApplicationContext`必須已經(jīng)加載并存儲在 `ServletContext` 中。
	 * <p>子類可以覆蓋此方法以提供不同的`WebApplicationContext`檢索策略。
	 * @return 該 servlet 的`WebApplicationContext`,如果未找到則返回`null`
	 * @see #getContextAttribute()
	 */
	@Nullable
	protected WebApplicationContext findWebApplicationContext() {
	    String attrName = getContextAttribute();
	    if (attrName == null) {
	        return null;
	    }
	    WebApplicationContext wac = WebApplicationContextUtils
            .getWebApplicationContext(getServletContext(), attrName);
	    if (wac == null) {
	        throw new IllegalStateException("未找到WebApplicationContext:初始化器未注冊?");
	    }
	    return wac;
	}
3、重寫創(chuàng)建WebApplicationContext實(shí)例

如果以上兩種方式都沒有獲取到WebApplicationContext實(shí)例,只能重寫創(chuàng)建新的實(shí)例了。

	/**
	 * 創(chuàng)建用于該servlet的WebApplicationContext,可以是默認(rèn)的
	 * {@link org.springframework.web.context.support.XmlWebApplicationContext}
	 * 或者如果設(shè)置了的話,可以是一個(gè)自定義的上下文類(通過
	 * {@link #setContextClass 設(shè)置})。
	 * 代理到#createWebApplicationContext(ApplicationContext)方法。
	 * @param parent 要使用的父WebApplicationContext,如果無,則傳入{@code null}
	 * @return 用于該servlet的WebApplicationContext
	 * @see org.springframework.web.context.support.XmlWebApplicationContext
	 * @see #createWebApplicationContext(ApplicationContext)
	 */
	protected WebApplicationContext createWebApplicationContext(@Nullable WebApplicationContext parent) {
	    return createWebApplicationContext((ApplicationContext) parent);
	}

/**
 * 創(chuàng)建本servlet的WebApplicationContext,可以是一個(gè)默認(rèn)的XmlWebApplicationContext或者是一個(gè)自定義的上下文類(通過setContextClass方法設(shè)置)。
 * <p>此實(shí)現(xiàn)期望自定義上下文實(shí)現(xiàn)ConfigurableWebApplicationContext接口。可以在子類中重寫。
 * <p>請不要忘記將此servlet實(shí)例作為創(chuàng)建的上下文的應(yīng)用監(jiān)聽器注冊(以便觸發(fā)它的onRefresh回調(diào)),并在返回上下文實(shí)例之前調(diào)用ConfigurableApplicationContext的refresh方法。
 * @param parent 要使用的父級ApplicationContext,如果無父級則為null
 * @return 本servlet的WebApplicationContext
 * @see org.springframework.web.context.support.XmlWebApplicationContext
 */
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
    // 讀取servlet的初始化參數(shù)contextClass,如果沒有配置默認(rèn)為XmlWebApplicationContext.class
    Class<?> contextClass = getContextClass();
    if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
        throw new ApplicationContextException(
                "在名稱為'" + getServletName() +
                "'的servlet中發(fā)生致命的初始化錯誤:自定義WebApplicationContext類[" + contextClass.getName() +
                "]不是ConfigurableWebApplicationContext類型的");
    }

    // 通過反射方式實(shí)例化contextClass
    ConfigurableWebApplicationContext wac =
            (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

    wac.setEnvironment(getEnvironment());

    // parent為在ContextLoaderListener中創(chuàng)建的實(shí)例
    // 在ContextLoaderListener加載的時(shí)候初始化的WebApplicationContext類型的實(shí)例
    wac.setParent(parent);

    // 獲取contextConfigLocation屬性,配置在servlet初始化參數(shù)中
    String configLocation = getContextConfigLocation();
    if (configLocation != null) {
        wac.setConfigLocation(configLocation);
    }

    //初始化Spring環(huán)境,包括加載配置文件等
    configureAndRefreshWebApplicationContext(wac);

    return wac;
}
4、configureAndRefreshWebApplicationContext

無論是通過構(gòu)造函數(shù)注入還是單獨(dú)創(chuàng)建,都會調(diào)用configureAndRefreshWebApplicationContext方法來對已經(jīng)創(chuàng)建的WebApplicationContext實(shí)例進(jìn)行配置及刷新,源碼如下:

	/**
	 * 配置并刷新Web應(yīng)用上下文
	 * 
	 * @param wac 可配置的Web應(yīng)用上下文
	 */
	protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
	    if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
	        // 應(yīng)用上下文id仍然設(shè)置為其默認(rèn)值
	        // -> 基于可用信息分配一個(gè)更有用的id
	        if (this.contextId != null) {
	            wac.setId(this.contextId);
	        } else {
	            // 生成默認(rèn)id...
	            wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
	                    ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
	        }
	    }

	    wac.setServletContext(getServletContext());
	    wac.setServletConfig(getServletConfig());
	    wac.setNamespace(getNamespace());
	    wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

	    // 當(dāng)上下文刷新時(shí),wac環(huán)境的#initPropertySources方法將被調(diào)用;這里提前調(diào)用以確保servlet屬性源在以下下方的post-processing或初始化之前可用
	    ConfigurableEnvironment env = wac.getEnvironment();
	    if (env instanceof ConfigurableWebEnvironment cwe) {
	        cwe.initPropertySources(getServletContext(), getServletConfig());
	    }

	    postProcessWebApplicationContext(wac);
	    applyInitializers(wac);
	    wac.refresh();
	}

個(gè)函數(shù)用于配置和刷新Web應(yīng)用上下文。首先根據(jù)上下文的id是否與默認(rèn)值相同來為其設(shè)置一個(gè)更有用的id。然后設(shè)置上下文的servletContext、servletConfig和命名空間,并添加一個(gè)監(jiān)聽器。接下來,通過調(diào)用wac環(huán)境的initPropertySources方法來初始化屬性源。最后,調(diào)用postProcessWebApplicationContext和applyInitializers方法對上下文進(jìn)行后處理和初始化,并刷新上下文。

5、刷新WebApplicationContext

onRefresh是FrameworkServlet類中提供的模板方法,在其子類DispatcherServlet中進(jìn)行了重寫,主要用于刷新Spring在Web功能實(shí)現(xiàn)中所必須使用的全局變量。DispatcherServlet中onRefresh函數(shù)的源碼如下:

/**
 * 這個(gè)方法調(diào)用了 {@link #initStrategies} 方法。
 */
@Override
protected void onRefresh(ApplicationContext context) {
    initStrategies(context);
}

/**
 * 初始化這個(gè) servlet 使用的策略對象。
 * <p>對于需要初始化更多策略對象的情況,可以被子類重寫。
 */
protected void initStrategies(ApplicationContext context) {
    initMultipartResolver(context);
    initLocaleResolver(context);
    initThemeResolver(context);
    initHandlerMappings(context);
    initHandlerAdapters(context);
    initHandlerExceptionResolvers(context);
    initRequestToViewNameTranslator(context);
    initViewResolvers(context);
    initFlashMapManager(context);
}
初始化MultipartResolver

在Spring中,MultipartResolver主要是處理文件上傳。默認(rèn)情況下,Spring是沒有Multipart處理的,因?yàn)楹芏嚅_發(fā)者想要自己處理它們。如果想使用Spring的Multipart,則需要在Web應(yīng)用的上下文中添加Multipart解析器。這樣,每個(gè)請求就會被檢查是否包含Multipart。然而如果請求中包含Multipart,那么上下文中定義的MultipartResolver就會解析它,這樣請求中的Multipart屬性就會想其他屬性一樣被處理。常用配置如下:

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="maximumFileSize">
        <value>100000</value>
    </property>
</bean>

那么MultipartResolver就是在initMultipartResolver中被加入到DispatcherServlet中的。

	/**
	 * 初始化用于此類的 MultipartResolver。
	 * <p>如果在 BeanFactory 中未定義給定名稱的 bean,則不提供多部分處理。
	 */
	private void initMultipartResolver(ApplicationContext context) {
	    try {
	        this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
	        if (logger.isTraceEnabled()) {
	            logger.trace("檢測到 " + this.multipartResolver);
	        }
	        else if (logger.isDebugEnabled()) {
	            logger.debug("檢測到 " + this.multipartResolver.getClass().getSimpleName());
	        }
	    }
	    catch (NoSuchBeanDefinitionException ex) {
	        // 默認(rèn)情況下沒有 multipart 解析器。
	        this.multipartResolver = null;
	        if (logger.isTraceEnabled()) {
	            logger.trace("未聲明 '" + MULTIPART_RESOLVER_BEAN_NAME + "' multipart 解析器");
	        }
	    }
	}

因?yàn)橹暗牟襟E已經(jīng)完成了Spring中配置文件的解析,所以在這里只要在配置文件注冊過都可以通過ApplicationContext提供的getBean方法來直接獲取對應(yīng)的bean,進(jìn)而初始化MultipartResolver中的multipartResolver變量。

初始化LocaleResolver

在Spring的國際化配置中一共有三種使用方式。

  • 基于URL參數(shù)的配置。通過URL參數(shù)來控制國際化,而提供這個(gè)功能的就是AcceptHeaderLocaleResolver,默認(rèn)的參數(shù)名為local,注意大小寫。
  • 基于session的配置。它通過檢驗(yàn)用戶會話中預(yù)置的屬性來解析區(qū)域。最常用的是根據(jù)用戶本次會話過程中的語言設(shè)定決定語言中來。
  • 基于cookie的國際配置。CookieLocaleResolver用于通過瀏覽器的cookie設(shè)置Locale對象。這種策略在應(yīng)用程序中不支持會話或者狀態(tài)必須保存在客戶端有用。

這三種方式都可以解決國際化問題,但是對于LocaleResolver的使用基礎(chǔ)是在DispatcherServlet中的初始化。

	/**
	 * 初始化該類使用的LocaleResolver。
	 * <p>如果BeanFactory中沒有給定名稱的bean,將默認(rèn)使用AcceptHeaderLocaleResolver。
	 */
	private void initLocaleResolver(ApplicationContext context) {
	    try {
	        this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
	        if (logger.isTraceEnabled()) {
	            logger.trace("檢測到 " + this.localeResolver);
	        }
	        else if (logger.isDebugEnabled()) {
	            logger.debug("檢測到 " + this.localeResolver.getClass().getSimpleName());
	        }
	    }
	    catch (NoSuchBeanDefinitionException ex) {
	        // 需要使用默認(rèn)的LocaleResolver
	        this.localeResolver = getDefaultStrategy(context, LocaleResolver.class);
	        if (logger.isTraceEnabled()) {
	            logger.trace("沒有LocaleResolver '" + LOCALE_RESOLVER_BEAN_NAME +
	                         "': 使用默認(rèn) [" + this.localeResolver.getClass().getSimpleName() + "]");
	        }
	    }
	}

提取配置文件中設(shè)置的LocaleResolver來初始化DispatcherServlet中的localeResolver屬性。

初始化ThemeResolver

initThemeResolver未來會被遺棄,這里不做詳細(xì)介紹,只是簡單的展示源碼。

/**
 * 初始化由該類使用的ThemeResolver。
 * 如果BeanFactory中沒有給定名稱的bean定義此命名空間,默認(rèn)為FixedThemeResolver。
 */
@Deprecated
private void initThemeResolver(ApplicationContext context) {
    try {
        this.themeResolver = context.getBean(THEME_RESOLVER_BEAN_NAME, ThemeResolver.class);
        if (logger.isTraceEnabled()) {
            logger.trace("檢測到 " + this.themeResolver);
        }
        else if (logger.isDebugEnabled()) {
            logger.debug("檢測到 " + this.themeResolver.getClass().getSimpleName());
        }
    }
    catch (NoSuchBeanDefinitionException ex) {
        // 需要使用默認(rèn)策略
        this.themeResolver = getDefaultStrategy(context, ThemeResolver.class);
        if (logger.isTraceEnabled()) {
            logger.trace("沒有ThemeResolver '" + THEME_RESOLVER_BEAN_NAME +
                    "': 使用默認(rèn) [" + this.themeResolver.getClass().getSimpleName() + "]");
        }
    }
}
初始化HandlerMappings

當(dāng)客戶端發(fā)出Request時(shí)DispatcherServlet會將Request提交給,然后HandlerMapping根據(jù)WebApplicationContext的配置來回傳給DispatcherServlet相應(yīng)的Controller。

在基于SpringMVC的Web應(yīng)用中,可以為DispatcherServlet提供多個(gè)HandlerMapping供其應(yīng)用。DispatcherServlet在選用HandlerMapping的過程中,將根據(jù)我們所指定的一些列HandlerMapping的優(yōu)先級進(jìn)行排序,然后優(yōu)先使用優(yōu)先級在前的HandlerMapping。如果當(dāng)前的HandlerMapping能夠返回可用的Handler,DispatcherServlet則使用當(dāng)前返回的Handler來進(jìn)行Web請求的處理,而不再繼續(xù)詢問其他的HandlerMapping。否則,DispatcherServlet將繼續(xù)按照各個(gè)HandlerMapping的優(yōu)先級進(jìn)行詢問,直到獲取一個(gè)可用的Handler為止。初始化配置的源碼如下:

/**
 * 初始化用于此類的HandlerMappings。
 * <p>如果BeanFactory中未定義此命名空間的HandlerMapping bean,默認(rèn)為BeanNameUrlHandlerMapping。
 */
private void initHandlerMappings(ApplicationContext context) {
    this.handlerMappings = null;

    if (this.detectAllHandlerMappings) {
        // 在ApplicationContext中查找所有HandlerMappings,包括祖先上下文。
        Map<String, HandlerMapping> matchingBeans =
                BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
        if (!matchingBeans.isEmpty()) {
            this.handlerMappings = new ArrayList<>(matchingBeans.values());
            // 我們保持HandlerMappings的排序。
            AnnotationAwareOrderComparator.sort(this.handlerMappings);
        }
    }
    else {
        try {
            HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
            this.handlerMappings = Collections.singletonList(hm);
        }
        catch (NoSuchBeanDefinitionException ex) {
            // 忽略,之后我們將添加一個(gè)默認(rèn)HandlerMapping。
        }
    }

    // 如果找不到其他HandlerMapping,則通過注冊默認(rèn)HandlerMapping來確保至少有一個(gè)HandlerMapping。
    if (this.handlerMappings == null) {
        this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
        if (logger.isTraceEnabled()) {
            logger.trace("未聲明servlet '" + getServletName() +
                    "': 使用DispatcherServlet.properties中的默認(rèn)策略");
        }
    }

    for (HandlerMapping mapping : this.handlerMappings) {
        if (mapping.usesPathPatterns()) {
            this.parseRequestPath = true;
            break;
        }
    }
}

這個(gè)Java函數(shù)用于初始化HandlerMappings,根據(jù)配置從ApplicationContext中獲取所有的HandlerMapping實(shí)例,并按照指定的順序排序。如果沒有定義HandlerMapping,則使用默認(rèn)的BeanNameUrlHandlerMapping。最后,根據(jù)找到的HandlerMapping實(shí)例設(shè)置一些屬性值。如果只期望SpringMVC加載指定的HandlerMapping時(shí),可以修改web.xml中的DispatcherServlet的初始參數(shù),將detectAllHandlerMappings設(shè)置為false,此時(shí)SpringMVC將會查找名為“handlerMapping”的bean,并作為當(dāng)前系統(tǒng)中唯一的HandlerMapping。

初始化HandlerAdapters

該步驟適用于初始化適配器,源碼如下:

/**
 * 初始化該類使用的HandlerAdapters。
 * <p>如果該命名空間的BeanFactory中沒有定義HandlerAdapter Bean,則默認(rèn)使用SimpleControllerHandlerAdapter。
 */
private void initHandlerAdapters(ApplicationContext context) {
    this.handlerAdapters = null;

    if (this.detectAllHandlerAdapters) {
        // 在ApplicationContext中,包括祖先上下文,查找所有的HandlerAdapters。
        Map<String, HandlerAdapter> matchingBeans =
                BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
        if (!matchingBeans.isEmpty()) {
            this.handlerAdapters = new ArrayList<>(matchingBeans.values());
            // 我們按排序順序保存HandlerAdapters。
            AnnotationAwareOrderComparator.sort(this.handlerAdapters);
        }
    } else {
        try {
            HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
            this.handlerAdapters = Collections.singletonList(ha);
        } catch (NoSuchBeanDefinitionException ex) {
            // 忽略,稍后添加默認(rèn)HandlerAdapter。
        }
    }

    // 確保我們至少有一些HandlerAdapters,如果找不到其他HandlerAdapters,則注冊默認(rèn)HandlerAdapters。
    if (this.handlerAdapters == null) {
        this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
        if (logger.isTraceEnabled()) {
            logger.trace("對于servlet '" + getServletName() + "'沒有聲明HandlerAdapters:使用DispatcherServlet.properties中的默認(rèn)策略");
        }
    }
}

同樣在初始化的過程中涉及了一個(gè)變量detectAllHandlerAdapters,和detectAllHandlerMappings作用很相似,只不過作用對象是HandlerAdapter??梢酝ㄟ^修改web.xml中的DispatcherServlet的初始參數(shù),將detectAllHandlerAdapters設(shè)置為false,使得SpringMVC查找bean名稱為“handlerAdapter”的HandlerAdapter實(shí)例。

作為總控制器的派遣servlet通過處理器映射得到處理器后,會輪詢處理器適配器模板,查找能夠處理當(dāng)前HTTP請求的處理器適配器的實(shí)現(xiàn),處理器適配器模塊根據(jù)處理器映射返回的處理器類型,例如簡單的適配器類型、注解控制器類型或者遠(yuǎn)程調(diào)用處理器類型,來選擇一個(gè)適當(dāng)?shù)奶幚砥鬟m配器的實(shí)現(xiàn),從而適配當(dāng)前的HTTP請求。

  • HTTP請求處理器適配器(HttpRequestHandlerAdapter),僅支持對HTTP請求處理器的適配,它簡單地將HTTP請求對象和相應(yīng)對象傳遞給HTTP請求處理器的實(shí)現(xiàn),它并不需要返回值,它主要是基于HTTP的遠(yuǎn)程調(diào)用的實(shí)現(xiàn)上。
  • 簡單控制處理器適配器(SimpleControllerHandlerAdapter),這個(gè)實(shí)現(xiàn)類將HTTP請求適配到一個(gè)控制器的實(shí)現(xiàn)進(jìn)行處理。這里控制器的實(shí)現(xiàn)是一個(gè)簡單的控制器接口的實(shí)現(xiàn)。簡單控制處理器適配器被設(shè)計(jì)成一個(gè)框架類的實(shí)現(xiàn),不需要被改寫,客戶化的業(yè)務(wù)邏輯通常是在控制器接口的實(shí)現(xiàn)類中實(shí)現(xiàn)的。
  • 注解方式處理器適配器(AnnotationMethodHandlerAdapter),這個(gè)類的實(shí)現(xiàn)是基于注解的實(shí)現(xiàn),它需要結(jié)合注解的方式映射和注解方法處理器協(xié)同工作。它通過解析聲明在注解控制器的請求映射信息來解析相應(yīng)的處理器方法來處理當(dāng)前的HTTP請求。在處理的過程中,它通過反射來發(fā)現(xiàn)探測處理器方法的參數(shù),調(diào)用處理器方法,并且映射返回值到模型和控制器對象,最后返回模型和控制器對象給作為主控制器的派遣器servlet。
初始化HandlerExceptionResolvers

基于HandlerExceptionResolver接口的異常處理,使用這種方法只需要實(shí)現(xiàn)resolveException方法,該方法返回一個(gè)ModelAndView對象,在方法內(nèi)部對異常的類型進(jìn)行判斷,然后嘗試生成對應(yīng)的ModelAndView對象,如果該方法返回了null,則Spring會繼續(xù)尋找其他實(shí)現(xiàn)了HandlerExceptionResolver接口的bean。換句話說,Spring會搜索所有注冊在其環(huán)境中實(shí)現(xiàn)了HandlerExceptionResolver接口bean,逐個(gè)執(zhí)行,直到返回一個(gè)ModelAndView對象。

public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionResolver {
	/**
	 * 檢查是否應(yīng)該應(yīng)用此解析器(即,如果提供的處理器與配置的
	 * {@linkplain #setMappedHandlers 處理器} 或 {@linkplain #setMappedHandlerClasses 處理器類} 中的任何一個(gè)匹配)
	 * 然后委托給 {@link #doResolveException} 模板方法。
	 */
	@Override
	@Nullable
	public ModelAndView resolveException(
			HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {

		if (shouldApplyTo(request, handler)) {
			prepareResponse(ex, response);
			ModelAndView result = doResolveException(request, response, handler, ex);
			if (result != null) {
				// 在 warn 日志啟用時(shí)打印調(diào)試消息。
				if (logger.isDebugEnabled() && (this.warnLogger == null || !this.warnLogger.isWarnEnabled())) {
					logger.debug(buildLogMessage(ex, request) + (result.isEmpty() ? "" : " to " + result));
				}
				// 在 logException 方法中顯式配置的 warn 日志器。
				logException(ex, request);
			}
			return result;
		}
		else {
			return null;
		}
	}

	@Override
	@Nullable
	protected ModelAndView doResolveException(
			HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {

		try {
			// ErrorResponse exceptions that expose HTTP response details
			if (ex instanceof ErrorResponse errorResponse) {
				ModelAndView mav = null;
				if (ex instanceof HttpRequestMethodNotSupportedException theEx) {
					mav = handleHttpRequestMethodNotSupported(theEx, request, response, handler);
				}
				else if (ex instanceof HttpMediaTypeNotSupportedException theEx) {
					mav = handleHttpMediaTypeNotSupported(theEx, request, response, handler);
				}
				else if (ex instanceof HttpMediaTypeNotAcceptableException theEx) {
					mav = handleHttpMediaTypeNotAcceptable(theEx, request, response, handler);
				}
				else if (ex instanceof MissingPathVariableException theEx) {
					mav = handleMissingPathVariable(theEx, request, response, handler);
				}
				else if (ex instanceof MissingServletRequestParameterException theEx) {
					mav = handleMissingServletRequestParameter(theEx, request, response, handler);
				}
				else if (ex instanceof MissingServletRequestPartException theEx) {
					mav = handleMissingServletRequestPartException(theEx, request, response, handler);
				}
				else if (ex instanceof ServletRequestBindingException theEx) {
					mav = handleServletRequestBindingException(theEx, request, response, handler);
				}
				else if (ex instanceof MethodArgumentNotValidException theEx) {
					mav = handleMethodArgumentNotValidException(theEx, request, response, handler);
				}
				else if (ex instanceof HandlerMethodValidationException theEx) {
					mav = handleHandlerMethodValidationException(theEx, request, response, handler);
				}
				else if (ex instanceof NoHandlerFoundException theEx) {
					mav = handleNoHandlerFoundException(theEx, request, response, handler);
				}
				else if (ex instanceof NoResourceFoundException theEx) {
					mav = handleNoResourceFoundException(theEx, request, response, handler);
				}
				else if (ex instanceof AsyncRequestTimeoutException theEx) {
					mav = handleAsyncRequestTimeoutException(theEx, request, response, handler);
				}

				return (mav != null ? mav :
						handleErrorResponse(errorResponse, request, response, handler));
			}

			// Other, lower level exceptions

			if (ex instanceof ConversionNotSupportedException theEx) {
				return handleConversionNotSupported(theEx, request, response, handler);
			}
			else if (ex instanceof TypeMismatchException theEx) {
				return handleTypeMismatch(theEx, request, response, handler);
			}
			else if (ex instanceof HttpMessageNotReadableException theEx) {
				return handleHttpMessageNotReadable(theEx, request, response, handler);
			}
			else if (ex instanceof HttpMessageNotWritableException theEx) {
				return handleHttpMessageNotWritable(theEx, request, response, handler);
			}
			else if (ex instanceof MethodValidationException theEx) {
				return handleMethodValidationException(theEx, request, response, handler);
			}
			else if (ex instanceof BindException theEx) {
				return handleBindException(theEx, request, response, handler);
			}
		}
		catch (Exception handlerEx) {
			if (logger.isWarnEnabled()) {
				logger.warn("Failure while trying to resolve exception [" + ex.getClass().getName() + "]", handlerEx);
			}
		}

		return null;
	}

/**
 * 處理 {@link ErrorResponse} 異常。
 * <p>默認(rèn)實(shí)現(xiàn)將狀態(tài)和頭信息設(shè)置為從 {@code ErrorResponse} 獲取到的值。
 * 如果可用,{@link ProblemDetail#getDetail()} 將用作
 * {@link HttpServletResponse#sendError(int, String)} 的消息。
 * @param errorResponse 需要處理的異常
 * @param request 當(dāng)前的 HTTP 請求
 * @param response 當(dāng)前的 HTTP 響應(yīng)
 * @param handler 執(zhí)行的處理器
 * @return 一個(gè)空的 {@code ModelAndView},表示異常已處理
 * @throws IOException 可能從 {@link HttpServletResponse#sendError} 拋出
 * @since 6.0
 */
protected ModelAndView handleErrorResponse(ErrorResponse errorResponse,
		HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {

	if (!response.isCommitted()) {
		HttpHeaders headers = errorResponse.getHeaders();
		headers.forEach((name, values) -> values.forEach(value -> response.addHeader(name, value)));

		int status = errorResponse.getStatusCode().value();
		String message = errorResponse.getBody().getDetail();
		if (message != null) {
			response.sendError(status, message);
		}
		else {
			response.sendError(status);
		}
	}
	else {
		logger.warn("忽略異常,響應(yīng)已提交。: " + errorResponse);
	}

	return new ModelAndView();
}

}
初始化RequestToViewNameTranslator

當(dāng)Controller處理器方法沒有返回一個(gè)View對象或邏輯視圖名稱,并且在該方法中沒有直接往Response的輸出流里面寫數(shù)據(jù)的時(shí)候,Spring就會采用約定好的方式提供一個(gè)邏輯視圖名稱。這個(gè)邏輯視圖名稱是通過Spring定義的RequestToViewNameTranslator接口的getViewName方法來實(shí)現(xiàn)的。首先看一下初始化RequestToViewNameTranslator的源碼如下:

	/**
	 * 初始化該servlet實(shí)例使用的RequestToViewNameTranslator。
	 * <p>如果沒有配置實(shí)現(xiàn),則默認(rèn)使用DefaultRequestToViewNameTranslator。
	 */
	private void initRequestToViewNameTranslator(ApplicationContext context) {
	    try {
	        this.viewNameTranslator = context.getBean(REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME, RequestToViewNameTranslator.class);
	        if (logger.isTraceEnabled()) {
	            logger.trace("檢測到 " + this.viewNameTranslator.getClass().getSimpleName());
	        }
	        else if (logger.isDebugEnabled()) {
	            logger.debug("檢測到 " + this.viewNameTranslator);
	        }
	    }
	    catch (NoSuchBeanDefinitionException ex) {
	        // 需要使用默認(rèn)實(shí)現(xiàn)
	        this.viewNameTranslator = getDefaultStrategy(context, RequestToViewNameTranslator.class);
	        if (logger.isTraceEnabled()) {
	            logger.trace("未檢測到RequestToViewNameTranslator '" + REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME +
	                         "': 使用默認(rèn) [" + this.viewNameTranslator.getClass().getSimpleName() + "]");
	        }
	    }
	}

Spring已經(jīng)給我們提供了一個(gè)它自己的實(shí)現(xiàn),就是DefaultRequestToViewNameTranslator,源碼如下:

public class DefaultRequestToViewNameTranslator implements RequestToViewNameTranslator {

	private static final String SLASH = "/";


	private String prefix = "";

	private String suffix = "";

	private String separator = SLASH;

	private boolean stripLeadingSlash = true;

	private boolean stripTrailingSlash = true;

	private boolean stripExtension = true;

	/**
	 * Translates the request URI of the incoming {@link HttpServletRequest}
	 * into the view name based on the configured parameters.
	 * @throws IllegalArgumentException if neither a parsed RequestPath, nor a
	 * String lookupPath have been resolved and cached as a request attribute.
	 * @see ServletRequestPathUtils#getCachedPath(ServletRequest)
	 * @see #transformPath
	 */
	@Override
	public String getViewName(HttpServletRequest request) {
		String path = ServletRequestPathUtils.getCachedPathValue(request);
		return (this.prefix + transformPath(path) + this.suffix);
	}

	/**
	 * Transform the request URI (in the context of the webapp) stripping
	 * slashes and extensions, and replacing the separator as required.
	 * @param lookupPath the lookup path for the current request,
	 * as determined by the UrlPathHelper
	 * @return the transformed path, with slashes and extensions stripped
	 * if desired
	 */
	@Nullable
	protected String transformPath(String lookupPath) {
		String path = lookupPath;
		if (this.stripLeadingSlash && path.startsWith(SLASH)) {
			path = path.substring(1);
		}
		if (this.stripTrailingSlash && path.endsWith(SLASH)) {
			path = path.substring(0, path.length() - 1);
		}
		if (this.stripExtension) {
			path = StringUtils.stripFilenameExtension(path);
		}
		if (!SLASH.equals(this.separator)) {
			path = StringUtils.replace(path, SLASH, this.separator);
		}
		return path;
	}

}
初始化ViewResolvers

在SpringMVC中,當(dāng)Controller將請求處理結(jié)果放入到ModelAndView中以后,DispatcherServlet會根據(jù)ModelAndView選擇合適的視圖進(jìn)行渲染。ViewResolver接口定義了resolveViewName方法,根據(jù)viewName創(chuàng)建合適類型的View實(shí)現(xiàn)。初始化ViewResolvers的源碼如下:

	/**
	 * 初始化用于此類的 ViewResolvers。
	 * <p如果在此命名空間的 BeanFactory 中未定義 ViewResolver 象,我們將默認(rèn)使用 InternalResourceViewResolver。
	 */
	private void initViewResolvers(ApplicationContext context) {
	    this.viewResolvers = null;

	    if (this.detectAllViewResolvers) {
	        // 在 ApplicationContext 中查找所有命名空間的 ViewResolvers,包括祖先層級。
	        Map<String, ViewResolver> matchingBeans =
	                BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);
	        if (!matchingBeans.isEmpty()) {
	            this.viewResolvers = new ArrayList<>(matchingBeans.values());
	            // 我們按升序排列 ViewResolvers。
	            AnnotationAwareOrderComparator.sort(this.viewResolvers);
	        }
	    } else {
	        try {
	           	ViewResolver vr = context.getBean(VIEW_RESOLVER_BEAN_NAME, ViewResolver.class);
	            this.viewResolvers = Collections.singletonList(vr);
	        }
	        catch (NoSuchBeanDefinitionException ex) {
	            // 忽略,后面我們會添加一個(gè)默認(rèn)的 ViewResolver。
	        }
	    }

	    // 如果沒有找到其他 ViewResolver,則通過從 DispatcherServlet.properties 中注冊一個(gè)默認(rèn)的 ViewResolver 來確保至少有一個(gè) ViewResolver。
	    if (this.viewResolvers == null) {
	        this.viewResolvers = getDefaultStrategies(context, ViewResolver.class);
	        if (logger.isTraceEnabled()) {
	            logger.trace("未為 servlet '" + getServletName() +
	                        " 聲明 ViewResolvers:使用默認(rèn)的 ViewResolver 策略");
	        }
	    }
	}
初始化FlashMapManager

SpringMVC Flush提供了一個(gè)請求存儲屬性,可供其他請求使用。在使用重定向的時(shí)候非常必要,例如POST/GET/DELETE。初始化FlashMapManager的源碼如下:文章來源地址http://www.zghlxwxcb.cn/news/detail-768566.html

	/**
	 * 初始化由此servlet實(shí)例使用的FlashMapManager。
	 * <p>如果未配置實(shí)現(xiàn),則默認(rèn)為{@code org.springframework.web.servlet.support.DefaultFlashMapManager}。
	 */
	private void initFlashMapManager(ApplicationContext context) {
		try {
			this.flashMapManager = context.getBean(FLASH_MAP_MANAGER_BEAN_NAME, FlashMapManager.class);
			if (logger.isTraceEnabled()) {
				logger.trace("Detected " + this.flashMapManager.getClass().getSimpleName());
			}
			else if (logger.isDebugEnabled()) {
				logger.debug("Detected " + this.flashMapManager);
			}
		}
		catch (NoSuchBeanDefinitionException ex) {
			// 需要使用默認(rèn)實(shí)現(xiàn)
			this.flashMapManager = getDefaultStrategy(context, FlashMapManager.class);
			if (logger.isTraceEnabled()) {
				logger.trace("No FlashMapManager '" + FLASH_MAP_MANAGER_BEAN_NAME +
						"': using default [" + this.flashMapManager.getClass().getSimpleName() + "]");
			}
		}
	}

到了這里,關(guān)于SpringMVC源碼解析——DispatcherServlet初始化的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • canal server初始化源碼分析

    canal server初始化源碼分析

    在開始之前,我們可以先了解下, canal 配置方式 ManagerCanalInstanceGenerator: 基于manager管理的配置方式,實(shí)時(shí)感知配置并進(jìn)行server重啟 SpringCanalInstanceGenerator:基于本地spring xml的配置方式,對于多instance的時(shí)候,不便于擴(kuò)展,一個(gè)instance一個(gè)xml配置 canal 配置文件 canal.properties ?

    2024年01月19日
    瀏覽(27)
  • 『手撕 Mybatis 源碼』06 - Mapper 代理方式初始化

    『手撕 Mybatis 源碼』06 - Mapper 代理方式初始化

    首先修改一下 SqlSession 獲取代理對象方式,即通過 getMapper() 來拿到動態(tài)代理對象 修改 sqlMapConfig.xml 引入配置文件的方式 把 UserMapper.xml 放到和 com.itheima.mapper.UserMapper 同一個(gè)目錄,同時(shí)修改一下命名空間,然后就可以學(xué)習(xí) MyBatis 的代理方式 問題 package name=“com.itheima.mapper”/

    2024年02月09日
    瀏覽(24)
  • LLMs之Colossal-LLaMA-2:源碼解讀(train.py文件)基于給定數(shù)據(jù)集實(shí)現(xiàn)持續(xù)預(yù)訓(xùn)練LLaMA-2—解析命令行參數(shù)→初始化配置(分布式訓(xùn)練環(huán)境colossalai+訓(xùn)練日志+加速插

    LLMs之Colossal-LLaMA-2:源碼解讀(train.py文件)基于給定數(shù)據(jù)集實(shí)現(xiàn)持續(xù)預(yù)訓(xùn)練LLaMA-2—解析命令行參數(shù)→初始化配置(分布式訓(xùn)練環(huán)境colossalai+訓(xùn)練日志+加速插件)→數(shù)據(jù)預(yù)處理(初始化分詞器+數(shù)據(jù)處理器+數(shù)據(jù)加載器)→模型訓(xùn)練(初始化模型/優(yōu)化器/學(xué)習(xí)率調(diào)度器/梯度檢查點(diǎn)/Flash-Att

    2024年02月06日
    瀏覽(20)
  • SpringBoot 源碼分析初始化應(yīng)用上下文(1)-createApplicationContext

    SpringBoot 源碼分析初始化應(yīng)用上下文(1)-createApplicationContext

    前言:springBoot的版本是? 2.2.4.RELEASE 問題切入:為什么叫做上下文對象呢?上下文對象,就是當(dāng)前應(yīng)用環(huán)境下的一個(gè)集合 初始化(創(chuàng)建)上下文對象主要看上面注釋那行,即: 接著看下 createApplicationContext() 這個(gè)方法的實(shí)現(xiàn) 截圖: 代碼: ?接著看下AnnotationConfigServletWebServerApp

    2024年02月08日
    瀏覽(17)
  • Spring 填充屬性和初始化流程源碼剖析及擴(kuò)展實(shí)現(xiàn)

    Spring 填充屬性和初始化流程源碼剖析及擴(kuò)展實(shí)現(xiàn)

    在上一篇博文 講解 Spring 實(shí)例化的不同方式及相關(guān)生命周期源碼剖析 介紹了 Spring 實(shí)例化的不同方式,本文主要圍繞實(shí)例化過后對象的填充屬性和初始化過程進(jìn)行詳細(xì)流程剖析 回顧前言知識,doCreateBean-createBeanInstance,通過 Supplier 接口、FactoryMethod、構(gòu)造函數(shù)反射 invoke,創(chuàng)建

    2024年02月06日
    瀏覽(28)
  • LLMs之Chinese-LLaMA-Alpaca-2:源碼解讀(run_clm_sft_with_peft.py文件)—模型訓(xùn)練前置工作(參數(shù)解析+配置日志)→模型初始化(檢測是否存在訓(xùn)練過的che

    LLMs之Chinese-LLaMA-Alpaca-2:源碼解讀(run_clm_sft_with_peft.py文件)—模型訓(xùn)練前置工作(參數(shù)解析+配置日志)→模型初始化(檢測是否存在訓(xùn)練過的checkpoint+加載預(yù)訓(xùn)練模型和tokenizer)→數(shù)據(jù)預(yù)處理(監(jiān)督式任務(wù)的數(shù)據(jù)收集器+指令數(shù)據(jù)集【json格式】)→優(yōu)化模型配置(量化模塊+匹配模型voca

    2024年02月06日
    瀏覽(24)
  • LLMs之Chinese-LLaMA-Alpaca-2:源碼解讀(run_clm_pt_with_peft.py文件)—模型訓(xùn)練前置工作(參數(shù)解析+配置日志)→模型初始化(檢測是否存在訓(xùn)練過的chec

    LLMs之Chinese-LLaMA-Alpaca-2:源碼解讀(run_clm_pt_with_peft.py文件)—模型訓(xùn)練前置工作(參數(shù)解析+配置日志)→模型初始化(檢測是否存在訓(xùn)練過的checkpoint+加載預(yù)訓(xùn)練模型和tokenizer)→數(shù)據(jù)預(yù)處理(處理【標(biāo)記化+分塊】+切分txt數(shù)據(jù)集)→優(yōu)化模型配置( 量化模塊 +匹配模型vocabulary大小與to

    2024年02月07日
    瀏覽(29)
  • 從內(nèi)核源碼看 slab 內(nèi)存池的創(chuàng)建初始化流程

    從內(nèi)核源碼看 slab 內(nèi)存池的創(chuàng)建初始化流程

    在上篇文章 《細(xì)節(jié)拉滿,80 張圖帶你一步一步推演 slab 內(nèi)存池的設(shè)計(jì)與實(shí)現(xiàn) 》中,筆者從 slab cache 的總體架構(gòu)演進(jìn)角度以及 slab cache 的運(yùn)行原理角度為大家勾勒出了 slab cache 的總體架構(gòu)視圖,基于這個(gè)視圖詳細(xì)闡述了 slab cache 的內(nèi)存分配以及釋放原理。 slab cache 機(jī)制確實(shí)比

    2023年04月12日
    瀏覽(100)
  • 【Vue2.0源碼學(xué)習(xí)】生命周期篇-初始化階段(initState)

    【Vue2.0源碼學(xué)習(xí)】生命周期篇-初始化階段(initState)

    本篇文章介紹生命周期初始化階段所調(diào)用的第五個(gè)初始化函數(shù)—— initState 。 從函數(shù)名字上來看,這個(gè)函數(shù)是用來初始化實(shí)例狀態(tài)的,那么什么是實(shí)例的狀態(tài)呢?在前面文章中我們略有提及,在我們?nèi)粘i_發(fā)中,在 Vue 組件中會寫一些如 props 、 data 、 methods 、 computed 、 watc

    2024年02月09日
    瀏覽(26)
  • 【Vue2.0源碼學(xué)習(xí)】生命周期篇-初始化階段(initInjections)

    本篇文章介紹生命周期初始化階段所調(diào)用的第四個(gè)初始化函數(shù)—— initInjections 。從函數(shù)名字上來看,該函數(shù)是用來初始化實(shí)例中的 inject 選項(xiàng)的。說到 inject 選項(xiàng),那必然離不開 provide 選項(xiàng),這兩個(gè)選項(xiàng)都是成對出現(xiàn)的,它們的作用是:允許一個(gè)祖先組件向其所有子孫后代注

    2024年02月09日
    瀏覽(16)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包