Spring做为一个优秀的web框架,其运行是基于Tomcat的。在咱们前面的讲解中,Spring的驱动都是使用的ClassPathXmlApplicationContext
,而且都是直接在main方法中启动的,可是在Tomcat容器中,咱们是没法使用main方法的,于是其驱动方式必然与咱们测试时不同。Tomcat是一个基于Servlet规范的web容器,而Spring则提供了对Servlet规范的支持,其DispatcherServlet
则是Servlet规范的具体实现。于是在web开发过程当中,当咱们启动Tomcat容器时其会根据Servlet规范启动Spring实现的DispatcherServlet
,这样也就驱动了Spring的运行。本文主要从源码的角度讲解Spring在web容器中是如何初始化的。java
在配置web容器时,咱们都会配置一个web.xml,而在配置web.xml时,最主要的两个组件就是ContextLoaderListener
和DispatcherServlet
的配置。以下是一个典型的web.xml文件的配置:web
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <servlet> <servlet-name>myservlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>myservlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
这里ContextLoaderListener
的做用是对对Servlet Context的行为进行监听,其实现了ServletContextListener
接口,这个接口声明以下:spring
public interface ServletContextListener extends EventListener { // 用于在Servlet Context初始化事前执行 default public void contextInitialized(ServletContextEvent sce) {} // 用于在Servlet Context被销毁以后执行 default public void contextDestroyed(ServletContextEvent sce) {} }
这里ServletContextListener
是Servlet规范中提供的一个接口,该接口中的contextInitialized()
会在servlet context初始化以前执行,而contextDestroyed()
方法则会在servlet context被销毁以后执行。Spring提供的ContextLoaderListener
对这两个方法都进行了实现。实际上,在web.xml中指定的contextConfigLocation参数的解析就是在ContextLoaderListener.contextInitialized()
方法中解析的,也就是说Spring对于bean的建立其实是在Servlet Context初始化以前就已经完成了。mvc
web.xml中配置的DispatcherServlet
则是Servlet规范中HttpServlet的一个具体实现,实现了该接口的以后该类就具备处理web请求的能力了。这里能够看到,DispatcherServlet
配置拦截的url是'/',也就是说全部的web请求都会通过DispatcherServlet
,而对于具体的url的处理,其实是在DispatcherServlet
中进行分发的。这也就是Spring为何只须要配置一个Servlet的缘由。app
关于DispatcherServlet
的配置这里不得不提的是,咱们得为其提供一个myservlet-servlet.xml的配置文件,用于只为当前servlet提供Spring的一些基本配置。这里该文件的命名必须按照servlet名称-servlet.xml
这种格式进行,因为咱们的servlet的名称为myservlet,于是配置文件名必须为myservlet-servlet.xml。若是使用者须要自定义文件名,能够在当前servlet中使用init-param
标签进行配置,如:框架
<servlet> <servlet-name>myservlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>WEB-INF/myservlet-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
另外,对于配置的myservlet-servlet.xml文件的初始化,是在DispatcherServlet.init()
方法中进行的。这里须要注意的是,myservlet-servlet.xml完彻底全是一个独立的Spring配置文件,咱们能够在其中声明Spring的bean,而且注册到Spring容器中。ide
在web.xml中咱们提到了两个Spring的配置文件,一个是咱们经常使用的applicationContext.xml,另外一个专属于某个Servlet的myservlet-servlet.xml。两个配置文件的初始化分别是由ServletContextListener.contextInitialized()
方法和GenericServlet.init()
方法进行的。这两个方法都是Servlet规范中提供的初始化方法,两个方法分别会初始化两个Spring容器,这两个容器中applicationContext.xml对应的容器会做为myservlet-servlet.xml初始化的容器的父容器而存在,于是在myservlet-servlet.xml的容器中,咱们是可使用任何在applicationContext.xml中声明的bean的,可是反过来则不行。在处理具体请求的时候,咱们所使用的Spring容器其实一直都是myservlet-servlet.xml声明而来的。测试
对于ContextLoaderListener,其主要是用于初始化咱们经常使用的applicationContext.xml的。以下是其源码:ui
public class ContextLoaderListener extends ContextLoader implements ServletContextListener { // 初始化Spring容器 public void contextInitialized(ServletContextEvent event) { initWebApplicationContext(event.getServletContext()); } // 销毁Spring容器 public void contextDestroyed(ServletContextEvent event) { closeWebApplicationContext(event.getServletContext()); ContextCleanupListener.cleanupAttributes(event.getServletContext()); } }
能够看到,这里对于Spring容器的初始化是委托给了initWebApplicationContext()
方法进行的,以下是该方法的源码:this
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { // 若是当前已经初始化过一个web application context则抛出异常,这样能够保证一个web容器中 // 只会有一个web application context if (servletContext.getAttribute(WebApplicationContext .ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) { throw new IllegalStateException( "Cannot initialize context because there is already a root application" + " context present - check whether you have multiple ContextLoader*" + " definitions in your web.xml!"); } Log logger = LogFactory.getLog(ContextLoader.class); servletContext.log("Initializing Spring root WebApplicationContext"); if (logger.isInfoEnabled()) { logger.info("Root WebApplicationContext: initialization started"); } long startTime = System.currentTimeMillis(); try { if (this.context == null) { // 经过servlet配置建立一个WebApplicationContext对象 this.context = createWebApplicationContext(servletContext); } // 这里在createWebApplicationContext()方法中会保证建立的WebApplicationContext本质上是 // ConfigurableWebApplicationContext类型的,于是这里进行类型判断的时候是可以进入if分支的 if (this.context instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context; // 若是当前WebApplicationContext没有初始化过就对其进行初始化 if (!cwac.isActive()) { // 若是当前WebApplicationContext没有父ApplicationContext,则经过 // loadParentContext()方法加载一个,该方法其实是一个空方法,这里提供 // 出来只是为了方便用户进行容器属性的自定义,由于父容器的内容会继承到子容器中 if (cwac.getParent() == null) { ApplicationContext parent = loadParentContext(servletContext); cwac.setParent(parent); } // 对WebApplicationContext进行配置,而且调用其refresh()方法初始化 // 配置文件中配置的Spring的各个组件 configureAndRefreshWebApplicationContext(cwac, servletContext); } } // 初始化完成以后将当前WebApplicationContext设置到ServletContext中 servletContext.setAttribute(WebApplicationContext .ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); // 设置当前WebApplicationContext的类加载器 ClassLoader ccl = Thread.currentThread().getContextClassLoader(); if (ccl == ContextLoader.class.getClassLoader()) { currentContext = this.context; } else if (ccl != null) { currentContextPerThread.put(ccl, this.context); } if (logger.isDebugEnabled()) { logger.debug("Published root WebApplicationContext as ServletContext" + " attribute with name [" + WebApplicationContext .ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]"); } if (logger.isInfoEnabled()) { long elapsedTime = System.currentTimeMillis() - startTime; logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms"); } return this.context; } catch (RuntimeException ex) { logger.error("Context initialization failed", ex); servletContext.setAttribute(WebApplicationContext .ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex); throw ex; } catch (Error err) { logger.error("Context initialization failed", err); servletContext.setAttribute(WebApplicationContext .ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err); throw err; } }
能够看到,对于WebApplicationContext的初始化,Spring首先会根据配置文件配置建立一个WebApplicationContext对象,而后判断该对象是否初始化过,若是没有,则对其进行配置而且初始化。这里咱们首先看看Spring是如何建立WebApplicationContext对象的:
protected WebApplicationContext createWebApplicationContext(ServletContext sc) { // 读取配置文件中配置的实现了WebApplicationContext接口的类 Class<?> contextClass = determineContextClass(sc); // 判断读取到的类是否实现了ConfigurableWebApplicationContext接口,若是没实现则抛出异常 if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]"); } // 经过反射实例化WebApplicationContext对象 return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); } // 这个方法的主要做用在于读取配置文件中配置的实现了WebApplicationContext接口的类, // 从而做为WebApplicationContext容器。这里读取配置文件的方式有两种:①读取web.xml中配置的 // contextClass属性,若是存在则将其做为WebApplicationContext容器;②读取Spring提供的 // ContextLoader.properties属性文件中配置的WebApplicationContext容器。 protected Class<?> determineContextClass(ServletContext servletContext) { // 读取用户在web.xml中使用contextClass属性自定义的WebApplicationContext容器, // 若是不为空,则直接返回 String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM); if (contextClassName != null) { try { return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader()); } catch (ClassNotFoundException ex) { throw new ApplicationContextException( "Failed to load custom context class [" + contextClassName + "]", ex); } } else { // 若是用户没有自定义WebApplicationContext,则经过defaultStrategies读取 // ContextLoader.properties属性文件中配置的WebApplicationContext, // 这里读取到的具体实现类就是XmlWebApplicationContext contextClassName = defaultStrategies .getProperty(WebApplicationContext.class.getName()); try { return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader()); } catch (ClassNotFoundException ex) { throw new ApplicationContextException( "Failed to load default context class [" + contextClassName + "]", ex); } } }
这里讲到,咱们能够在web.xml中配置自定义的WebApplicationContext,具体的配置方式就是在web.xml中配置以下属性:
<context-param> <param-name>contextClass</param-name> <param-value>mvc.config.MyXmlWebApplicationContext</param-value> </context-param>
经过这种方式咱们就能够实现自定义的WebApplicationContext。对于Spring提供的默认WebApplicationContext实现,其是经过defaultStrategies这个属性读取的,这个属性的初始化是在ContextLoader(ContextLoaderListener继承了该类)中使用static代码块进行初始化的,读者可自行查阅。
在建立了WebApplicationContext对象以后,Spring会对其进行配置和各个组件的初始化,以下是ContextLoader.configureAndRefreshWebApplicationContext()
方法的具体实现:
protected void configureAndRefreshWebApplicationContext( ConfigurableWebApplicationContext wac, ServletContext sc) { // 判断当前WebApplicationContext是否具备统一的id,若是没有,首先会从web.xml中读取, // 具体的使用contextId属性进行制定,该属性的配置方式与上面的contextClass一致,若是没有, // 则经过默认规则声明一个 if (ObjectUtils.identityToString(wac).equals(wac.getId())) { String idParam = sc.getInitParameter(CONTEXT_ID_PARAM); if (idParam != null) { wac.setId(idParam); } else { wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getContextPath())); } } // 获取web.xml中配置的contextConfigLocation属性值,这里也就是咱们前面配置的 // applicationContext.xml,在后面调用refresh()方法时会根据xml文件中的配置 // 初始化Spring的各个组件 wac.setServletContext(sc); String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM); if (configLocationParam != null) { wac.setConfigLocation(configLocationParam); } // 获取当前Spring的运行环境,而且初始化其propertySources ConfigurableEnvironment env = wac.getEnvironment(); if (env instanceof ConfigurableWebEnvironment) { ((ConfigurableWebEnvironment) env).initPropertySources(sc, null); } // 这里customizeContext()方法是一个空方法,供给用户自定义实现ContextLoaderListener时 // 对WebApplicationContext进行自定义 customizeContext(sc, wac); // 这里refresh()方法用于读取上面声明的配置文件,而且初始化Spring的各个组件 wac.refresh(); }
关于WebApplicationContext的配置和初始化,这里主要分为了四个步骤:①为当前WebApplicationContext声明一个id,用于对其进行惟一标识;②读取web.xml中配置的Spring配置文件的位置;③初始化propertySources;④读取Spring配置文件中的内容,而且实例化Spring的各个组件。这里须要说明的是,对于Spring各个组件的初始化,调用的是ConfigurableWebApplicationContext.refresh()
方法,这个方法咱们前面讲解Spring bean注册解析时已经讲解了,读者能够翻阅Spring Bean注册解析(一)和Spring Bean注册解析(二)。
在ConfigurableWebApplicationContext.refresh()
方法调用完成以后,Spring配置文件中的各项配置就都已经处理完成。如此,ContextLoaderListener
的初始化工做也就完成。
对于DispatcherServlet的初始化,这里须要注意的是,在web.xml中咱们配置了load-on-startup
标签,配置了该标签就表示当前Servlet的初始化方法会在web容器启动完成后调用,也就是这里的DispatcherServlet.init()
方法。咱们首先看看该方法的源码:
@Override public final void init() throws ServletException { if (logger.isDebugEnabled()) { logger.debug("Initializing servlet '" + getServletName() + "'"); } // 读取在web.xml中经过init-param标签设置的属性,若是没有配置,这里pvs就会是empty的 PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); if (!pvs.isEmpty()) { try { // 注册Resource对象对应的PropertyEditor BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment())); // 初始化BeanWrapper对象,这里是一个空方法,供给使用者对BeanWrapper进行自定义处理 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; } } // 初始化当前DispatcherServlet的各项配置 initServletBean(); if (logger.isDebugEnabled()) { logger.debug("Servlet '" + getServletName() + "' configured successfully"); } }
能够看到,这里对DispatcherServlet的初始化主要分为两个步骤:①判断当前Servlet中使用使用init-param标签自定义了属性,若是定义了,则将其设置到BeanWrapper中;②初始化DispatcherServlet。这里咱们继续阅读initServletBean()
的源码:
@Override protected final void initServletBean() throws ServletException { getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'"); if (this.logger.isInfoEnabled()) { this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started"); } long startTime = System.currentTimeMillis(); try { // 初始化当前servlet配置的Spring配置 this.webApplicationContext = initWebApplicationContext(); // 这里initFrameworkServlet()方法是一个空方法,供给用户对当前servlet对应的Spring容器 // 进行自定义的处理 initFrameworkServlet(); } catch (ServletException ex) { this.logger.error("Context initialization failed", ex); throw ex; } catch (RuntimeException ex) { this.logger.error("Context initialization failed", ex); throw ex; } if (this.logger.isInfoEnabled()) { long elapsedTime = System.currentTimeMillis() - startTime; this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " + elapsedTime + " ms"); } }
这里initServletBean()
除了进行一些日志记录之外,主要工做仍是委托给了initWebApplicationContext()
方法进行,咱们这里直接阅读该方法的源码:
protected WebApplicationContext initWebApplicationContext() { // 获取在ContextLoaderListener中初始化的Spring容器,而且将其做为当前servlet对应 // 的容器的父容器,这样当前servlet容器就可使用其父容器中的全部内容了 WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; // 若是当前Servlet对应的WebApplicationContext不为空,而且其未被初始化,则对其进行初始化 if (this.webApplicationContext != null) { wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; // 判断当前WebApplicationContext是否已初始化过,没有则进行初始化 if (!cwac.isActive()) { if (cwac.getParent() == null) { cwac.setParent(rootContext); } // 初始化当前WebApplicationContext configureAndRefreshWebApplicationContext(cwac); } } } if (wac == null) { // 若是wac为空,则说明当前servlet对应的WebApplicationContext是空的, // 这里会经过当前servlet配置的contextAttribute属性查找一个自定义的 // WebApplicationContext,将其做为当前servlet的容器 wac = findWebApplicationContext(); } if (wac == null) { // 若是用户没有自定义WebApplicationContext,则建立一个,而且对其进行初始化 wac = createWebApplicationContext(rootContext); } if (!this.refreshEventReceived) { // 这里的onRefresh()方法并非初始化Spring配置文件中的bean的, // 而是用于初始化Spring处理web请求相关的组件的,如RequestMappingHandlerMapping等 onRefresh(wac); } if (this.publishContext) { // 将当前WebApplicationContext对象设置到ServletContext中 String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); if (this.logger.isDebugEnabled()) { this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() + "' as ServletContext attribute with name [" + attrName + "]"); } } return wac; }
这里默认状况下,DispatcherServlet中是不存在已经初始化过的WebApplicationContext的,于是最终仍是会调用createWebApplicationContext()方法进行初始化,在初始化完成以后就会初始化Spring处理web请求的相关组件。咱们首先看createWebApplicationContext()方法的实现:
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) { // 读取web.xml中配置的contextClass属性,将其做为当前servlet的WebApplicationContext Class<?> contextClass = getContextClass(); if (this.logger.isDebugEnabled()) { this.logger.debug("Servlet with name '" + getServletName() + "' will try to create custom WebApplicationContext context of class '" + contextClass.getName() + "'" + ", using parent context [" + parent + "]"); } // 保证用户定义的WebApplicationContext对象是ConfigurableWebApplicationContext类型的 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"); } // 实例化WebApplicationContext对象 ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); // 设置当前的运行环境 wac.setEnvironment(getEnvironment()); // 将ContextLoaderListener中初始化的WebApplicationContext做为当前 // WebApplicationContext的父容器 wac.setParent(parent); // 获取当前servlet配置的contextConfigLocation String configLocation = getContextConfigLocation(); if (configLocation != null) { wac.setConfigLocation(configLocation); } // 读取当前WebApplicationContext配置的Spring相关的bean,并进行初始化 configureAndRefreshWebApplicationContext(wac); return wac; }
这里对当前servlet的WebApplicationContext的初始化过程其实比较简单,其中最主要须要注意的有两点:①会将ContextLoaderListener初始化的WebApplicationContext做为当前WebApplicationContext的父容器;②在获取当前configLocation的时候,若是没有设置,则使用"servlet名称-servlet.xml"的方式读取。
在第三点最后,咱们讲到,初始化servlet对应的容器以后,其会调用onRefresh()方法初始化Spring web相关的组件,该方法的具体实如今DispatcherServlet.onRefresh()
方法中,这里咱们直接阅读该方法的源码:
@Override protected void onRefresh(ApplicationContext context) { initStrategies(context); } protected void initStrategies(ApplicationContext context) { initMultipartResolver(context); initLocaleResolver(context); initThemeResolver(context); initHandlerMappings(context); initHandlerAdapters(context); initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); initViewResolvers(context); initFlashMapManager(context); }
这里对Spring的九大组件的实例化方式都比较统一,关于这九大组件的具体细节咱们后面会依次进行讲解。这里咱们主要讲解其初始化方式。关于这九大组件,其实例化方式可分为两类:
对于第一种实例化方式,咱们这里以LocaleResolver的初始化为例进行讲解,以下是initLocaleResolver()方法的源码:
private void initLocaleResolver(ApplicationContext context) { try { // 这里LOCALE_RESOLVER_BEAN_NAME的值为localeResolver,也就是说用户若是 // 须要自定义的LocaleResolver,那么在声明该bean是,其名称必须为localeResolver this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class); if (logger.isDebugEnabled()) { logger.debug("Using LocaleResolver [" + this.localeResolver + "]"); } } catch (NoSuchBeanDefinitionException ex) { // 若是Spring容器中没有配置自定义的localeResolver,则经过默认策略实例化对应的bean this.localeResolver = getDefaultStrategy(context, LocaleResolver.class); if (logger.isDebugEnabled()) { logger.debug("Unable to locate LocaleResolver with name '" + LOCALE_RESOLVER_BEAN_NAME + "': using default [" + this.localeResolver + "]"); } } }
对于第二种方式,咱们这里以HandlerMapping的实例化为例进行讲解,以下是initHandlerMappings()方法的实现原理:
private void initHandlerMappings(ApplicationContext context) { this.handlerMappings = null; // 检查是否配置了获取Spring中配置的全部HandlerMapping类型对象,是则进行读取,而且按照 // 指定的排序规则对其进行排序,不然就从Spring中读取名称为handlerMapping的bean, // 并将其做为指定的bean if (this.detectAllHandlerMappings) { // 从Spring容器中读取全部的实现了HandlerMapping接口的bean Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerMappings = new ArrayList<>(matchingBeans.values()); // 对获取到的HandlerMapping进行排序 AnnotationAwareOrderComparator.sort(this.handlerMappings); } } else { try { // 获取Spring容器中名称为handlerMapping的bean HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class); this.handlerMappings = Collections.singletonList(hm); } catch (NoSuchBeanDefinitionException ex) { // 忽略当前异常 } } if (this.handlerMappings == null) { // 若是上述方式无法获取到对应的HandlerMapping,则使用默认策略获取对应的HandlerMapping this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); if (logger.isDebugEnabled()) { logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default"); } } }
上述两种初始化Spring web组件的方式中都涉及到一个获取默认的bean的方法,该方法其实是从Spring提供的配置文件指定对应的bean的Class,在读取该文件以后会对其进行实例化,而后返回。对于getDefaultStrategies()方法的实现原理,其实比较简单,咱们这里主要给你们展现Spring提供的组件的配置文件的内容,该配置文件的名称为DispatcherServlet.properties
,以下是该文件的内容:
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
能够看到,这里的配置文件的key就是对应的接口的全路径名,这也就是getDefaultStrategies()方法第二个参数传入的是Class对象的缘由,而value就是该接口对应的实现类,能够有多个。
本文首先讲解了web.xml文件的配置方式,而且着重讲解了该文件中各个配置的意义,接着依次在源码的层面对web.xml中配置的各个组件的初始化方式进行了讲解。