程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> (spring-第21回【MVC基礎篇】)SpringMVC一點就通,spring-springmvc

(spring-第21回【MVC基礎篇】)SpringMVC一點就通,spring-springmvc

編輯:JAVA綜合教程

(spring-第21回【MVC基礎篇】)SpringMVC一點就通,spring-springmvc


概述

  • Spring MVC通過一套MVC注解,讓POJO變成處理請求的控制器,無需實現任何接口,同時,SpringMVC還支持REST風格的URL請求:注解驅動和REST風格的Spring MVC是Spring 3.0最出彩的功能之一。
  • Spring MVC框架圍繞 DispatcherServlet這個核心展開,DispatcherServlet是SpringMVC的總導演,它負責截獲請求,並將其分派給相應的處理器處理。
  • 和大多數WEB MVC一樣,Spring MVC通過一個前端Servlet接收所有的請求,並將具體工作委托給其他組件進行處理,DispatcherServlet就是Spring MVC的前端Servlet。

本文主要講了一些什麼

 

Spring MVC的基本原理  -->

用一個簡單的示例對基本原理有個具體了解 -->

分別描述MVC的幾個重量級角色:過濾請求、綁定請求、封裝對象、處理XML和JSON、處理模型數據。

 

本篇文章重在知識點整合,作為讀書筆記,語言簡潔,刪繁就簡,由淺入深,引人入勝。通讀本文,你將對Spring MVC有一個深入的了解。如果讀,請深讀。如果愛,請讀完。

 

基本原理

要了解Spring MVC框架的工作機理,必須回答下面三個問題:

問題一:DispatcherServlet框架如何截獲特定的HTTP請求,交由Spring MVC框架處理?

答:在web.xml中配置一個Servlet,並通過<servlet-mapping>指定其處理的url。像下面這樣:

那麼,所有帶.html後綴的http請求都會被DispatcherServlet截獲並處理。

問題二:位於Web層的Spring容器(WebApplicationContext)如何與位於業務層的Spring容器(ApplicationContext)相關聯,以使web層的bean可以調用業務層的bean?

答:下面是一個web.xml:

ContextLoaderListener是一個ServletContextListener,它通過contextConfigLocation參數指定的配置文件(如本例的applicationContext.xml)啟動“業務層”的Spring容器。而由下面引入的DispatcherServlet啟動Web層的spring容器。Web層的spring容器將作為業務層容器的子容器(即:Web層容器可以引用業務層容器的bean,而業務層容器不可以引用Web層容器的bean。)

問題三:如何初始化Spring MVC的各個組件,並將它們裝配到DispatcherServlet中?

答:當DispatcherServlet加載後,就會自動掃描上下文的bean,根據名稱或者類型匹配的機制查找自定義的組件,找不到時則使用DispatcherServlet.properties中使用的默認組件。

 

一個簡單的實例

 

Spring MVC的開發一般包括以下步驟:

步驟一 :配置web.xml,指定業務層對應的配置文件,定義DispatcherServlet:

步驟二:編寫處理請求的控制器(處理器):

@Controller注解將一個POJO類轉化為處理請求的控制器,通過@RequestMapping指定控制器處理哪些請求。上例中,處理/user/register請求,返回user/register視圖(當然,該視圖字符串加上前後綴才能定位到具體JSP,後面步驟會講到)。

步驟三:編寫視圖對象(比如JSP):

步驟四:配置Spring MVC的配置文件(如applicationContext.xml):

1是默認的視圖解析器,2是視圖解析器加的前後綴,它與步驟二返回的user/register拼起來:/WEB-INF/views/user/register.jsp,表示步驟二將返回這個視圖,即:把請求交給這個jsp來處理。

 

過濾請求

使用@RequestMapping可以過濾請求URL、請求參數、請求方法,請求頭這4個方面的信息項。比如:

 

 

綁定請求信息到方法入參中

Spring MVC會將HTTP請求的信息綁定到相應的方法入參中,並根據方法返回值類型作出相應的後續處理。比如:

 

 

將請求信息封裝成對象

使用HttpMessageConverter<T>可以將請求信息轉換成一個對象,或者將對象輸出為響應信息。(T就是對象類型)。

兩把利劍:

1.使用RequestBody、ResponseBody。

2.使用HttpEntity<T>/ResponseEntity<T>

示例1:

客戶端:

服務端:

 

示例2:

客戶端:

 

服務端:

 

與RequestBody、ResponseBody不同的是,HttpEntity不僅可以訪問請求、響應報文體的數據,還可以訪問請求和響應報文頭的數據。

示例1:

客戶端:

 

服務端:

 

示例2:

客戶端:

服務端:

怎麼做到的?

 

HttpMessageConverter<T>提供了眾多的實現類,它組成了一個功能強大、用途廣泛的HttpMessageConverter<T>家族。

 

比如:

 

  StringHttpMessageConverter負責將請求信息轉換成字符串;

 

  FormHttpMessageConverter負責將表單數據讀取到MultiValueMap中;

 

  還有諸如讀寫XML、Resource、JSON等等對應的實現類。

 

  AnnotationMethodHandlerAdapter適配器默認裝配了基本的Converter(String、ByteArray、Source、XmlAwareForm),容器會根據需要自動調用不同的HttpMessageConverter實現類。

  如果不能滿足需求,那麼繼續在xml中配置其他的converter:

 

 

處理XML和JSON

 

從上文可知,HttpMessageConverter有一個很強大的家族,能夠處理各種格式的請求及響應。當然也可以處理XML和JSON。

 

處理XML和JSON的Converter分別是:

 

MarsHallingHttpMessageConverter(處理xml)。

 

Jaxb2RootElementHttpMessageConverter(同上,底層使用JAXB)。

 

MappingJacksonHttpMessageConverter(處理JSON)。

 

按照前文的思路,首先要在配置文件中裝配好相應的Converter,但是還有其他必要的步驟:

步驟1:XML中配置Converter以及Marshaller:

步驟2:測試客戶端發出請求:

步驟3:響應(服務)端:

步驟4:User的注解:

步驟5:用tcptrace監聽測試

其實,如果把第92行和第93行代碼的頭部格式換成json,就變成傳送json格式的數據了。這個時候,用tcptrace監聽傳送json的結果:

可以看到,請求中,我們的報文頭格式變成了json,報文體也是json格式的。而響應中報文體也是json格式的。

為什麼我直接截圖了json格式的測試結果而沒有測試請求xml的測試結果呢?

因為在xml測試時,報出了415 Unsupported media type的錯誤,尚未發現原因。

但是思路就是這樣了。 讀者如果知道“不支持此類型”的原因,歡迎評論留言。

 

 

處理模型數據

 

 

我們在Controller控制器中接收請求參數,把處理後得到的數據封裝起來,然後返回到一個JSP,在JSP中就可以獲取封裝的數據。那麼,在這個例子中,封裝的數據就是模型數據,而JSP就是渲染模型數據的視圖。這在MVC框架中是最重要的步驟。

在控制器方法中輸出模型數據有四種方法:

方法一:ModelAndView

在控制器方法中新建ModelAndView對象,把“視圖”和“模型數據”封裝到該對象中,然後返回該對象即可。

方法二:@ModelAttribute

加到入參的前面,就自動把入參綁定為模型數據;加到方法A的前面,那麼Spring MVC就會在執行任何目標處理方法之前,先執行方法A(B\C等等),然後把這些加了標注的方法的返回值綁定為模型數據。

舉例1:

Spring MVC會先把模型數據儲存到ServletRequest的屬性列表中(使用setAttribute保存)。

然後在返回的jsp(視圖對象)中,使用${user.userName}來訪問user的屬性。

舉例2:

在執行handle62之前,spring掃描到有方法頭部標注了@ModelAttribute,那麼,spring就會先去執行getUser,並把返回值user作為模型數據,鍵為”user”。

這時執行handle62,入參前面的@ModelAttribute鍵為”user”,正好匹配了剛封裝的模型數據的鍵,那麼就把模型數據賦值給入參User user,然後285行對user做了覆蓋操作,最後交給視圖層處理。

方法三:Map and Model

如果處理方法的入參類型為Map或者Model(或者具備二者功能的對象如:ModelMap),那麼spring會將隱含的模型數據傳給該入參,在處理方法中,我們可以從該入參中取出隱含的模型數據,也可以繼續添加模型數據。

標注了@ModelAttribute的方法的返回值(getUser方法返回的user)就是隱含的模型數據。

由於handle63的入參為ModelMap類型,所以,spring會把user這個隱含模型數據授予modelMap,在300行,我們通過該入參取出模型數據,在302行,我們通過該入參繼續添加模型數據。

方法四:@SessionAttributes

如果Controller類的頭部標注了@SessionAttributes(”xxx”),那麼spring會把鍵為xxx的模型數據暫存到HttpSession中,以便多個請求之間可以共享該數據。

 

... ...

61行的標注表示:後面處理器在處理任何方法時,看到屬性名為user的模型屬性就會把它存到session中共享。由於328行把user作為了模型數據,那麼這個user就放到了session中。

當轉發請求到handle72時,就可以從modelMap中拿到該對象。

當使用完成時,在338行表示把該對象從session中移除。否則它會一直存在。

然而程序是跑不起來的,會報HttpSessionRequiredException。為什麼呢?

這是因為,如果有了61行標注的會話屬性user,那麼spring就會嘗試從會話中獲取該屬性,然後把它賦給入參。

也就是說,spring看到61行的標注,會從會話中找user為鍵的模型數據,找到後把它賦值給334行的modelMap。

但是現在從代碼看來,在執行61行標注之前,會話中並沒有鍵為user的模型數據。

所以,解決方法很簡單:

只需要像方法二那樣,加一個:

 

就好了。這樣,在執行61行之前,先執行getUser,把user作為隱含模型數據。那麼執行61行時,該會話中就已經有user了。

當你讀到這裡,你已經百毒不侵了。親,不點個贊嗎?

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved