SpringMvc啓動源碼解析

1. 前言

  上篇文章介紹了Spring容器的初始化,接下來介紹SpringMvc容器的初始化web

2. 初始化化過程

  上文講過一個Web項目的啓動在加載listener、fliter初始化後,再進行servlet初始化。那SpringMvc如何與Servlet聯繫起來?看web.xml配置文件,有一個專門配置SpringMvc的servlet,就是DispatcherServlet。看下DispatcherServlet類繼承關係面試

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

 

   如上圖,DispatcherServlet本質上是一個Servlet。DispatcherServlet類的設計很巧妙,上層父類不一樣程度的實現了相關接口的部分方法,並留出了相關方法用於子類覆蓋,將不變的部分統一實現,將變化的部分預留方法用於子類實現。對Servlet有必定了解的,Servlet初始化會首先調用init()方法。子類最後重寫init()的是HttpServletBean,因此最開始對HttpServletBean的init()方法進行分析設計模式

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

 

   PropertyValues主要解析web.xml定義中<servlet>元素的子元素<init-param>中的參數值。見上圖,有一個鍵值對就是SpringMvc的配置文件。bw.setPropertyValues(pvs, true) 將上一步解析的servlet初始化參數值綁定到DispatcherServlet對應的字段上;app

  接着就是執行initServletBean方法,由於HttpServletBean中的initServletBean就是個空方法,經過觀察上述類圖,發現子類FrameworkServlet重寫了其initServletBean。因而對FrameworkServle的initServletBean進行分析ide

  •  
@Overrideprotected 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 (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");}}

  該方法中比較重要的就是initWebApplicationContext()方法的調用,該方法仍由FrameworkServlet抽象類實現,繼續查看其源碼以下所示:函數

  •  
protected WebApplicationContext initWebApplicationContext() {/*獲取由ContextLoaderListener建立的根IoC容器獲取根IoC容器有兩種方法,還可經過key直接獲取*/WebApplicationContext rootContext =WebApplicationContextUtils.getWebApplicationContext(getServletContext());WebApplicationContext wac = null;
if (this.webApplicationContext != null) {// A context instance was injected at construction time -> use itwac = 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, etcif (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/*若是當前Servelt存在一個WebApplicationContext即子IoC容器而且上文獲取的根IoC容器存在,則將根IoC容器做爲子IoC容器的父容器 */cwac.setParent(rootContext);}//配置並刷新當前的子IoC容器,功能與前文講解根IoC容器時的配置刷新一致,用於構建相關BeanconfigureAndRefreshWebApplicationContext(cwac);}}}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//若是當前Servlet不存在一個子IoC容器則去查找一下wac = findWebApplicationContext();}if (wac == null) {// No context instance is defined for this servlet -> create a local one//若是仍舊沒有查找到子IoC容器則建立一個子IoC容器wac = createWebApplicationContext(rootContext);}
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方法完成「可變」的初始化過程onRefresh(wac);}
if (this.publishContext) {// Publish the context as a servlet context attribute.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;}

  該方法的主要做用一樣是建立一個WebApplicationContext對象,即Ioc容器,上文咱們已經建立過一個根Ioc容器,即Spring容器。Web第一次啓動時,經過Debug,會執行wac = createWebApplicationContext(rootContext);將根IOC容器做爲參數,調用createWebApplicationContex建立一個子IOC容器學習

  這裏簡單提一下父子IOC容器,父子容器相似於類的繼承關係,子類能夠訪問父類中的成員變量,而父類不可訪問子類的成員變量,一樣的,子容器能夠訪問父容器中定義的Bean,但父容器沒法訪問子容器定義的Bean。在一個SpringMvc項目中,父容器一般就是咱們所說的Spring容器,它是加載Spring.xml配置文件,來管理Spring.xml中的Bean,這些Bean是全局共享的,即在任何當前容器或子容器中都能使用,咱們通常配置Service,dao等bean。Service類中能夠調用其餘Service,dao。子容器一般是咱們所說的SpringMvc容器,它所配置的Bean只能被當前子容器使用,但可使用父容器的Bean。咱們通常在子容器配置Controller、Interceptor等重要組件。這也就說明了咱們爲何能夠在Controller中使用service或dao,而反過來不行this

  watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

 

  接下來繼續看createWebApplicationContex源碼:spa

  •  
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {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);wac.setConfigLocation(getContextConfigLocation());
configureAndRefreshWebApplicationContext(wac);
return wac;}

  該方法用於建立一個子IoC容器並將根IoC容器作爲其父容器,接着進行配置和刷新操做用於構造相關的Bean。至此,根IoC容器以及相關Servlet的子IoC容器已經配置完成。子IOC容器配置完成後,調用onRefresh(wac)方法,經過類圖可知,onRefresh具體實現是由DispatcherServlet類實現debug

  •  
@Overrideprotected void onRefresh(ApplicationContext context) {initStrategies(context);}
/*** Initialize the strategy objects that this servlet uses.* <p>May be overridden in subclasses in order to initialize further strategy objects.*/protected void initStrategies(ApplicationContext context) {initMultipartResolver(context);initLocaleResolver(context);initThemeResolver(context);initHandlerMappings(context);initHandlerAdapters(context);initHandlerExceptionResolvers(context);initRequestToViewNameTranslator(context);initViewResolvers(context);initFlashMapManager(context);}

  摘抄一段評論:onRefresh()方法直接調用了initStrategies()方法,源碼如上,經過函數名能夠判斷,該方法用於初始化建立multipartResovle來支持圖片等文件的上傳、本地化解析器、主題解析器、HandlerMapping處理器映射器、HandlerAdapter處理器適配器、異常解析器、視圖解析器、flashMap管理器等,這些組件都是SpringMVC開發中的重要組件,相關組件的初始化建立過程均在此完成。

3. 總結

  在Debug源碼中,涉及到了不少設計模式,想起校招面試時面試官問我,你知道Spring源碼中有哪些設計模式嗎,哈哈哈,一臉懵逼,不過如今也是。看來之後得好好學習設計模式了。

  至此,對Tomcat啓動一個Spring項目已有了大概認知,仍是很開心。小白進階之路任重而道遠。

相關文章
相關標籤/搜索