上一篇文章《SpringMVC源碼剖析(一)- 從抽象和接口提及》中,我介紹了一次典型的SpringMVC請求處理過程當中,相繼粉墨登場的各類核心類和接口。我刻意忽略了源碼中的處理細節,只列出最簡單的類甚至是接口類,目的就是讓你們先從最高層次的抽象意義上來審視SpringMVC這個框架;我也刻意將SpringMVC和Struts2作對比,目的是讓你們看到,SpringMVC究竟吸收了Sturts2設計思想中的哪些精華,又彌補了它的哪些遺憾。 前端
DispatcherServlet做爲SpringMVC的核心之中的核心類,再怎麼強調它的重要性也不爲過。SpringMVC全部的核心類和接口,都密集地出如今DispatcherServlet的源碼中,SpringMVC源碼剖析,很大程度上能夠說也是在剖析DispatcherServlet這一個類。這一篇文章裏,我先說幾點關於DispatcherServlet的前世此生,但願能幫助你更好的理解它。 java
1.對擴展開放,對修改封閉 web
SpringMVC是一個基於著名的Open-Closed,即開閉原則進行設計的框架。在Spring官方文檔裏面關於SpringMVC的介紹開宗明義地進行了說明: spring
A key design principle in Spring Web MVC and in Spring in general is the 「Open for extension,closed for modification」 principle.
開閉原則是一個很寬泛的原則,具體體現到DispatcherServlet的源碼中,咱們能夠大體摸獲得一些線索: 設計模式
其中第一點,在一個框架的設計中尤其重要,也是貫徹開閉原則最重要的一點。由於當你經過一些高層次的接口或者抽象類,將一個類完成的邏輯或流程編寫完成後(具體點說,是經過一個接口的引用調用接口方法),整個邏輯或流程的功能就被確實的在源碼中固定下來了。但是這時,這些接口或抽象類的具體實現者是誰,尚未固定!這就給了你的系統或框架近乎無限的擴展性,由於你能夠任意安排和實現這些類。 服務器
我認爲,面向對象設計的精髓,是對現實世界中「行爲和契約」的描述。這個「行爲和契約」,體如今接口和抽象類的方法聲明中。軟件設計師要用面向對象的眼光去觀察和抽象這個世界中的事物,這裏的事物能夠是一些商業邏輯、能夠是一些處理流程,而後用高層次的接口去描述這些行爲和契約。當你在越抽象的層次上將這些行爲和契約描述清楚後,你所設計的系統就是越符合開閉原則的。 session
SpringMVC框架在面向對象設計上,作出了絕佳的示範。它經過高度抽象的接口,描述出了一次請求處理的流程,從而讓整個框架從一開始就是符合開閉原則的。同時它也提供了這些接口的一系列默認實現類,讓你不須要很複雜的配置,就能很好的使用SpringMVC進行開發。抽象的確是個利器,可是框架毫不能運行在空中樓閣中,SpringMVC提供的的這一系列默認實現類必需要有容身之所。聰明的你可能早已想到:Spring IOC容器。這就引出了我要說的第二點。 架構
2.配置元素的對象化 app
全部的框架,都須要有這樣一個功能,叫作:配置元素的對象化。由於幾乎全部的框架,都將配置元素集中到外部的xml配置文件中,而後在框架的初始化流程中,對這些配置文件進行解析,再變成java世界中的一個個對象供框架使用,這整個過程,能夠被稱爲配置元素的對象化。爲何要有配置文件呢?這個問題的回答也是很簡單,由於沒有人會想要使用一個配置散佈在框架中各個java類源碼裏面的框架。框架也不容許使用者這樣子作,由於框架在發佈的時候,提供的是一個個jar包文件,jar包內是已經編譯好的class文件。配置文件由使用者外部提供,框架對它進行解析,使用者能獲得集中配置的好處,框架也樂於這樣子,能夠說是合情合理。 框架
那麼做爲Spring產品族的新成員,SpringMVC在設計的時候,相信設計者們不作它想,這一個「配置元素的對象化」功能既然不可避免,那麼使用Spring IOC容器,經過bean配置文件來配置SpringMVC,絕對是不二之選。不可能像Struts2同樣,內部再搞一個別的容器,由於Spring容器自己已是被高度設計,並且已經在java世界得到巨大成功。從推廣的角度上來講,若是對spring容器的全部知識,均可以完整的應用到SpringMVC,那麼對於開發者無疑是一個極大的吸引力。
剩下的問題就只有:到底該如何將Spring容器和SpringMVC的初始化過程整合起來呢?
答案就是WebApplicationContext接口,更具體點說,是XmlWebApplicationContext這個Spring上下文實現類。SpringMVC也使用了這一個爲了將Spring容器和Web環境整合而特地設計的Spring上下文類。咱們打開WebApplicationContext的源碼:
package org.springframework.web.context; import javax.servlet.ServletContext; import org.springframework.context.ApplicationContext; public interface WebApplicationContext extends ApplicationContext { String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT"; String SCOPE_REQUEST = "request"; String SCOPE_SESSION = "session"; String SCOPE_GLOBAL_SESSION = "globalSession"; String SCOPE_APPLICATION = "application"; String SERVLET_CONTEXT_BEAN_NAME = "servletContext"; String CONTEXT_PARAMETERS_BEAN_NAME = "contextParameters"; String CONTEXT_ATTRIBUTES_BEAN_NAME = "contextAttributes"; ServletContext getServletContext(); }
發現它是繼承於ApplicationContext這個普通Spring容器所使用的上下文接口類,除了一些常量的聲明,只多了一個能夠獲取到ServletContext的getServletContext()方法。回到上面提到的「行爲和契約的描述」上,咱們能夠大膽的斷言,Spring容器和Web環境的整合,是在ServletContext上作文章。
打開全部使用了Spring的Web項目的web.xml文件,一定有這樣一段配置:
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>ContextLoaderListener實現了ServletContextListener接口,在Servlet容器啓動的時候,會初始化一個WebApplicationContext的實現類,並將其做爲ServletContext的一個屬性設置到Servlet環境中,摘抄源碼以下:
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE的值,在上面WebApplicationContext的源碼中的第一個常量中就被聲明,是WebApplicationContext.class.getName() + ".ROOT",更直接一點,它是「org.springframework.web.context.WebApplicationContext.ROOT」。ContextLoaderListener所初始化的這個Spring容器上下文,被稱爲根上下文。
SpringMVC在DispatcherServlet的初始化過程當中,一樣會初始化一個WebApplicationContext的實現類,做爲本身獨有的上下文,這個獨有的上下文,會將上面的根上下文做爲本身的父上下文,來存放SpringMVC的配置元素,而後一樣做爲ServletContext的一個屬性,被設置到ServletContext中,只不過它的key就稍微有點不一樣,key和具體的DispatcherServlet註冊在web.xml文件中的名字有關,從這一點也決定了,咱們能夠在web.xml文件中註冊多個DispatcherServlet,由於Servlet容器中註冊的Servlet名字確定不同,設置到Servlet環境中的key也確定不一樣。
因爲在Spring容器中,子上下文能夠訪問到全部父上下文中的信息,而父上下文訪問不到子上下文的信息,這個根上下文,就很適合做爲多個子上下文配置的集中點。以官方文檔中的圖來講明:
3.前端控制器
前端控制器,即所謂的Front Controller,體現的是設計模式中的前端控制器模式。前端控制器處理全部從用戶過來的請求。全部用戶的請求都要經過前端控制器。SpringMVC框架和其餘請求驅動的表示層框架同樣,也是圍繞一個將請求分發到相應控制器的核心Servlet來設計的。DispatcherServlet和其餘框架中的Servlet不同的地方在於,它和Spring容器無縫整合在了一塊兒,所以你能夠在SpringMVC中使用Spring容器全部的特性。
DispatcherServlet這個前端控制器,在SpringMVC中的做用,以官方文檔中的配圖來講明:
整個流程能夠被大體描述爲:一個http請求到達服務器,被DispatcherServlet接收。DispatcherServlet將請求委派給合適的處理器Controller,此時處理控制權到達Controller對象。Controller內部完成請求的數據模型的建立和業務邏輯的處理,而後再將填充了數據後的模型即model和控制權一併交還給DispatcherServlet,委派DispatcherServlet來渲染響應。DispatcherServlet再將這些數據和適當的數據模版視圖結合,向Response輸出響應。
能夠看到Model-View-Controller這三樣東西協同合做,共同體現出MVC的設計理念,三個層次能夠分別獨立演化,整個系統架構又清晰又簡潔。這是SpringMVC爲咱們描述的美好願景,後面咱們也將看到,SpringMVC爲了實現這一承諾,究竟作出了什麼樣的努力。