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初始化的工作就基本完成了嘿悬。