本系列文章順延「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