標籤: it |
![]() |
上面的是springMVC的工做原理圖:css
一、客戶端發出一個http請求給web服務器,web服務器對http請求進行解析,若是匹配DispatcherServlet的請求映射路徑(在web.xml中指定),web容器將請求轉交給DispatcherServlet.html
二、DipatcherServlet接收到這個請求以後將根據請求的信息(包括URL、Http方法、請求報文頭和請求參數Cookie等)以及HandlerMapping的配置找處處理請求的處理器(Handler)。java
3-四、DispatcherServlet根據HandlerMapping找到對應的Handler,將處理權交給Handler(Handler將具體的處理進行封裝),再由具體的HandlerAdapter對Handler進行具體的調用。web
五、Handler對數據處理完成之後將返回一個ModelAndView()對象給DispatcherServlet。spring
六、Handler返回的ModelAndView()只是一個邏輯視圖並非一個正式的視圖,DispatcherSevlet經過ViewResolver將邏輯視圖轉化爲真正的視圖View。服務器
七、Dispatcher經過model解析出ModelAndView()中的參數進行解析最終展示出完整的view並返回給客戶端。mvc
-------------------------------------------------------------------------------------------------------------------------------------app
spring MVC主要包括如下要點:框架
1:由DispatcherServlet控制的整個流程;webapp
2:註解驅動的控制器,其中包括請求映射、數據的綁定和格式化;
3:文件上傳;
4:一些雜項,如靜態資源處理、異常處理等等。
這些東西構成了精緻的Spring MVC框架,下面我將針對這些要點作詳細討論,以期其能在開發上對各位觀衆有些做用。
1. Spring MVC框架原理
DispatcherServlet是Spring MVC的靈魂和心臟,它負責接收HTTP請求並協調Spring MVC的各個組件完成請求處理的工做。和任何Servlet同樣,用戶必須在web.xml中配置好DispatcherServlet,而且讓其接受一 切HTTP請求。當用戶的請求被截獲時,DispatcherServlet經過HandlerMapping定位到特定的Controller(使用 @Controller註解的普通Java類,此處已經定位到了具體的業務處理方法了,因此咱們稱其爲Handler)。而後經過 HandlerAdapter調用Handler中對應的業務處理方法(從這裏能夠看出與Struts不一樣的是,Spring MVC是方法級的攔截)。業務處理方法返回一個邏輯視圖名(View)和模型數據(Model,兩者統稱ModelAndView)交給 DispatcherServlet,DispatcherServlet調用ViewResolver解析出真實的視圖對象,獲得這個視圖對象 後,DispatcherServlet就使用Model對其進行渲染,將最後結果返回給用戶。
要了解Spring MVC框架的工做原理,必須回答如下三個問題:
1) DispatcherServlet如何截獲特定的HTTP請求,交由Spring MVC框架處理?
2) 位於Web層的Spring容器(WebApplicationContext),如何與位於業務層的Spring容器(ApplicationContext)創建關聯,以使Web層的Bean能夠調用業務層的Bean?
3) 如何初始化SpringMVC的各個組件,並將它們裝配到DispatcherServlet中?
第一個問題已然在上面的步驟中說明了,很簡單。對於第二條,其實Web層的容器是做爲業務層容器的子容器來配置的,因此訪問不是問題。第三,DispatcherServlet有一個初始化方法initStrategies,它在WebApplicationContext初始化後執行,此時全部的組件Bean均已可用。該方法經過反射機制查找並裝配Spring容器中用戶顯示自定義的組件Bean,若是找不到再裝配默認的組件實例。
怎麼樣,是否是對SpringMVC的工做原理有一個模糊的認識了。到這一步你只要知道Spring MVC也是基於Servlet的,它能夠根據URL直接定位到業務處理方法,同時咱們能夠自然地使用Spring容器,至關之美!
2. 註解驅動的控制器
正如上面所說,SpringMVC能夠直接定位到業務處理方法,那麼咱們提交的數據是否是還要像Servlet那樣經過HTTPServlet來獲取,或 者能夠像Struts同樣綁定到Form中或是Action裏面。固然能夠,不只僅如此,Spring MVC還提供了更增強大的數據綁定和轉化的功能,使之將Struts之類遠遠甩到後面去了。
2.1. 請求映射
在POJO類定義處標註@Controller,再經過<context:component-scan/>掃描相應的類包,便可使POJO 成爲一個能處理HTTP請求的控制器。一個控制器的每個方法均可以成爲請求處理方法,如何將請求映射到控制器的方法中是Spring MVC框架最重要的任務之一,這項任務由@RequestMapping承擔。
<ignore_js_op style="word-wrap: break-word;">
①處的註解很重要,Spring會在啓動的時候掃描它,將其劃入到Handler中去,沒有它一切都白搭。②處的@RequestMapping標註的路 徑是相對於應用系統根路徑的,在此處寫這個註解是爲了同一控制器的多個處理方法負責處理相同業務模塊的不一樣操做,這個註解亦可省略,但建議不要這樣。③處 的註解是必須的,要定位到具體的處理方法中去。上面的顯示列表的URL能夠是這樣:host:port/app/excavation /list.XXX。
@RequestMapping不但支持標準的URL,還支持Ant風格(即?、*和**的字符)的和帶{xxx}佔位符的URL。如下URL都是合法的:
複製代碼
佔位符的URL是Spring 3.0新增的功能,該功能在SpringMVC向REST目標挺進的發展過程當中具備里程碑的意義。經過@PathVariable能夠將URL中的佔位符參數綁定到控制器處理方法的入參中,以下所示:
<ignore_js_op style="word-wrap: break-word;">
除了經過URL進行映射外,咱們還能夠經過請求參數、請求方法和請求頭進行映射,因爲以上方法已經足夠咱們進行一般的開發,因此這裏就再也不詳述其餘映射方法了,有興趣的同事能夠查詢互聯網。
2.2. 數據的綁定
前面說過了,SpringMVC是方法級的映射,那麼Spring是如何處理方法簽名的,又是如何將表單數據綁定到方法參數中的?下面咱們就來討論這個問題。
2.2.1. 處理方法簽名
首先,咱們能夠在方法簽名中放入@CookieValue註解參數,Spring自動將Cookie值綁定到參數中;同理@RequestHeader可 以綁定報文頭的屬性值;同時咱們還能夠將Servlet API如HttpServletRequest、HttpServletResponse、HttpSession、WebRequest直接做爲方法參 數,Spring負責綁定;Spring MVC還容許控制器的處理方法使用java.io.InputStream/java.io.Reader及java.io.OutputStream /java.io.Writer做爲方法的入參,SpringMVC將獲取ServletRequest的InputStream/Reader或 ServletResponse的OutputStream/Writer,而後按類型匹配的方式,傳遞給控制器的處理方法入參;控制器處理方法的入參除 支持以上類型的參數之外,還支持java.util.Locale、java.security.Principal,能夠經過Servlet的 HttpServletRequest的getLocale()及getUserPrincipal()獲得相應的值。若是處理方法的入參類型爲 Locale或Principal,Spring MVC自動從請求對象中獲取相應的對象並傳遞給處理方法的入參。
2.2.1. 表單數據綁定到方法參數
再有,表單的數據只要名稱相同就能夠往方法參數中放,或者是級聯的能夠封裝成對象置於參數中,Spring會自動綁定,以下圖所示,極其地方便簡單:
2.2.3. HttpMessageConverter<T>
最後,還有一類處理方法入參的形式,即便用HttpMessageConverter<T>,這個很是強大。它提供了兩種途徑:
1) 使用@RequestBody/@ResponseBody對處理方法進行標註;
2) 使用HttpEntity<T>/ResponseEntity<T>做爲處理方法的入參或返回值。
HttpMessageConverter顧名思義,它負責將請求信息轉換爲一個對象,或者將對象輸出爲響應信息。前面說過,當請求映射到具體的處理方法後,DispatcherServlet調用HandlerAdapter來封裝並執行處理方法。DispatcherServlet默認已經安裝了AnnotationMethodHandlerAdapter做爲HandlerAdapter的組件實現類,HttpMessageConverter即由AnnotationMethodHandlerAdapter使用,將請求信息轉換爲對象,或者將對象轉換爲響應信息。先看幾個示例:
複製代碼
複製代碼
複製代碼
這裏講一下HttpMessageConverter中的重點@ResponseBody,咱們用它來處理XML和JSON很是之方便。只要在 SpringWeb容器中爲AnnotationMethodHandlerAdapter裝配好相應的處理XML、JSON的 HttpMessageConverter(AnnotationMethodHandlerAdapter默認只裝配部分轉換器),並在交互中經過請求 的Accept指定MIME類型,Spring MVC就可使服務端的處理方法和客戶端透明地經過XML或JSON格式的消息進行通訊了。
<ignore_js_op style="word-wrap: break-word;">
代碼中咱們能夠這樣作:
-wrap:
這部分其實很簡單,對於服務端的處理方法而言,除使用@RequestBody/@ResponseBody或HttpEntity<T>/ResponseEntity<T>進行方法簽名外,不須要進行任何額外的處理,藉由Spring MVC中裝配的HttpMessageConverter,它即擁有了處理XML及JSON的能力了。
3. 文件上傳
Spring MVC爲文件上傳提供了直接的支持,這種支持是經過即插即用的MultipartResolver實現的。
<ignore_js_op style="word-wrap: break-word;">
爲了能使CommonsMultipartResolver正確工做,必須將JakartaCommons FileUpload和Jakarta Commons io的類包添加到類路徑下。下面是代碼的寫法:
<ignore_js_op style="word-wrap: break-word;">
file:///C:\Users\wmq\AppData\Local\Temp\msohtmlclip1\01\clip_image015.jpg
4. 雜項
這裏主要講一下靜態文件的處理。如何訪問到靜態的文件,如jpg,js,css?若是你的DispatcherServlet攔截 *.do這樣的URL,就不存在訪問不到靜態資源的問題。若是你的DispatcherServlet攔截「/」,攔截了全部的請求,同時 對*.js,*.jpg的訪問也就被攔截了。這種狀況下如何搞定靜態文件訪問問題:
方案一:激活Tomcat的defaultServlet來處理靜態文件
複製代碼
要配置多個,每種文件配置一個;要寫在DispatcherServlet的前面,讓defaultServlet先攔截,這個就不會進入Spring了,我想性能是最好的吧。各服務器defaultServlet名稱以下:
複製代碼
方案二:在spring3.0.4之後版本提供了mvc:resources
複製代碼
/images/**映射到ResourceHttpRequestHandler進行處理,location指定靜態資源的位置.能夠是webapplication根目錄下、jar包裏面,這樣能夠把靜態資源壓縮到jar包中。使用<mvc:resources/>元素,把mapping的URI註冊到SimpleUrlHandlerMapping的urlMap中,key爲mapping的URI pattern值,而value爲ResourceHttpRequestHandler,這樣就巧妙的把對靜態資源的訪問由HandlerMapping轉到ResourceHttpRequestHandler處理並返回,因此就支持classpath目錄,jar包內靜態資源的訪問.另外須要注意的一點是,不要對SimpleUrlHandlerMapping設置defaultHandler.由於對static uri的defaultHandler就是ResourceHttpRequestHandler,不然沒法處理static resourcesrequest.
方案三,使用<mvc:default-servlet-handler/>
複製代碼
會把"/**" url,註冊到SimpleUrlHandlerMapping的urlMap中,把對靜態資源的訪問由HandlerMapping轉到DefaultServletHttpRequestHandler處理並返回。DefaultServletHttpRequestHandler使用就是各個Servlet容器本身的默認Servlet。
補充說明:多個HandlerMapping的執行順序問題:
複製代碼
Spring會先執行order值比較小的。當訪問一個a.jpg圖片文件時,先經過DefaultAnnotationHandlerMapping來找處理器,必定是找不到的,咱們沒有叫a.jpg的Action。再按order值升序找,因爲最後一個SimpleUrlHandlerMapping是匹配"/**"的,因此必定會匹配上,再響應圖片。
最後再說明一下,若是你的DispatcherServlet攔截 *.do這樣的URL,就不存上述問題了。