请先关注 [低调大师] 公众号 优秀的自媒体个人博客,低调大师,许军

低调大师

您现在的位置是:首页>文章详情

文章详情

Spring MVC之DispatcherServlet初始化详解

2018-09-28 96热度

       Spring作为一个优秀的web框架,其运行是基于Tomcat的。在我们前面的讲解中,Spring的驱动都是使用的ClassPathXmlApplicationContext,并且都是直接在main方法中启动的,但是在Tomcat容器中,我们是无法使用main方法的,因而其驱动方式必然与我们测试时不一样。Tomcat是一个基于Servlet规范的web容器,而Spring则提供了对Servlet规范的支持,其DispatcherServlet则是Servlet规范的具体实现。因而在web开发过程中,当我们启动Tomcat容器时其会根据Servlet规范启动Spring实现的DispatcherServlet,这样也就驱动了Spring的运行。本文主要从源码的角度讲解Spring在web容器中是如何初始化的。

1. web.xml配置

       在配置web容器时,我们都会配置一个web.xml,而在配置web.xml时,最主要的两个组件就是ContextLoaderListenerDispatcherServlet的配置。如下是一个典型的web.xml文件的配置:

<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接口,这个接口声明如下:

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初始化之前就已经完成了。

       web.xml中配置的DispatcherServlet则是Servlet规范中HttpServlet的一个具体实现,实现了该接口的之后该类就具有处理web请求的能力了。这里可以看到,DispatcherServlet配置拦截的url是'/',也就是说所有的web请求都会经过DispatcherServlet,而对于具体的url的处理,实际上是在DispatcherServlet中进行分发的。这也就是Spring为什么只需要配置一个Servlet的原因。

       关于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容器中。

       在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声明而来的。

2. ContextLoaderListener初始化

       对于ContextLoaderListener,其主要是用于初始化我们常用的applicationContext.xml的。如下是其源码:

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()方法进行的,如下是该方法的源码:

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的初始化工作也就完成。

3. DispatcherServlet的初始化

       对于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"的方式读取。

4. Spring web九大组件初始化

       在第三点最后,我们讲到,初始化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的九大组件的实例化方式都比较统一,关于这九大组件的具体细节我们后面会依次进行讲解。这里我们主要讲解其初始化方式。关于这九大组件,其实例化方式可分为两类:

  • 通过制定的bean名称在Spring容器中读取对应的bean,如果不存在则使用默认的类来初始化;
  • 通过参数配置控制是在Spring容器中读取指定实现指定接口的所有bean,还是读取Spring容器中指定名称的bean,如果这两种方式都无法读取到对应的bean,则读取Spring配置文件中配置的默认的bean。

       对于第一种实例化方式,我们这里以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就是该接口对应的实现类,可以有多个。

5. 小结

       本文首先讲解了web.xml文件的配置方式,并且着重讲解了该文件中各个配置的意义,接着依次在源码的层面对web.xml中配置的各个组件的初始化方式进行了讲解。

收藏 (0)

相关文章

    文章评论

    共有0条评论来说两句吧...