本系列文章顺延“Spring源码解析”,是在“父容器”建立完成后,对“子容器”(SpringMVC)建立,以及请求处理的解析。java
提及 SpringMVC,DispatcherServlet 应该是最熟悉的类之一。它几乎掌控着整个请求的分发以及最终响应,咱们就从它来追溯“子容器”建立的源头。web
public abstract class GenericServlet implements Servlet, ServletConfig, java.io.Serializable { // getInitParameter可获取配置的 <init-param> // getServletName可获取配置的 <servlet-name> private transient ServletConfig config; // SpringMVC启动入口 @Override public void init(ServletConfig config) throws ServletException { this.config = config; // 子类实现 this.init(); } }
该类就是 DispatcherServlet 的最顶层抽象父类了,它实现了 Servlet.init 方法,经过 Servlet 规范咱们能够了解到,根据 <load-on-startup> 配置的不一样,调用时机也不一样。mvc
public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware { // GenericServlet.init调用 @Override public final void init() throws ServletException { if (logger.isDebugEnabled()) { logger.debug("Initializing servlet '" + getServletName() + "'"); } // 将 <init-param>键值对封装成 PropertyValue PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); if (!pvs.isEmpty()) { try { // 将自身(DispatcherServlet)封装成 BeanWrapper,方便 Spring注入 BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); // 注册解析器,对于 Resource类型的属性用 ResourceEditor解析 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; } } // 子类扩展实现 initServletBean(); if (logger.isDebugEnabled()) { logger.debug("Servlet '" + getServletName() + "' configured successfully"); } } }
这一层主要是针对配置属性相关的代码,若是看过以前“实例建立”章节,对 bw.setPropertyValues(pvs, true) 应该不陌生,就是为 Servlet 作属性填充。接下来 initServletBean 方法实现了几乎“子容器”建立的大部分工做,由 FrameworkServlet 实现:app
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware { // 可用 <init-param>:contextClass指定 private Class<?> contextClass = DEFAULT_CONTEXT_CLASS; // 默认 XmlWebApplicationContext public static final Class<?> DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class; @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 { // 初始化容器 this.webApplicationContext = initWebApplicationContext(); // 空实现:可自定义扩展 initFrameworkServlet(); }....// 省略 catch ....// 省略日志 } protected WebApplicationContext initWebApplicationContext() { // 获取父容器:经过调用 ServletContext.getAttribute(ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) // 设置逻辑见容器启动: ContextLoader.initWebApplicationContext WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; // 是经过构造器传入的方式会走这个分支(见 SpringBoot) if (this.webApplicationContext != null) { wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; // 调用 refresh会置 active为 true if (!cwac.isActive()) { if (cwac.getParent() == null) { // 设置父容器 cwac.setParent(rootContext); } configureAndRefreshWebApplicationContext(cwac); } } } if (wac == null) { // 查找 contextAttribute是否已绑定的上下文 key wac = findWebApplicationContext(); } if (wac == null) { // 若是尚未找到上下文,建立一个 wac = createWebApplicationContext(rootContext); } if (!this.refreshEventReceived) { // 子容器(SpringMVC),在容器刷新完毕后执行 onRefresh(wac); } if (this.publishContext) { 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; } protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { return createWebApplicationContext((ApplicationContext) parent); } protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) { // 默认 XmlWebApplicationContext // 可用<init-param>:contextClass指定 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 + "]"); } 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); // 获取 <servlet>中配置的 <init-param>:contextConfigLocation String configLocation = getContextConfigLocation(); if (configLocation != null) { // 设置“mvc”配置文件所在路径 wac.setConfigLocation(configLocation); } // 和父容器类似的套路 configureAndRefreshWebApplicationContext(wac); return wac; } protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) { if (ObjectUtils.identityToString(wac).equals(wac.getId())) { // 设置 ID if (this.contextId != null) { wac.setId(this.contextId); } else { wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName()); } } // Servelt相关的设置 wac.setServletContext(getServletContext()); wac.setServletConfig(getServletConfig()); wac.setNamespace(getNamespace()); // 注册事件监听器:被 SourceFilteringListener包裹,仅在“子容器”(mvc)中触发 // ContextRefreshListener:触发 FrameworkServlet.onApplicationEvent // onApplicationEvent进而调用:onRefresh wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener())); // 任何状况下 initPropertySources都在上下文刷新时被调用 ConfigurableEnvironment env = wac.getEnvironment(); if (env instanceof ConfigurableWebEnvironment) { ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig()); } // 空实现 postProcessWebApplicationContext(wac); // ApplicationContextInitializer执行,由如下属性指定 // <init-param>:globalInitializerClasses、contextInitializerClasses指定 applyInitializers(wac); // 容器刷新:AbstractApplicationContext实现(同父容器) wac.refresh(); } }
抛开一些设置属性的步骤不谈,上面的逻辑主要能够总结为:容器实例化(反射调用构造器)——>将<init-param>指定的“mvc”配置文件设置到容器,以便随后的解析——>而后注册了一个“容器刷新”事件监听器,以便回调——>调用 AbstractApplicationContext.refresh 刷新容器——>最后回调 onRefresh。框架
这里的 onRefresh 在 FrameworkServlet 中是空实现,由子类定制实现。来看看 DispatcherServlet 的实现吧。ide
public class DispatcherServlet extends FrameworkServlet { // 在 refresh中的 finishRefresh中会发布“刷新”事件 // 会触发 ContextRefreshListener监听器,继而调用该方法 @Override protected void onRefresh(ApplicationContext context) { initStrategies(context); } // 主要初始化了 mvc中所必须的成员变量 protected void initStrategies(ApplicationContext context) { // 若是定义了 MultipartResolver,实例化 initMultipartResolver(context); // 若是定义了 LocaleResolver,实例化 // 默认使用 AcceptHeaderLocaleResolver initLocaleResolver(context); // 若是定义了 ThemeResolver,实例化 // 默认使用 FixedThemeResolver initThemeResolver(context); // 初始化全部的 HandlerMapping:具体处理器 // 初始化参数 detectAllHandlerMappings设为 false能够只加载指定的bean initHandlerMappings(context); // 初始化全部的 HandlerAdapter // 初始化参数 detectAllHandlerAdapters设为 false能够只加载指定的bean initHandlerAdapters(context); // 初始化全部的 HandlerExceptionResolver(异常处理) initHandlerExceptionResolvers(context); // 若是定义了 RequestToViewNameTranslator,实例化 // 默认使用 DefaultRequestToViewNameTranslator initRequestToViewNameTranslator(context); // 初始化全部的 ViewResolver // 默认使用 InternalResourceViewResolver initViewResolvers(context); // 若是定义了 FlashMapManager,实例化 // 默认使用 DefaultFlashMapManager initFlashMapManager(context); } }
以上的这些就是“子容器”和“父容器”差别之处了,支持了“mvc”框架例如文件上传、请求映射、异常处理、视图解析等功能。咱们会在以后的章节展开讲解。post
本篇主要梳理了“子容器”(SpringMVC)的初始化脉络,顶层父类留下可扩展的“口子”,让子类去实现具体的初始化逻辑。ui