54--DispatcherServlet初始化

1.引

上一節(jié)已經(jīng)簡(jiǎn)單介紹了ContextLoaderListener的初始化過程狠鸳,接下來應(yīng)該初始化DispatcherServlet。DispatcherServlet作為SpringMVC的核心Servlet控制器靶剑,了解其初始化機(jī)制十分有必要。

Spring MVC框架,與其他很多web的MVC框架一樣:請(qǐng)求驅(qū)動(dòng);所有設(shè)計(jì)都圍繞著一個(gè)中央Servlet來展開户秤,它負(fù)責(zé)把所有請(qǐng)求分發(fā)到控制器;同時(shí)提供其他web應(yīng)用開發(fā)所需要的功能逮矛。不過Spring的中央處理器鸡号, DispatcherServlet ,能做的比這更多须鼎。它與Spring IoC容器做到了無縫集成鲸伴,這意味著,Spring提供的任何特性莉兰,在Spring MVC中你都可以使用挑围。下圖展示了Spring Web MVC的 DispatcherServlet 處理請(qǐng)求的工作流。熟悉設(shè)計(jì)模式的朋友會(huì)發(fā)現(xiàn)糖荒, DispatcherServlet 應(yīng)用的其實(shí)就是一個(gè)“前端控制器”的設(shè)計(jì)模式( 其他很多優(yōu)秀的web框架也都使用了這個(gè)設(shè)計(jì)模式) 杉辙。

在這里插入圖片描述

DispatcherServlet的繼承關(guān)系如下:
DispatcherServlet-->FrameworkServlet-->HttpServletBean-->HttpServlet,從這里可以看到捶朵,DispatcherServlet其本質(zhì)也還是Servlet蜘矢。

根據(jù)之前對(duì)HttpServlet生命周期的分析,DispatcherServlet應(yīng)該也存在init方法综看,通過查找品腹,我們可以在其父類HttpServletBean找到init方法,那么本篇的分析就從這里開始吧红碑。

2.HttpServletBean初始化
/**
 * DispatcherServlet 初始化入口
 * Map config parameters onto bean properties of this servlet, and
 * invoke subclass initialization.
 * @throws ServletException if bean properties are invalid (or required
 * properties are missing), or if subclass initialization fails.
 */
@Override
public final void init() throws ServletException {

    // Set bean properties from init parameters.
    /**
     * 1.加載初始化參數(shù)舞吭,如:
     * <servlet>
     *      <servlet-name>example</servlet-name>
     *      <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
     *      <init-param>
     *          <param-name>name</param-name>
     *          <param-value>jack</param-value>
     *      </init-param>
     *      <load-on-startup>1</load-on-startup>
     *  </servlet>
     *  這里會(huì)解析init-param列表。
     */
    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;
        }
    }

    // Let subclasses do whatever initialization they like.
    // 2.留給子類覆蓋的模板方法
    initServletBean();
}

該方法最主要的作用就是初始化init-param析珊,如果我們沒有配置任何init-param羡鸥,那么該方法不會(huì)執(zhí)行任何操作。從這里我們沒有拿到有用的信息忠寻,但是在該方法結(jié)尾有initServletBean()惧浴,這是一個(gè)模板方法,可以由子類來實(shí)現(xiàn)奕剃,那么接下來我們就去看其子類FrameworkServlet中的initServletBean吧衷旅。

3.FrameworkServlet初始化
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 {
        // 為當(dāng)前servlet初始化web應(yīng)用上下文
        this.webApplicationContext = initWebApplicationContext();
        // 空的模板方法
        initFrameworkServlet();
    }
    catch (ServletException | RuntimeException ex) {
        logger.error("Context initialization failed", ex);
        throw ex;
    }

    if (logger.isDebugEnabled()) {
        String value = this.enableLoggingRequestDetails ?
                "shown which may lead to unsafe logging of potentially sensitive data" :
                "masked to prevent unsafe logging of potentially sensitive data";
        logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
                "': request parameters and headers will be " + value);
    }

    if (logger.isInfoEnabled()) {
        logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
    }
}
protected WebApplicationContext initWebApplicationContext() {
    // 獲取rootContext捐腿,該Context就是通過ContextLoaderListener創(chuàng)建的XmlWebApplicationContext
    WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    WebApplicationContext wac = null;

    // 如果當(dāng)前webApplicationContext不為null,則為其設(shè)置父容器
    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);
            }
        }
    }
    // 未能通過構(gòu)造函數(shù)注入柿顶,則嘗試去ServletContext容器中查找有無WebApplicationContext
    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();
    }
    // 以上均無WebApplicationContext茄袖,則創(chuàng)建一個(gè)新的WebApplicationContext
    if (wac == null) {
        // No context instance is defined for this servlet -> create a local one
        wac = createWebApplicationContext(rootContext);
    }

    // 刷新上下文容器,空的模板方法九串,留給子類實(shí)現(xiàn)
    if (!this.refreshEventReceived) {
        // Either the context is not a ConfigurableApplicationContext with refresh
        // support or the context injected at construction time had already been
        // refreshed -> trigger initial onRefresh manually here.
        onRefresh(wac);
    }

    if (this.publishContext) {
        // Publish the context as a servlet context attribute.
        String attrName = getServletContextAttributeName();
        getServletContext().setAttribute(attrName, wac);
    }

    return wac;
}

代碼讀到這里的時(shí)候绞佩,大家一定心存疑問寺鸥,在加載ContextLoaderListener的時(shí)候不是已經(jīng)創(chuàng)建過一次XmlWebApplicationContext了么猪钮?為什么這里又要?jiǎng)?chuàng)建一次呢?通過下面的圖來看一下:

在這里插入圖片描述

Spring中的 ApplicationContext 實(shí)例是可以有范圍( scope) 的胆建。在Spring MVC中烤低,每個(gè) DispatcherServlet 都持有一個(gè)自己的上下文對(duì)象 WebApplicationContext ,它又繼承了根( root) WebApplicationContext 對(duì)象中已經(jīng)定義的所有bean笆载。這些繼承的bean可以在具體的Servlet實(shí)例中被重載扑馁,在每個(gè)Servlet實(shí)例中你也可以定義其scope下的新bean。

DispatcherServlet 的初始化過程中凉驻,Spring MVC會(huì)在你web應(yīng)用的 WEB-INF 目錄下查找一個(gè)名為[servlet-name]-servlet.xml的配置文件腻要,并創(chuàng)建其中所定義的bean。如果在全局上下文中存在相同名字的bean涝登,則它們將被新定義的同名bean覆蓋雄家。

看看下面這個(gè) DispatcherServlet 的Servlet配置( 定義于web.xml文件中)

<servlet>
    <servlet-name>example</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>namespace</param-name>
        <param-value>example-servlet</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>example</servlet-name>
    <url-pattern>/example/*</url-pattern>
</servlet-mapping>

有了以上的Servlet配置文件,你還需要在應(yīng)用中的 /WEB-INF/ 路徑下創(chuàng)建一個(gè) example-servlet.xml 文件胀滚,在該文件中定義所有Spring MVC相關(guān)的組件( 比如bean等) 趟济。你可以通過servlet初始化參數(shù)為這個(gè)配置文件指定其他的路徑 。

當(dāng)你的應(yīng)用中只需要一個(gè) DispatcherServlet 時(shí)咽笼,只配置一個(gè)根context對(duì)象也是可行的顷编。

在這里插入圖片描述

要配置一個(gè)唯一的根context對(duì)象,可以通過在servlet初始化參數(shù)中配置一個(gè)空的contextConfigLocation來做到剑刑,如下所示:

<web-app>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/root-context.xml</param-value>
    </context-param>
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value></param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
    <listener>
        <listene-class>org.springframework.web.context.ContextLoaderListener</listene-class>
    </listener>
</web-app>

我們繼續(xù)來看initWebApplicationContext方法媳纬,根據(jù)Spring的注釋,可以看到初始化的具體工作委托給了createWebApplicationContext方法施掏。

protected WebApplicationContext createWebApplicationContext(@Nullable WebApplicationContext parent) {
    return createWebApplicationContext((ApplicationContext) parent);
}


protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
    Class<?> contextClass = getContextClass();
    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);
    String configLocation = getContextConfigLocation();
    if (configLocation != null) {
        wac.setConfigLocation(configLocation);
    }
    configureAndRefreshWebApplicationContext(wac);

    return wac;
}

該方法與上一節(jié)的邏輯大致相同钮惠,這里不多贅述了。為當(dāng)前Servlet創(chuàng)建完WebApplicationContext之后其监,緊接著就要調(diào)用其onRefresh方法萌腿,在FrameworkServlet中,該方法是一個(gè)空的模板方法抖苦,其具體的實(shí)現(xiàn)留給了子類毁菱。那么接下來就要去分析DispatcherServlet的onRefresh方法了米死。

4.DispatcherServlet初始化

在了解DispatcherServlet之前,先回顧一下DispatcherServlet的內(nèi)置組件及其作用贮庞。

bean的類型 作用
HandlerMapping 處理器映射峦筒。它會(huì)根據(jù)某些規(guī)則將進(jìn)入容器的請(qǐng)求映射到具體的處理器以及一系列前處理器和后處理器( 即處理器攔截器) 上。具體的規(guī)則視 HandlerMapping 類的實(shí)現(xiàn)不同而有所不同窗慎。其最常用的一個(gè)實(shí)現(xiàn)支持你在控制器上添加注解物喷,配置請(qǐng)求路徑。當(dāng)然遮斥,也存在其他的實(shí)現(xiàn)峦失。
HandlerAdapter 處理器適配器。拿到請(qǐng)求所對(duì)應(yīng)的處理器后术吗,適配器將負(fù)責(zé)去調(diào)用該處理器尉辑,這使得 DispatcherServlet 無需關(guān)心具體的調(diào)用細(xì)節(jié)。比方說较屿,要調(diào)用的是一個(gè)基于注解配置的控制器隧魄,那么調(diào)用前還需要從許多注解中解析出一些相應(yīng)的信息。因此隘蝎, HandlerAdapter 的主要任務(wù)就是對(duì) DispatcherServlet 屏蔽這些具體的細(xì)節(jié)购啄。
HandlerExceptionResolver 處理器異常解析器。它負(fù)責(zé)將捕獲的異常映射到不同的視圖上去嘱么,此外還支持更復(fù)雜的異常處理代碼狮含。
ViewResolver 視圖解析器。它負(fù)責(zé)將一個(gè)代表邏輯視圖名的字符串( String) 映射到實(shí)際的視圖類型 View 上拱撵。
LocaleResolver&LocaleContextResolver 地區(qū)解析器 和 地區(qū)上下文解析器辉川。它們負(fù)責(zé)解析客戶端所在的地區(qū)信息甚至?xí)r區(qū)信息,為國(guó)際化的視圖定制提供了支持拴测。
ThemeResolver 主題解析器乓旗。它負(fù)責(zé)解析你web應(yīng)用中可用的主題,比如集索,提供一些個(gè)性化定制的布局等屿愚。
MultipartResolver 解析multi-part的傳輸請(qǐng)求,比如支持通過HTML表單進(jìn)行的文件上傳等务荆。
FlashMapManager FlashMap管理器妆距。它能夠存儲(chǔ)并取回兩次請(qǐng)求之間的 FlashMap 對(duì)象。后者可用于在請(qǐng)求之間傳遞數(shù)據(jù)函匕,通常是在請(qǐng)求重定向的情境下使用娱据。

來看具體代碼:

protected void onRefresh(ApplicationContext context) {
    initStrategies(context);
}

protected void initStrategies(ApplicationContext context) {
    // 1.初始化MultipartResolver
    initMultipartResolver(context);
    // 2.初始化LocaleResolver
    initLocaleResolver(context);
    // 3.初始化ThemeResolver
    initThemeResolver(context);
    // 4.初始化HandlerMappings
    initHandlerMappings(context);
    // 5.初始化HandlerAdapters
    initHandlerAdapters(context);
    // 6.初始化HandlerExceptionResolvers
    initHandlerExceptionResolvers(context);
    // 7.初始化RequestToViewNameTranslator
    initRequestToViewNameTranslator(context);
    // 8.初始化ViewResolvers
    initViewResolvers(context);
    // 9.初始化FlashMapManager
    initFlashMapManager(context);

    // 以上,其中4盅惜、5中剩、6忌穿、8步的初始化邏輯是相同的
}
4.1 初始化MultipartResolver
private void initMultipartResolver(ApplicationContext context) {
    try {
        this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
        if (logger.isTraceEnabled()) {
            logger.trace("Detected " + this.multipartResolver);
        }
        else if (logger.isDebugEnabled()) {
            logger.debug("Detected " + this.multipartResolver.getClass().getSimpleName());
        }
    }
    catch (NoSuchBeanDefinitionException ex) {
        // Default is no multipart resolver.
        this.multipartResolver = null;
        if (logger.isTraceEnabled()) {
            logger.trace("No MultipartResolver '" + MULTIPART_RESOLVER_BEAN_NAME + "' declared");
        }
    }
}

如果配置文件中有multipartResolver,則會(huì)將配置的multipartResolver進(jìn)行實(shí)例化结啼。

4.2 初始化LocaleResolver
private void initLocaleResolver(ApplicationContext context) {
    try {
        this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
        if (logger.isTraceEnabled()) {
            logger.trace("Detected " + this.localeResolver);
        }
        else if (logger.isDebugEnabled()) {
            logger.debug("Detected " + this.localeResolver.getClass().getSimpleName());
        }
    }
    catch (NoSuchBeanDefinitionException ex) {
        // We need to use the default.
        this.localeResolver = getDefaultStrategy(context, LocaleResolver.class);
        if (logger.isTraceEnabled()) {
            logger.trace("No LocaleResolver '" + LOCALE_RESOLVER_BEAN_NAME +
                    "': using default [" + this.localeResolver.getClass().getSimpleName() + "]");
        }
    }
}

如果配置文件中有l(wèi)ocaleResolver掠剑,則會(huì)將配置的localeResolver進(jìn)行實(shí)例化。

4.3 初始化ThemeResolver
private void initThemeResolver(ApplicationContext context) {
    try {
        this.themeResolver = context.getBean(THEME_RESOLVER_BEAN_NAME, ThemeResolver.class);
        if (logger.isTraceEnabled()) {
            logger.trace("Detected " + this.themeResolver);
        }
        else if (logger.isDebugEnabled()) {
            logger.debug("Detected " + this.themeResolver.getClass().getSimpleName());
        }
    }
    catch (NoSuchBeanDefinitionException ex) {
        // We need to use the default.
        this.themeResolver = getDefaultStrategy(context, ThemeResolver.class);
        if (logger.isTraceEnabled()) {
            logger.trace("No ThemeResolver '" + THEME_RESOLVER_BEAN_NAME +
                    "': using default [" + this.themeResolver.getClass().getSimpleName() + "]");
        }
    }
}

如果配置文件中有themeResolver郊愧,則會(huì)將配置的themeResolver進(jìn)行實(shí)例化朴译。

4.4 初始化HandlerMappings
/**
 * 實(shí)例化HandlerMappings,如果沒有自定義HandlerMappings属铁,則默認(rèn)使用BeanNameUrlHandlerMapping
 * Initialize the HandlerMappings used by this class.
 * <p>If no HandlerMapping beans are defined in the BeanFactory for this namespace,
 * we default to BeanNameUrlHandlerMapping.
 */
private void initHandlerMappings(ApplicationContext context) {
    this.handlerMappings = null;

    // 1.如果希望獲取所有的HandlerMapping眠寿,包括父容器。
    // detectAllHandlerMappings默認(rèn)為true
    if (this.detectAllHandlerMappings) {
        // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
        Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
                                            context,
                                            HandlerMapping.class,
                                            true,
                                            false);
        if (!matchingBeans.isEmpty()) {
            this.handlerMappings = new ArrayList<>(matchingBeans.values());
            // 排序
            // We keep HandlerMappings in sorted order.
            AnnotationAwareOrderComparator.sort(this.handlerMappings);
        }
    }
    // 2.否則只獲取當(dāng)前上下文自定義配置的handlerMapping
    else {
        try {
            HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
            this.handlerMappings = Collections.singletonList(hm);
        }
        catch (NoSuchBeanDefinitionException ex) {
            // Ignore, we'll add a default HandlerMapping later.
        }
    }
    // 3.上述兩步都未能獲取到handlerMapping红选,則使用默認(rèn)的handlerMapping澜公,
    //   包括BeanNameUrlHandlerMapping和RequestMappingHandlerMapping
    // Ensure we have at least one HandlerMapping, by registering
    // a default HandlerMapping if no other mappings are found.
    if (this.handlerMappings == null) {
        this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
        if (logger.isTraceEnabled()) {
            logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
                    "': using default strategies from DispatcherServlet.properties");
        }
    }
}

這里要注意一下this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);,Spring是如何來加載默認(rèn)的handlerMapping的喇肋。在getDefaultStrategies方法中有defaultStrategies靜態(tài)變量,該變量通過下面的靜態(tài)代碼塊來初始化:

static {
    // Load default strategy implementations from properties file.
    // This is currently strictly internal and not meant to be customized
    // by application developers.
    try {
        ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
        defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
    }
    catch (IOException ex) {
        throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
    }
}

該代碼加載了DispatcherServlet.properties配置文件迹辐,并對(duì)其進(jìn)行了解析和存儲(chǔ)蝶防。打開該配置文件:

# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.

org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
    org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
    org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
    org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator

org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver

org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

這里存儲(chǔ)了DispatcherServlet一些默認(rèn)內(nèi)置組件的實(shí)現(xiàn)類,從這里可以看到Spring默認(rèn)使用的HandlerMapping為BeanNameUrlHandlerMapping和RequestMappingHandlerMapping明吩。

4.5 初始化HandlerAdapters
private void initHandlerAdapters(ApplicationContext context) {
    this.handlerAdapters = null;

    if (this.detectAllHandlerAdapters) {
        // Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.
        Map<String, HandlerAdapter> matchingBeans =
                BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
        if (!matchingBeans.isEmpty()) {
            this.handlerAdapters = new ArrayList<>(matchingBeans.values());
            // We keep HandlerAdapters in sorted order.
            AnnotationAwareOrderComparator.sort(this.handlerAdapters);
        }
    }
    else {
        try {
            HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
            this.handlerAdapters = Collections.singletonList(ha);
        }
        catch (NoSuchBeanDefinitionException ex) {
            // Ignore, we'll add a default HandlerAdapter later.
        }
    }

    // Ensure we have at least some HandlerAdapters, by registering
    // default HandlerAdapters if no other adapters are found.
    if (this.handlerAdapters == null) {
        this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
        if (logger.isTraceEnabled()) {
            logger.trace("No HandlerAdapters declared for servlet '" + getServletName() +
                    "': using default strategies from DispatcherServlet.properties");
        }
    }
}

與初始化HandlerMappings邏輯大致相同间学,不多贅述。

4.6 初始化HandlerExceptionResolvers
private void initHandlerExceptionResolvers(ApplicationContext context) {
    this.handlerExceptionResolvers = null;

    if (this.detectAllHandlerExceptionResolvers) {
        // Find all HandlerExceptionResolvers in the ApplicationContext, including ancestor contexts.
        Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils
                .beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);
        if (!matchingBeans.isEmpty()) {
            this.handlerExceptionResolvers = new ArrayList<>(matchingBeans.values());
            // We keep HandlerExceptionResolvers in sorted order.
            AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);
        }
    }
    else {
        try {
            HandlerExceptionResolver her =
                    context.getBean(HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class);
            this.handlerExceptionResolvers = Collections.singletonList(her);
        }
        catch (NoSuchBeanDefinitionException ex) {
            // Ignore, no HandlerExceptionResolver is fine too.
        }
    }

    // Ensure we have at least some HandlerExceptionResolvers, by registering
    // default HandlerExceptionResolvers if no other resolvers are found.
    if (this.handlerExceptionResolvers == null) {
        this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class);
        if (logger.isTraceEnabled()) {
            logger.trace("No HandlerExceptionResolvers declared in servlet '" + getServletName() +
                    "': using default strategies from DispatcherServlet.properties");
        }
    }
}

與初始化HandlerMappings邏輯大致相同印荔,不多贅述低葫。

4.7初始化RequestToViewNameTranslator
private void initRequestToViewNameTranslator(ApplicationContext context) {
    try {
        this.viewNameTranslator =
                context.getBean(REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME, RequestToViewNameTranslator.class);
        if (logger.isTraceEnabled()) {
            logger.trace("Detected " + this.viewNameTranslator.getClass().getSimpleName());
        }
        else if (logger.isDebugEnabled()) {
            logger.debug("Detected " + this.viewNameTranslator);
        }
    }
    catch (NoSuchBeanDefinitionException ex) {
        // We need to use the default.
        this.viewNameTranslator = getDefaultStrategy(context, RequestToViewNameTranslator.class);
        if (logger.isTraceEnabled()) {
            logger.trace("No RequestToViewNameTranslator '" + REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME +
                    "': using default [" + this.viewNameTranslator.getClass().getSimpleName() + "]");
        }
    }
}
4.8初始化ViewResolvers
private void initViewResolvers(ApplicationContext context) {
    this.viewResolvers = null;

    if (this.detectAllViewResolvers) {
        // Find all ViewResolvers in the ApplicationContext, including ancestor contexts.
        Map<String, ViewResolver> matchingBeans =
                BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);
        if (!matchingBeans.isEmpty()) {
            this.viewResolvers = new ArrayList<>(matchingBeans.values());
            // We keep ViewResolvers in sorted order.
            AnnotationAwareOrderComparator.sort(this.viewResolvers);
        }
    }
    else {
        try {
            ViewResolver vr = context.getBean(VIEW_RESOLVER_BEAN_NAME, ViewResolver.class);
            this.viewResolvers = Collections.singletonList(vr);
        }
        catch (NoSuchBeanDefinitionException ex) {
            // Ignore, we'll add a default ViewResolver later.
        }
    }

    // Ensure we have at least one ViewResolver, by registering
    // a default ViewResolver if no other resolvers are found.
    if (this.viewResolvers == null) {
        this.viewResolvers = getDefaultStrategies(context, ViewResolver.class);
        if (logger.isTraceEnabled()) {
            logger.trace("No ViewResolvers declared for servlet '" + getServletName() +
                    "': using default strategies from DispatcherServlet.properties");
        }
    }
}

與初始化HandlerMappings邏輯大致相同,不多贅述仍律。

4.9 初始化FlashMapManager
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) {
        // We need to use the default.
        this.flashMapManager = getDefaultStrategy(context, FlashMapManager.class);
        if (logger.isTraceEnabled()) {
            logger.trace("No FlashMapManager '" + FLASH_MAP_MANAGER_BEAN_NAME +
                    "': using default [" + this.flashMapManager.getClass().getSimpleName() + "]");
        }
    }
}

到這里DispatcherServlet初始化的工作就基本完成了嘿悬。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市水泉,隨后出現(xiàn)的幾起案子善涨,更是在濱河造成了極大的恐慌,老刑警劉巖草则,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件钢拧,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡炕横,警方通過查閱死者的電腦和手機(jī)源内,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來份殿,“玉大人膜钓,你說我怎么就攤上這事塔鳍。” “怎么了呻此?”我有些...
    開封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵轮纫,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我焚鲜,道長(zhǎng)掌唾,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任忿磅,我火速辦了婚禮糯彬,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘葱她。我一直安慰自己败明,他們只是感情好掩蛤,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般邀泉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上纤泵,一...
    開封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天铣口,我揣著相機(jī)與錄音,去河邊找鬼偶器。 笑死斩萌,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的屏轰。 我是一名探鬼主播颊郎,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼霎苗!你這毒婦竟也來了姆吭?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤叨粘,失蹤者是張志新(化名)和其女友劉穎猾编,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體升敲,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡答倡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了驴党。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瘪撇。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出倔既,到底是詐尸還是另有隱情恕曲,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布渤涌,位于F島的核電站佩谣,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏实蓬。R本人自食惡果不足惜茸俭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望安皱。 院中可真熱鬧调鬓,春花似錦、人聲如沸酌伊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)居砖。三九已至虹脯,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間悯蝉,已是汗流浹背归形。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留鼻由,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓厚棵,卻偏偏與公主長(zhǎng)得像蕉世,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子婆硬,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 本文是我自己在秋招復(fù)習(xí)時(shí)的讀書筆記狠轻,整理的知識(shí)點(diǎn),也是為了防止忘記彬犯,尊重勞動(dòng)成果向楼,轉(zhuǎn)載注明出處哦!如果你也喜歡谐区,那...
    波波波先森閱讀 12,294評(píng)論 6 86
  • 3.1湖蜕、DispatcherServlet作用 DispatcherServlet是前端控制器設(shè)計(jì)模式的實(shí)現(xiàn),提供...
    秋名山車神_f776閱讀 2,937評(píng)論 0 0
  • 1.Spring整體架構(gòu) 1)核心容器(Core Container) Core模塊宋列,主要包含了Spring框架基...
    Sponge1128閱讀 1,066評(píng)論 0 1
  • Spring容器高層視圖 Spring 啟動(dòng)時(shí)讀取應(yīng)用程序提供的Bean配置信息昭抒,并在Spring容器中生成一份相...
    Theriseof閱讀 2,815評(píng)論 1 24
  • 28. 五官不靈。現(xiàn)在大家五官的功能都下降了,眼睛近視灭返,耳朵聾盗迟,鼻子聞不出香臭味兒來,都是由于五臟六腑整體功能下降...
    海風(fēng)居士6645閱讀 288評(píng)論 0 0