JavaEE框架---SpringMVC

 

1.Spring入門

1.1 Springmvc是什麼

Spring web mvc和Struts2都屬於表現層的框架,它是Spring框架的一部分,我們可以從Spring的整體結構中看得出來,如下圖:

1.2 Springmvc處理流程

 

1.3  入門程序(Hello world)

  • 步驟:
    • 加入 jar – 包
    • 在 web.xml 中配置 DispatcherServlet –
    • 加入 Spring MVC 的配置文件 –
    • 編寫處理請求的處理器,並標識爲處理器 –
    • 編寫視圖

1.3.1  創建web工程

1.3.2  導入jar包

 

1.3.3 加入配置文件

1.3.3.1  創建springmvc.xml

創建SpringMVC的核心配置文件

SpringMVC本身就是Spring的子項目,對Spring兼容性很好,不需要做很多配置。

這裏只配置一個Controller掃描就可以了,讓Spring對頁面控制層Controller進行管理。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">

	<!-- 配置controller掃描包=================== -->
	<context:component-scan base-package="com.itheima" />

	<!-- 配置處理器映射器,默認的已經廢棄 -->
	<!-- <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping" /> -->
	<!-- 配置處理器適配器,默認的已經廢棄 -->
	<!-- <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter" /> -->
	
	<!-- 註解驅動,替代上面兩個====================== -->
	<mvc:annotation-driven/>
	
	<!-- 配置視圖解析器 ==========================-->
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<!-- 配置邏輯視圖的前綴 -->
		<property name="prefix" value="/WEB-INF/views/"/>
		<!-- 配置邏輯視圖的後綴 -->
		<property name="suffix" value=".jsp"/>
	</bean>
	
</beans>

1.3.3.2. 配置前端控制器

配置 DispatcherServlet :DispatcherServlet 默認加載 /WEB-INF/<servletName-servlet>.xml 的 Spring 配置文件, 啓動 WEB 層
的 Spring 容器。可以通過 contextConfigLocation 初始化參數自定義配置文件的位置和名稱

在web.xml中

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>SpringMvc_day01</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  
  <!-- 配置SpringMVC前端控制器 -->
  <servlet>
  	<servlet-name>springmvc</servlet-name>
  	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  		<!-- 指定SpringMVC配置文件 -->
		<!-- SpringMVC的配置文件的默認路徑是/WEB-INF/${servlet-name}-servlet.xml -->
  		<init-param>
  			<param-name>contextConfigLocation</param-name>
  			<param-value>classpath:springmvc.xml</param-value>
  		</init-param>
       <load-on-startup>1</load-on-startup>
  </servlet>
  
  <servlet-mapping>
  	<servlet-name>springmvc</servlet-name>
  		<!-- 
  			1. /*攔截所有,全部攔截
  			2. *.action *.do 攔截以do,action結尾的請求    ERP
  			3. /攔截所有(不包括jsp)(包含 .js  .png  .css) 強烈建議使用  面向前端消費者  對靜態資源放行
  		 -->
  	<url-pattern>/</url-pattern>
  </servlet-mapping>
  
</web-app>

1.3.4 Controller

Controller是一個普通的java類,不需要實現任何接口。需要在類上添加@Controller註解,把Controller交由Spring管理,在方法上面添加@RequestMapping註解,裏面指定請求的url。

1.3.5 頁面

 

1.3.6 success.jsp頁面

2 Springmvc架構

2.1 框架結構

框架結構如下圖: 

2.2 架構流程

  1. 用戶發送請求至前端控制器DispatcherServlet
  2. DispatcherServlet收到請求調用HandlerMapping處理器映射器。
  3. 處理器映射器根據請求url找到具體的處理器,生成處理器對象及處理器攔截器(如果有則生成)一併返回給DispatcherServlet。
  4. DispatcherServlet通過HandlerAdapter處理器適配器調用處理器
  5. 執行處理器(Controller,也叫後端控制器)。
  6. Controller執行完成返回ModelAndView
  7. HandlerAdapter將controller執行結果ModelAndView返回給DispatcherServlet
  8. DispatcherServlet將ModelAndView傳給ViewReslover視圖解析器
  9. ViewReslover解析後返回具體View
  10. DispatcherServlet對View進行渲染視圖(即將模型數據填充至視圖中)。
  11. DispatcherServlet響應用戶

2.3 組件說明

以下組件通常使用框架提供實現:

  • DispatcherServlet:前端控制器

用戶請求到達前端控制器,它就相當於mvc模式中的c,dispatcherServlet是整個流程控制的中心,由它調用其它組件處理用戶的請求,dispatcherServlet的存在降低了組件之間的耦合性。

  • HandlerMapping:處理器映射器

HandlerMapping負責根據用戶請求url找到Handler即處理器,springmvc提供了不同的映射器實現不同的映射方式,例如:配置文件方式,實現接口方式,註解方式等。

  • Handler:處理器

Handler 是繼DispatcherServlet前端控制器的後端控制器,在DispatcherServlet的控制下Handler對具體的用戶請求進行處理。

由於Handler涉及到具體的用戶業務請求,所以一般情況需要程序員根據業務需求開發Handler。

  • HandlAdapter:處理器適配器

通過HandlerAdapter對處理器進行執行,這是適配器模式的應用,通過擴展適配器可以對更多類型的處理器進行執行。

下圖是許多不同的適配器,最終都可以使用usb接口連接

  • ViewResolver:視圖解析器

View Resolver負責將處理結果生成View視圖,View Resolver首先根據邏輯視圖名解析成物理視圖名即具體的頁面地址,再生成View視圖對象,最後對View進行渲染將處理結果通過頁面展示給用戶。

  • View:視圖

springmvc框架提供了很多的View視圖類型的支持,包括:jstlView、freemarkerView、pdfView等。我們最常用的視圖就是jsp。

一般情況下需要通過頁面標籤或頁面模版技術將模型數據通過頁面展示給用戶,需要由程序員根據業務需求開發具體的頁面。

說明:在springmvc的各個組件中,處理器映射器、處理器適配器、視圖解析器稱爲springmvc的三大組件。

需要用戶開發的組件有handlerview

2.4 默認加載的組件

我們沒有做任何配置,就可以使用這些組件
因爲框架已經默認加載這些組件了,配置文件位置如下圖:

2.5 組件掃描器

使用組件掃描器省去在spring容器配置每個Controller類的繁瑣。

使用<context:component-scan>自動掃描標記@Controller的控制器類,

在springmvc.xml配置文件中配置如下:

2.6. 註解映射器和適配器

2.6.1. 配置處理器映射器

註解式處理器映射器,對類中標記了@ResquestMapping的方法進行映射。根據@ResquestMapping定義的url匹配@ResquestMapping標記的方法,匹配成功返回HandlerMethod對象給前端控制器。
HandlerMethod對象中封裝url對應的方法Method。 

從spring3.1版本開始,廢除了DefaultAnnotationHandlerMapping的使用,推薦使用RequestMappingHandlerMapping完成註解式處理器映射。

在springmvc.xml配置文件中配置如下:

註解描述:

@RequestMapping定義請求url到處理器功能方法的映射

2.6.2. 配置處理器適配器

註解式處理器適配器,對標記@ResquestMapping的方法進行適配。

從spring3.1版本開始,廢除了AnnotationMethodHandlerAdapter的使用,推薦使用RequestMappingHandlerAdapter完成註解式處理器適配。

在springmvc.xml配置文件中配置如下:

2.6.3. 註解驅動

直接配置處理器映射器和處理器適配器比較麻煩,可以使用註解驅動來加載。
SpringMVC使用<mvc:annotation-driven>自動加載RequestMappingHandlerMapping和RequestMappingHandlerAdapter
可以在springmvc.xml配置文件中使用<mvc:annotation-driven>替代註解處理器和適配器的配置。

2.7. 視圖解析器

視圖解析器使用SpringMVC框架默認的InternalResourceViewResolver,這個視圖解析器支持JSP視圖解析
在springmvc.xml配置文件中配置如下:

邏輯視圖名需要在controller中返回ModelAndView指定,比如邏輯視圖名爲ItemList,則最終返回的jsp視圖地址:

“WEB-INF/jsp/itemList.jsp”

最終jsp物理地址:前綴+邏輯視圖名+後綴

4. 使用 @RequestMapping 映射請求

  • Spring MVC 使用 @RequestMapping 註解爲控制器指定可 以處理哪些 URL 請求
  • 在控制器的類定義及方法定義處都可標註@RequestMapping
    • 類定義處:提供初步的請求映射信息。相對於 WEB 應用的根目錄
    • 方法處:提供進一步的細分映射信息。相對於類定義處的 URL。若類定義處未標註 @RequestMapping,則方法處標記的 URL 相對於WEB 應用的根目錄
  • DispatcherServlet 截獲請求後,就通過控制器上 @RequestMapping 提供的映射信息確定請求所對應的處理方法。

  • value的值是數組,可以將多個url映射到同一個方法

4.1 映射請求參數、請求方法或請求頭

  • @RequestMapping 除了可以使用請求 URL 映射請求外, 還可以使用請求方法、請求參數及請求頭映射請求
  • @RequestMapping 的 valuemethodparams heads 分別表示請求 URL、請求方法、請求參數及請求頭的映射條件,他們之間是與的關係,聯合使用多個條件可讓請求映射更加精確化。
  • params headers支持簡單的表達式:
    • param1: 表示請求必須包含名爲 param1 的請求參數 
    • !param1: 表示請求不能包含名爲 param1 的請求參數 
    • param1 != value1: 表示請求包含名爲 param1 的請求參數,但其值不能爲 value1
    • {“param1=value1”, “param2”}: 請求必須包含名爲 param1 和param2 的兩個請求參數,且 param1 參數的值必須爲 value1

4.2 Ant 風格

  • Ant 風格資源地址支持 3 種匹配符:
    • ?:匹配文件名中的一個字符 –
    • *:匹配文件名中的任意字符 –
    • **:** 匹配多層路徑 –
  • @RequestMapping 還支持 Ant 風格的 URL• :
    • /user/*/createUser: 匹配 /user/aaa/createUser、/user/bbb/createUser 等 URL
    • /user/**/createUser: 匹配 /user/createUser、/user/aaa/bbb/createUser 等 URL
    • /user/createUser??: 匹配 /user/createUseraa、/user/createUserbb 等 URL
  • 實例: 

4.3 @PathVariable 映射 URL 綁定的佔位符

  • 帶佔位符的 URL 是 Spring3.0 新增的功能,該功能在 SpringMVC 向 REST 目標挺進發展過程中具有里程碑的意義
  • 通過 @PathVariable 可以將 URL 中佔位符參數綁定到控 制器處理方法的入參中:URL 中的 {xxx} 佔位符可以通過@PathVariable("xxx") 綁定到操作方法的入參中。

   

4.4  RestFu風格

  • REST:即 Representational State Transfer。(資源)表現層狀態轉化。是目前 最流行的一種互聯網軟件架構。它結構清晰、符合標準、易於理解、擴展方便,所以正得到越來越多網站的採用。
  • 資源(Resources):網絡上的一個實體,或者說是網絡上的一個具體信息。它 可以是一段文本、一張圖片、一首歌曲、一種服務,總之就是一個具體的存在。可以用一個URI(統一資源定位符)指向它,每種資源對應一個特定的 URI 。要獲取這個資源,訪問它的URI就可以,因此 URI 即爲每一個資源的獨一無二的識別符。
  • 表現層(Representation):把資源具體呈現出來的形式,叫做它的表現層 •(Representation)。比如,文本可以用 txt 格式表現,也可以用 HTML 格式、XML 格式、JSON 格式表現,甚至可以採用二進制格式。
  • 狀態轉化(State Transfer):每 發出一個請求,就代表了客戶端和服務器的一次交互過程。HTTP協議,是一個無狀態協議,即所有的狀態都保存在服務器端。因此,如果客戶端想要操作服務器,必須通過某種手段,讓服務器端發生“狀態轉化”(State Transfer)。而這種轉化是建立在表現層之上的,所以就是 “表現層狀態轉化”。具體說,就是 HTTP 協議裏面,四個表示操作方式的動詞:GET、POST、PUT、DELETE。它們分別對應四種基本操作:GET 用來獲取資源,POST 用來新建資源,PUT 用來更新資源,DELETE 用來刪除資源。
  • Rest 風格的 URL. 以 CRUD 爲例: 
    • 新增: /order POST 
    • 修改: /order/1 PUT update?id=1 
    • 獲取:/order/1 GET get?id=1 
    • 刪除: /order/1 DELETE delete?id=1     

如何發送 PUT 請求和 DELETE 請求呢 ? 

HiddenHttpMethodFilter:瀏覽器 form 表單只支持 GET •與 POST 請求,而DELETE、PUT 等 method 並不支持Spring3.0 添加了一個過濾器,可以將這些請求轉換爲標準的 http 方法,使得支持 GET、POST、PUT 與DELETE 請求。

1. 需要配置 HiddenHttpMethodFilter 
2. 需要發送 POST 請求
3. 需要在發送 POST 請求時攜帶一個 name="_method" 的隱藏域, 值爲 DELETE 或 PUT 
 在 SpringMVC 的目標方法中如何得到 id 呢? 使用 @PathVariable 註解

表單: 必須包含隱藏的屬性

controller:

 

【特別注意】:

     * 我們要能支持直接發送PUT之類的請求還要封裝請求體中的數據(針對ajax)
     * 1、配置上HttpPutFormContentFilter
     * 2、他的作用;將請求體中的數據解析包裝成一個map。
     * 3、request被重新包裝,request.getParameter()被重寫,就會從自己封裝的map中取數據

 

5 請求處理方法簽名

  • Spring MVC 通過分析處理方法的簽名,將 HTTP 請求信 息綁定到處理方法的相應人蔘中。
  • Spring MVC 對控制器處理方法簽名的限制是很寬鬆的,幾乎可以按喜歡的任何方式對方法進行簽名。
  • 必要時可以對方法及方法入參標註相應的註解(@PathVariable、@RequestParam、@RequestHeader 等)、Spring MVC 框架會將 HTTP 請求的信息綁定到相應的方法入參中,並根據方法的返回值類型做出相應的後續處理。

5.1 使用 @RequestParam 綁定請求參數值

value參數名字,即入參的請求參數名字,如value=“itemId表示請求的參數    區中的名字爲itemId的參數的值將傳入

required是否必須,默認是true,表示請求中一定要有相應的參數,否則將報錯

TTP Status 400 - Required Integer parameter 'XXXX' is not present

defaultValue默認值,表示如果請求中沒有同名參數時的默認值

定義如下:

5.2 @RequestHeader

請求頭包含了若干個屬性,服務器可據此獲知客戶端的信 息,通過 @RequestHeader 即可將請求頭中的屬性值綁
定到處理方法的入參中。

5.3 @CookieValue 

@CookieValue 可• 讓處理方法入參綁定某個 Cookie 值

5.4. 綁定簡單類型

當請求的參數名稱和處理器形參名稱一致時會將請求參數與形參進行綁定。這樣,從Request取參數的方法就可以進一步簡化。

參數類型推薦使用包裝數據類型,因爲基礎數據類型不可以爲null

整形:Integer、int

字符串:String

單精度:Float、float

雙精度:Double、double

布爾型:Boolean、boolean

說明:對於布爾類型的參數,請求的參數值爲true或false。或者1或0

請求url:

http://localhost:8080/xxx.action?id=2&status=false

處理器方法:

public String editItem(Model model,Integer id,Boolean status)

5.5  綁定pojo類型

Spring MVC 會按請求參數名和 POJO 屬性名進行自動匹配,自動爲該對象填充屬性值。

支持級聯屬性,如:dept.deptId、dept.address.tel 等

要求:pojo對象中的屬性名和表單中input的name屬性一致。

5.5.1 示例(包含級聯)

1. POJO類:

2. 表單頁面定義如下圖:


3. 請求的參數名稱和pojo的屬性名稱一致,會自動將請求參數賦值給pojo的屬性。

注意:提交的表單中不要有日期類型的數據,否則會報400錯誤。如果想提交日期類型的數據需要用到後面的自定義參數綁定的內容。

5.5.2 解決post亂碼問題

提交發現,保存成功,但是保存的是亂碼
在web.xml中加入:

以上可以解決post請求亂碼問題。

對於get請求中文參數出現亂碼解決方法有兩個:

修改tomcat配置文件添加編碼與工程編碼一致,如下:

<Connector URIEncoding="utf-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>

另外一種方法對參數進行重新編碼:

String userName new String(request.getParamter("userName").getBytes("ISO8859-1"),"utf-8")

ISO8859-1是tomcat默認編碼,需要將tomcat編碼後的內容按utf-8編碼

5.6 複雜類型數據綁定

5.6.1 綁定數組類型

表單頁面:checkbox是複選框提交到後臺將是一個數組類型

pojo對象:

處理表單頁面請求的handler:使用數組類型的參數或者直接使用pojo類就可以綁定這個數組

5.6.2 綁定list類型

首先是頁面請求handler,hander保存數據到域對象,保存的數據是一個list集合

items.jsp頁面接受到handler發送來的數據,把數據遍歷顯示出來:

Pojo類和Item類:

items.jsp表單提交的頁面,使用Poojo類接收表單提交的list集合,實現參數綁定:

5.7  Controller中方法默認支持的參數類型(ServletAPI)

處理器形參中添加如下類型的參數處理適配器會默認識別並進行賦值。

  • HttpServletRequest:通過request對象獲取請求信息
  • HttpServletResponse:通過response處理響應信息
  • HttpSession:通過session對象得到session中存放的對象
  • java.security.Principal 
  • Locale 
  • InputStream 
  • OutputStream 
  • Reader 
  • Writer

6 處理模型數據

Spring MVC 提供了以下幾種途徑輸出模型數據:

  1. ModelAndView: 處理方法返回值類型爲 ModelAndView 時, 方法體即可通過該對象添加模型數據
  2. Map 及 Model: 入參java.util.Map /  org.springframework.io.Model org.springframework.ui.ModelMap時,處理方法返回時,Map 中的數據會自動添加到模型中。
  3. @SessionAttributes: 將模型中的某個屬性暫存到 HttpSession 中,以便多個請求之間可以共享這個屬性
  4. @ModelAttribute: 方法入參標註該註解後, 入參的對象就會放到數據模型中

模型數據就是放到request域中,可以在頁面獲取到

6.1 ModelAndView

控制器處理方法的返回值如果爲 ModelAndView, 則其既 包含視圖信息,也包含模型數據信息。

  • 添加模型數據 :
    • MoelAndView addObject(String attributeName, Object attributeValue)
    • ModelAndView addAllObject(Map<String, ?> modelMap– )
  • 設置視圖 :
    • void setView(View view )
    • void setViewName(String viewName)

示例:

controller:

頁面: 

跳轉的頁面:從request域中獲取

6.2 Map & Model & ModelMap

  • Spring MVC 在內部使用了一個org.springframework.ui.Model 接口存儲模型數據
  • 具體• 步驟
    • Spring MVC 在調用方法前會創建一個–隱含的模型對象作爲模型數據的存儲容器。
    • 如果方法的入參爲 Map 或 Model類型,Spring MVC 會將隱含模型的引用傳給這些入參。在方法體內,開發者可以通過這個入參對象訪問到模型中的所有數據,也可以向模型中添加新的屬性數據

  • Model是一個接口,在參數裏直接聲明model即可。
  • 如果使用Model則可以不使用ModelAndView對象,Model對象可以向頁面傳遞數據,View對象則可以使用String返回值替代。不管是Model還是ModelAndView,其本質都是使用Request對象向jsp傳遞數據。
  • ModelMap是Model接口的實現類,也可以通過ModelMap向頁面傳遞數據,使用Model和ModelMap的效果一樣,如果直接使用Model,springmvc會實例化ModelMap。

頁面請求:

Controller:

顯示的頁面:

6.3 @SessionAttributes

  • 若希望在多個請求之間共用某個模型屬性數據,則可以在 控制器類上標註一個@SessionAttributes, Spring MVC 將在模型中對應的屬性暫存到 HttpSession 中。
  • @SessionAttributes 除了可以通過屬性名指定需要放到會 話中的屬性外,還可以通過模型屬性的對象類型指定哪些。
  • 模型屬性需要放到會話中:
    • @SessionAttributes(types=User.class) 會將隱含模型中所有類型爲 User.class 的屬性添加到會話中
    • @SessionAttributes(value={“user1”, “user2”})
    • @SessionAttributes(types={User.class, Dept.class})
    • @SessionAttributes(value={“user1”, “user2”}, types={Dept.class})

頁面:

Contriller:

6.4 @ModelAttribute

  • 1. 有 @ModelAttribute 標記的方法, 會在每個目標方法執行之前被 SpringMVC 調用! 
  •  2. @ModelAttribute 註解也可以來修飾目標方法 POJO 類型的入參, 其 value 屬性值有如下的作用:
    • 1). SpringMVC 會使用 value 屬性值在 implicitModel 中查找對應的對象, 若存在則會直接傳入到目標方法的入參中.
    • 2). SpringMVC 會以 value 爲 key, POJO 類型的對象爲 value, 存入到 request 中.將方法入參對象添加到模型中
  • 在方法定義上使用 @ModelAttribute 註解有兩種方式:

  • 在方法參數上使用@ModelAttribute註解:

案例:修改user的數據,但是不修改密碼

  • 表單:

  • Controller:

  • success.jsp:

  • success.jsp頁面顯示結果:

  • 運行流程:

     1. 執行 @ModelAttribute 註解修飾的方法: 從數據庫中取出對象, 把對象放入到了 Map 中. 鍵爲: user
      2. SpringMVC 從 Map 中取出 User 對象, 並把表單的請求參數賦給該 User 對象的對應屬性.
      3. SpringMVC 把上述對象傳入目標方法的參數. 
      注意: 在 @ModelAttribute 修飾的方法中, 放入到 Map 時的鍵需要和目標方法入參類型的第一個字母小寫的字符串一致!

  •    SpringMVC 確定目標方法 POJO 類型入參的過程

      1. 確定一個 key:
            1). 若目標方法的 POJO 類型的參數木有使用 @ModelAttribute 作爲修飾, 則 key 爲 POJO 類名第一個字母的小寫
            2). 若使用了  @ModelAttribute 來修飾, 則 key 爲 @ModelAttribute 註解的 value 屬性值. 
      2. 在 implicitModel 中查找 key 對應的對象, 若存在, 則作爲入參傳入
              1). 若在 @ModelAttribute 標記的方法中在 Map 中保存過, 且 key 和 1 確定的 key 一致, 則會獲取到. 
      3. 若 implicitModel 中不存在 key 對應的對象, 則檢查當前的 Handler 是否使用 @SessionAttributes 註解修飾, 若使用了該註解, 且 @SessionAttributes 註解的 value 屬性值中包含了 key, 則會從 HttpSession 中來獲取 key 所對應的 value 值, 若存在則直接傳入到目標方法的入參中. 若不存在則將拋出異常.。
      4. 若 Handler 沒有標識 @SessionAttributes 註解或 @SessionAttributes 註解的 value 值中不包含 key, 則會通過反射來創建 POJO 類型的參數, 傳入爲目標方法的參數。
      5. SpringMVC 會把 key 和 POJO 類型的對象保存到 implicitModel 中, 進而會保存到 request 中。
 

  • 概括:使用@ModelAttribute修飾方法的入參,會先從implicitModel中取,若取不到則從session中取,若再取不到,則自己反射創建一個,然後放到implicitModel中。
  •  源代碼分析的流程

      1. 調用 @ModelAttribute 註解修飾的方法. 實際上把 @ModelAttribute 方法中 Map 中的數據放在了 implicitModel 中.
      2. 解析請求處理器的目標參數, 實際上該目標參數來自於 WebDataBinder 對象的 target 屬性
         1). 創建 WebDataBinder 對象:
         ①. 確定 objectName 屬性: 若傳入的 attrName 屬性值爲 "", 則 objectName 爲類名第一個字母小寫. 
        注意: attrName. 若目標方法的 POJO 屬性使用了 @ModelAttribute 來修飾, 則 attrName 值即爲 @ModelAttribute 的 value 屬性值 
         ②. 確定 target 屬性:
           > 在 implicitModel 中查找 attrName 對應的屬性值. 若存在, ok
           > 若不存在: 則驗證當前 Handler 是否使用了 @SessionAttributes 進行修飾, 若使用了, 則嘗試從 Session 中獲取 attrName 所對應的屬性值. 若 session 中沒有對應的屬性值, 則拋出了異常. 
           > 若 Handler 沒有使用 @SessionAttributes 進行修飾, 或 @SessionAttributes 中沒有使用 value 值指定的 key和 attrName 相匹配, 則通過反射創建了 POJO 對象  
           2). SpringMVC 把表單的請求參數賦給了 WebDataBinder 的 target 對應的屬性. 
           3). SpringMVC 會把 WebDataBinder 的 attrName 和 target 給到 implicitModel.  近而傳到 request 域對象中. 
           4). 把 WebDataBinder 的 target 作爲參數傳遞給目標方法的入參. 

HttpSessionRequiredException異常:

  • 在3處使用@SessionAttributes(value = "user")將model中的數據放置到session中,使用1將user對象放置到model中。當請求過來時,先訪問1方法,1將user對象放置到model,表單的數據會被封裝到這個model中,然後@SessionAttributes會將model中的數據放置到session中,2直接從model中取數據,放到自己的入參中。
  • 如果沒有1,則2從model和session中取不到數據,會報異常。

如果在處理類定義處標註了@SessionAttributes(“xxx”),則 嘗試從會話中獲取該屬性,並將其賦給該入參,然後再用請求消息填充該入參對象。如果在會話中找不到對應的屬性,則拋出 HttpSessionRequiredException 異常。

 如何解決異常:使用@ModelAttribute標註的方法往session中添加數據

 

7 視圖&視圖解析器

7.1 Spring MVC如何解析視圖

  • 請求處理方法執行完成後,最終返回一個 ModelAndView對象。對於那些返回 String,View 或 ModeMap 等類型的處理方法,Spring MVC 也會在內部將它們裝配成一個ModelAndView 對象,它包含了邏輯名和模型對象的視圖
  • Spring MVC 藉助視圖解析器(ViewResolver)得到最終的視圖對象(View),最終的視圖可以是 JSP ,也可能是Excel、JFreeChart 等各種表現形式的視圖
  • 對於最終究竟採取何種視圖對象對模型數據進行渲染,處理器並不關心,處理器工作重點聚焦在生產模型數據的工作上,從而實現 MVC 的充分解耦

7.2 視圖

視圖的作用是渲染模型數據,將模型裏的數據以某種形式呈現給客 戶。
爲了實現視圖模型和具體實現技術的解耦,Spring 在org.springframework.web.servlet 包中定義了一個高度抽象的 View接口:

視圖對象由視圖解析器負責實例化。由於視圖是無狀態的,所以他們不會有線程安全的問題

常用的視圖實現類:

7.3 視圖解析器

  • 以在 Spring WEB 上下文中配置一種或多種解析策略,並指定他們之間的先後順序。每一種映射策略對應一個具體的視圖解析器實現類。
  • 視圖解析器的作用比較單一:將邏輯視圖解析爲一個具體 的視圖對象。
  • 所有的視圖解析器都必須實現 ViewResolver 接口:

常用的視圖解析器實現類:

  • 程序員可以選擇一種視圖解析器或混用多種視圖解析器 每個視圖解析器都實現了 Ordered 接口並開放出一個 order 屬性,可 以通過 order 屬性指定解析器的優先順序,order 越小優先級越高。
  • SpringMVC 會按視圖解析器順序的優先順序對邏輯視圖名進行解 析,直到解析成功並返回視圖對象,否則將拋出ServletException 異常

7.4 InternalResourceViewResolver

  • JSP 是最常見的視圖技術,可以使用InternalResourceViewResolver 作爲視圖解析器:

  • 若項目中使用了 JSTL,• 則 SpringMVC 會自動把視圖由InternalResourceView 轉爲 JstlView
  • 若使用 JSTL 的 fmt 標籤則需要在 SpringMVC 的配置文件中配置國際化資源文件

  • success.jsp頁面輸出:

  • i18n配置文件:

  • 若希望直接響應通過 SpringMVC 渲染的頁面,可以使用 mvc:view-controller 標籤實現直接跳轉

注意:配置這個標籤後需要配置下面這個標籤,不然(requestmapping會不起作用)

----自定義示視圖解析器----

1.編寫一個類實現view接口

2.在speingMVC配置文件中配置視圖解析器

3.使用方法

7.5 關於重定向

  • 一般情況下,控制器方法返回字符串類型的值會被當成邏輯視圖名處理.
  • 如果返回的字符串中帶 forward: 或 redirect: 前綴時,SpringMVC 會對他們進行特殊處理:將 forward: 和redirect: 當成指示符,其後的字符串作爲 URL 來處理.
    • redirect:success.jsp:會完成一個到 success.jsp 的重定向的操作 –
    • forward:success.jsp:會完成一個到 success.jsp 的轉發操作
  • 頁面:

  • Controller
    

8 使用 Spring 的表單標籤

通過 SpringMVC 的表單標籤可以實現將模型數據 中的屬性和 HTML 表單元素相綁定,以實現表單數據更便捷編輯和表單值的回顯。

  • 使用前要引入表單標籤庫:

全部標籤:

form 標籤:

  • 一般情況下, 通過 GET 請求獲取表單頁面,而通過POST 請求提交表單頁面,因此獲取表單頁面和提交表單頁面的 URL 是相同的。只要滿足該最佳條件的契約,<form:form> 標籤就無需通過 action 屬性指定表單提交的 URL
  • 可以通過 modelAttribute 屬性指定綁定的模型屬性,若 沒有指定該屬性,則默認從 request 域對象中讀取command 的表單 bean,如果該屬性值也不存在,則會發生錯誤。一定要設置modelAttribute 屬性的值。

表單標籤:

  • SpringMVC提供了多個表單組件標籤,如<form:input/>、<form:select/> 等,用以綁定表單字段的屬性值,它們的共有屬性下:
    • path:表單字段,對應 html 元素的 name 屬性,支持級聯屬性
    • htmlEscape:是否對錶單值的 HTML 特殊字符進行轉換,默認值爲 true
    • cssClass:表單組件對應的 CSS 樣式類名
    • cssErrorClass:表單組件的數據存在錯誤時,採取的 CSS樣式
  • form:input、form:password、form:hidden、form:textarea :對應 HTML 表單的 text、password、hidden、textarea標籤
  • form:radiobutton:單選框組件標籤,當表單 bean 對應的屬性值和 value 值相等時,單選框被選中
  • form:radiobuttons:單選框組標籤,用於構造多個單選框
    • items:可以是一個 List、String[] 或 Map 
    • itemValue:指定 radio 的 value 值。可以是集合中 bean 的一個屬性值
    • itemLabel:指定 radio 的 label值
    • delimiter:多個單選框可以通過 delimiter 指定分隔符
  • form:checkbox:複選框組件。用於構造單個複選框
  • form:checkboxs:用於構造多個複選框。使用方式同 form:radiobuttons 標籤
  • form:select:用於構造下拉框組件。使用方式同 form:radiobuttons 標籤
  • form:option:下拉框選項組件標籤。使用方式同 form:radiobuttons 標籤
  • form:errors:顯示錶單組件或數據校驗所對應的錯誤 
    • <form:errors path= “ *” /> :顯示錶單所有的錯誤
    • <form:errors path= “ user*” /> :顯示所有以 user 爲前綴的屬性對應的錯誤
    • <form:errors path= “ username” /> :顯示特定表單對象屬性的錯誤

9 處理靜態資源

  • 優雅的 REST 風格的資源URL 不希望帶 .html 或 .do 等後綴
  • 若將 DispatcherServlet 請求映射配置爲 /,則 Spring MVC 將捕獲WEB 容器的所有請求,包括靜態資源的請求, SpringMVC 會將他們當成一個普通請求處理,因找不到對應處理器將導致錯誤。
  • 可以在 SpringMVC 的配置文件中配置 <mvc:default-servlet-handler/> 的方式解決靜態資源的問題:
    • <mvc:default-servlet-handler/> 將在 SpringMVC 上下文中定義一個DefaultServletHttpRequestHandler,它會對進入 DispatcherServlet 的請求進行篩查,如果發現是沒有經過映射的請求,就將該請求交由 WEB 應用服務器默認的 Servlet 處理,如果不是靜態資源的請求,才由DispatcherServlet 繼續處理
    • 一般 WEB 應用服務器默認的 Servlet 的名稱都是 default。若所使用的WEB 服務器的默認 Servlet 名稱不是 default,則需要通過 default-servlet-name 屬性顯式指定

10 數據轉換 & 數據格式化 & 數據校驗

10.1 數據綁定流程

  1. Spring MVC 主框架將 ServletRequest對象及目標方法的入參實例傳遞給 WebDataBinderFactory 實例,以創建 DataBinder 實例對象
  2. DataBinder 調用裝配在 Spring MVC 上下文中的ConversionService 組件進行數據類型轉換、數據格式化工作。將 Servlet 中的請求信息填充到入參對象中
  3. 調用 Validator 組件對已經綁定了請求消息的入參對象進行數據合法性校驗,並最終生成數據綁定結果BindingData 對象
  4. Spring MVC 抽取 BindingResult 中的入參對象和校驗錯誤對象,將它們賦給處理方法的響應入參
  • Spring MVC 通過反射機制對目標處理方法進行解析,將請求消息綁定到處理方法的入參中。數據綁定的核心部件是DataBinder,運行機制如下:

10.2 數據轉換

Spring MVC 上下文中內建了很多轉換器,可完成大多數 Java 類型的轉換工作。

自定義類型轉換器:

  • ConversionService 是 Spring 類型轉換體系的核心接口。
  • 可以利用 ConversionServiceFactoryBean 在 Spring 的 IOC 容器中定義一個 ConversionService. Spring 將自動識別出IOC 容器中的 ConversionService,並在 Bean 屬性配置及Spring MVC 處理方法入參綁定等場合使用它進行數據的轉換
  • 可通過 ConversionServiceFactoryBean 的 converters 屬性註冊自定義的類型轉換器

Spring 支持的轉換器:

  • Spring 定義了 3 種類型的轉換器接口,實現任意一個轉換 器接口都可以作爲自定義轉換器註冊到ConversionServiceFactroyBean 中:
    • Converter<S,T>:將 S 類型對象轉爲 T 類型對象 .
    • ConverterFactory:將相同系列多個 “同質” Converter 封裝在一起。如果希望將一種類型的對象轉換爲另一種類型及其子類的對象(例如將 String 轉換爲 Number 及 Number 子類(Integer、Long、Double 等)對象)可使用該轉換器工廠類.
    • GenericConverter:會根據源類對象及目標類對象所在的宿主類中的上下文信息進行類型轉換.

可以在springmvc處理器適配器上自定義轉換器:

  1. Converter:哪裏都可以用,可以將任意數據類型轉成任意數據類型
  2. Formatter:web層推薦使用,只能將String類型轉成任意數據類型

一般使用<mvc:annotation-driven/>註解驅動加載處理器適配器,可以在此標籤上進行配置。

注意:配置FormattingConversionServiceFactoryBean既可以進行類型轉換,又可以進行格式化。

10.2.1 設置自定義數據轉換器示例1

需求:在商品修改頁面可以修改商品的生產日期,並且根據業務需求自定義日期格式。

需求分析:由於日期數據有很多種格式,springmvc沒辦法把字符串轉換成日期類型。所以需要自定義參數綁定。前端控制器接收到請求後,找到註解形式的處理器適配器,對RequestMapping標記的方法進行適配,並對方法中的形參進行參數綁定。

修改jsp頁面

  • 如下圖修改itemEdit.jsp頁面,顯示時間

  • 自定義Converter:

  • 配置Converter:我們同時可以配置多個的轉換器。在springmvc.xml裏面

10.2.2 設置自定義數據轉換器示例2(將字串-->對象)

  • 頁面

  • 轉換器

  • 配置轉換器

  • controller使用

10.2.3 自定義Formatter 

轉換器:

配置Formatter:

  • 需要給formatter添加一個component-scan

使用Register註冊Formatter

  • 有了Register就不需要在SpringMVC的配置文件中註冊Formatter了,只需要註冊Register即可。

10.2.4 關於 mvc:annotation-driven

  • <mvc:annotation-driven /> 會自動注 冊RequestMappingHandlerMapping、RequestMappingHandlerAdapter 與ExceptionHandlerExceptionResolver 三個bean。
  • 還將提供以下支持: 
    • 支持使用 ConversionService 實例對錶單參數進行類型轉換 
    • 支持使用 @NumberFormat annotation、@DateTimeFormat 註解完成數據類型的格式化
    • 支持使用 @Valid 註解對 JavaBean 實例進行 JSR 303 驗證
    • 支持使用 @RequestBody 和 @ResponseBody 註解

10.2.5 @InitBinder

  • @InitBinder 標識的方法,可以對 WebDataBinder 對象進行初始化。WebDataBinder DataBinder 的子類,用於完成由表單字段到 JavaBean 屬性的綁定
  • @InitBinder方法不能有返回值,它必須聲明爲void 
  • @InitBinder方法的參數通常是是 WebDataBinder

10.3 數據格式化

  • 對屬性對象的輸入/輸出進行格式化,從其本質上講依然 屬於 “類型轉換” 的範疇。
  • Spring 在格式化模塊中定義了一個實現 ConversionService 接口的FormattingConversionService 實現類,該實現類擴展了 GenericConversionService,因此它既具有類型轉換的功能,又具有格式化的功能
  • FormattingConversionService 擁有一個 FormattingConversionServiceFactroyBean 工廠類,後者用於在 Spring 上下文中構造前者
  • FormattingConversionServiceFactroyBean • 內部已經註冊了 :
    • NumberFormatAnnotationFormatterFactroy:支持對數字類型的屬性 使用 @NumberFormat 註解
    • JodaDateTimeFormatAnnotationFormatterFactroy:支持對日期類型的屬性使用 @DateTimeFormat 註解
  • 裝配了 FormattingConversionServiceFactroyBean 後,就可以在 Spring MVC 入參綁定及模型數據輸出時使用註解驅動了。<mvc:annotation-driven/> 默認創建的ConversionService 實例即FormattingConversionServiceFactroyBean。使用這個bean,既可以進行數據轉換,又可以進行格式化。

10.3.1 日期格式化

  • @ DateTimeFormat 註解可對java.util.Date、java.util.Calendar、java.long.Long 時間類型進行標註:
    • pattern 屬性:類型爲字符串。指定解析/格式化字段數據的模式,如:”yyyy-MM-dd hh:mm:ss”
    • iso 屬性:類型爲 DateTimeFormat.ISO。指定解析/格式化字段數據 的ISO模式,包括四種:ISO.NONE(不使用) 默認、ISO.DATE(yyyy-MM-dd) 、ISO.TIME(hh:mm:ss.SSSZ)、ISO.DATE_TIME(yyyy-MM-dd hh:mm:ss.SSSZ)
    • style 屬性:字符串類型。通過樣式指定日期時間的格式,由兩位字符組成,第一位表示日期的格式,第二位表示時間的格式:S:短日期/時間格式、M:中日期/時間格式、L:長日期/時間格式、F:完整日期/時間格式、-:忽略日期或時間格式

10.3.2 數值格式化

  • @NumberFormat 可對類似數字類型的屬性進行標註,它擁有兩個互斥的屬性:
    • style:類型爲 NumberFormat.Style。用於指定樣式類型,包括三種:Style.NUMBER(正常數字類型)、Style.CURRENCY(貨幣類型)、 Style.PERCENT(百分數類型)
    • pattern:類型爲 String,自定義樣式,如patter="#,###";

10.3.3 格式化示例

 bean的註解標識:

  • controller層處理頁面數據(使用BindingResult

  • 配置文件的設置

10.4 JSR 303

  • JSR 303 是 Java 爲 Bean 數據合法性校驗提供的標準框架, 它已經包含在 JavaEE 6.0 中 .
  • JSR 303 通過在 Bean 屬性上標註類似於 @NotNull、@Max 等標準的註解指定校驗規則,並通過標準的驗證接口對 Bean 進行驗證

10.4.1 Hibernate Validator 擴展註解

Hibernate Validator 是 JSR 303 的一個參考實現,除支持 所有標準的校驗註解外,它還支持以下的擴展註解

  • Spring 4.0 擁有自己獨立的數據校驗框架,同時支持 JSR 303 標準的校驗框架。
  • Spring 在進行數據綁定時,可同時調用校驗框架完成數據校 驗工作。在 Spring MVC 中,可直接通過註解驅動的方式進行數據校驗
  • Spring 的 LocalValidatorFactroyBean 既實現了 Spring 的Validator 接口,也實現了 JSR 303 的 Validator 接口。只要在 Spring 容器中定義了一個LocalValidatorFactoryBean,即可將其注入到需要數據校驗的 Bean 中。
  • Spring 本身並沒有提供 JSR303 的實現,所以 必須將JSR303 的實現者的 jar 包放到類路徑下。
  • <mvc:annotation-driven/> 會默認裝配好一個 LocalValidatorFactoryBean,通過在處理方法的入參上標註 @valid 註解即可讓 Spring MVC 在完成數據綁定後執行數據校驗的工作
  • 在已經標註了 JSR303 註解的表單/命令對象前標註一個 @Valid,Spring MVC 框架在將請求參數綁定到該入參對象後,就會調用校驗框架根據註解聲明的校驗規則實施校驗
  • Spring MVC 是通過對處理方法簽名的規約來保存校驗結果的:前一個表單/命令對象的校驗結果保存到隨後的入參中,這個保存校驗結果的入參必須是 BindingResult 或Errors 類型,這兩個類都位於org.springframework.validation 包中
  • 需校驗的 Bean 對象和其綁定結果對象或錯誤對象時成對出現的,它們之間不允許聲明其他的入參
  • Errors 接口提供了獲取錯誤信息的方法,如 getErrorCount()  或getFieldErrors(String field)
  • BindingResult 擴展了 Errors 接口

10.4.2 在目標方法中獲取校驗結果 

  • 在表單/命令對象類的屬性中標註校驗註解,在處理方法對應的入參前添加 @Valid,Spring MVC 就會實施校驗並將校驗結果保存在被校驗入參對象之後的 BindingResult 或Errors 入參中
  • 常用方法: 
    • FieldError getFieldError(String field) 
    • List<FieldError> getFieldErrors() 
    • Object getFieldValue(String field) 
    • Int getErrorCount()

10.4.3 在頁面上顯示錯誤

  • Spring MVC 除了會將表單/命令對象的校驗結果保存到對 應的 BindingResult 或 Errors 對象中外,還會將所有校驗結果保存到 “隱含模型”
  • 即使處理方法的簽名中沒有對應於表單/命令對象的 結果入參,校驗結果也會保存在 “隱含對象” 中。
  • 隱含模型中的所有數據最終將通過 HttpServletRequest 的屬性列表暴露給 JSP 視圖對象,因此在 JSP 中可以獲取
  • 錯誤信息在 JSP 頁面上可通過 <form:errors path=“userName”>顯示錯誤消息

示例:

10.4.4 提示消息的國際化

  • 每個屬性在數據綁定和數據校驗發生錯誤時,都會生成一 個對應的 FieldError 對象。
  • 當一個屬性校驗失敗後,校驗框架會爲該屬性生成 4 個消 息代碼,這些代碼以校驗註解類名爲前綴,結合modleAttribute、屬性名及屬性類型名生成多個對應的消息代碼:例如 User 類中的 password 屬性標準了一個 @Pattern 註解當該屬性值不滿足 @Pattern 所定義的規則時, 就會產生以下 4個錯誤代碼:
    • Pattern.user.password 
    • Pattern.password 
    • Pattern.java.lang.String 
    • Pattern 

示例:

  • 當使用 Spring MVC 標籤顯示錯誤消息時, Spring MVC 會查看WEB 上下文是否裝配了對應的國際化消息,如果沒有,則顯示默認的錯誤消息,否則使用國際化消息。
  • 數據類型轉換或數據格式轉換時發生錯誤,或該有的參 數不存在,或調用處理方法時發生錯誤,都會在隱含模型中創建錯誤消息。其錯誤代碼前綴說明如下:
    • required:必要的參數不存在。如 @RequiredParam(“param1”) 標註了一個入參,但是該參數不存在
    • typeMismatch:在數據綁定時,發生數據類型不匹配的問題 
    • methodInvocation:Spring MVC 在調用處理方法時發生了錯誤 
  • 註冊國際化資源文件

  • 國際化資源書寫規則文件示例:

11 處理 JSON:使用 HttpMessageConverter

11.1 處理 JSON

1. 加入 jar 包: 


2. 編寫目標方法,使其返回 JSON 對應的對象或集合 

3. 在方法上添加 @ResponseBody 註解 

4. 頁面Ajax請求和和回調函數的處理: 

11.2 HttpMessageConverter<T>

  • HttpMessageConverter<T> 是 Spring3.0 新添加的一個接 口,負責將請求信息轉換爲一個對象(類型爲 T),將對象(類型爲 T)輸出爲響應信息
  • HttpMessageConverter<T>接口定義的方法:
    • Boolean canRead(Class<?> clazz,MediaType – mediaType): 指定轉換器可以讀取的對象類型,即轉換器是否可將請求信息轉換爲 clazz 類型的對象,同時指定支持 MIME 類型(text/html,applaiction/json等)
    • Boolean canWrite(Class<?> clazz,MediaType mediaType):指定轉換器是否可將 clazz 類型的對象寫到響應流中,響應流支持的媒體類型在MediaType 中定義。
    • LIst<MediaType> getSupportMediaTypes():該轉換器支持的媒體類型。
    • T read(Class<? extends T> clazz,HttpInputMessage inputMessage– ):將請求信息流轉換爲 T 類型的對象。
    • void write(T t,MediaType contnetType,HttpOutputMessgae outputMessage):將T類型的對象寫到響應流中,同時指定相應的媒體類型爲 contentType。

  • 使用 HttpMessageConverter<T> 將請求信息轉化並綁定到處理方法的入 參中或將響應結果轉爲對應類型的響應信息,Spring 提供了兩種途徑:
    • 使用 @RequestBody / @ResponseBody – 對處理方法進行標註
    • 使用 HttpEntity<T> / ResponseEntity<T> 作爲處理方法的入參或返回值
  • 當控制器處理方法使用到 @RequestBody/@ResponseBody •或HttpEntity<T>/ResponseEntity<T> 時, Spring 首先根據請求頭或響應頭的Accept 屬性選擇匹配的 HttpMessageConverter, 進而根據參數類型或泛型類型的過濾得到匹配的 HttpMessageConverter, 若找不到可用的HttpMessageConverter 將報錯
  • @RequestBody 和 @ResponseBody 不需要成對出現

@RequestBody、@ResponseBody示例

 文件下載示例:ResponseEntity<T>

頁面請求:

12 國際化

關於國際化:
        1. 在頁面上能夠根據瀏覽器語言設置的情況對文本(不是內容), 時間, 數值進行本地化處理
        2. 可以在 bean 中獲取國際化資源文件 Locale 對應的消息
        3. 可以通過超鏈接切換 Locale, 而不再依賴於瀏覽器的語言設置情況
        
        解決:
        1. 使用 JSTL 的 fmt 標籤
        2. 在 bean 中注入 ResourceBundleMessageSource 的示例, 使用其對應的 getMessage 方法即可
        3. 配置 LocalResolver 和 LocaleChangeInterceptor

實現方式一:通過自己設置瀏覽器的語言實現國際化切換

實現方式二:通過超鏈接獲取locale對象實現國際化切換

1. 頁面設置超鏈接傳遞locale,超鏈接的hreaf=“自身的路徑”

2. 在springmvc.xml配置SessionLocaleResolver 和LocaleChangeInterceptor

3.在handler中還可以請求獲取信息

點擊i18n1.jsp頁面的中英文切換,控制檯輸出:

內部原理:

  • 默認情況下,SpringMVC 根據 Accept-Language 參數 判斷客戶端的本地化類型。
  • 當接受到請求時,SpringMVC 會在上下文中查找一個本 地化解析器(LocalResolver),找到後使用它獲取請求所對應的本地化類型信息。
  • SpringMVC還允許裝配一個動態更改本地化類型的攔截器,這樣通過指定一個請求參數就可以控制單個請求的本地化類型。

SessionLocaleResolver & LocaleChangeInterceptor 工作原理

本地化解析器和本地化攔截器:

  • AcceptHeaderLocaleResolver:根據 HTTP 請求頭的Accept-Language 參數確定本地化類型,如果沒有顯式定義本地化解析器, SpringMVC 使用該解析器。
  • CookieLocaleResolver:根據指定的 Cookie 值確定本地化 類型
  • SessionLocaleResolver:根據 Session 中特定的屬性確定本 地化類型
  • LocaleChangeInterceptor:從請求參數中獲取本次請求對應的本地化類型。

13 文件上傳

  • Spring MVC 爲文件上傳提供了直接的支持,這種支持是通 過即插即用的 MultipartResolver 實現的。Spring 用Jakarta Commons FileUpload 技術實現了一個MultipartResolver 實現類:CommonsMultipartResovler
  • Spring MVC 上下文中默認沒有裝配 MultipartResovler,因 此默認情況下不能處理文件的上傳工作,如果想使用 Spring 的文件上傳功能,需現在上下文中配置 MultipartResolver

配置 MultipartResolver:

  • defaultEncoding: 必須和用戶 JSP 的 pageEncoding 屬性 一致,以便正確解析表單的內容
  • 爲了讓 CommonsMultipartResovler 正確工作,必須先將 Jakarta Commons FileUpload 及 Jakarta Commons io的類包添加到類路徑下。

文件上傳示例:

1. springmvc.xml配置:

2.前端頁面:

3.handle處理:

14 攔截器

14.1 自定義攔截器

  • Spring MVC也可以使用攔截器對請求進行攔截處理,用戶可以自定義攔截器來實現特定的功能,自定義的攔截器必須實現HandlerInterceptor接口
    • preHandle():這個方法在業務處理器處理請求之前被調用,在該方法中對用戶請求 request 進行處理。如果程序員決定該攔截器對請求進行攔截處理後還要調用其他的攔截器,或者是業務處理器去進行處理,則返回true;如果程序員決定不需要再調用其他的組件去處理請求,則返回false。
    • postHandle():這個方法在業務處理器處理完請求後,但 是DispatcherServlet 向客戶端返回響應前被調用,在該方法中對用戶請求request進行處理。
    • afterCompletion():這個方法在 DispatcherServlet 完全處理完請求後被調用,可以在該方法中進行一些資源清理的操作。

攔截器實例:

public class FirstInterceptor  implements HandlerInterceptor {
    /**
     * 該方法在目標方法之前被調用.
     * 若返回值爲 true, 則繼續調用後續的攔截器和目標方法.
     * 若返回值爲 false, 則不會再調用後續的攔截器和目標方法.
     *
     * 可以考慮做權限. 日誌, 事務等.
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {
        System.out.println("[FirstInterceptor] preHandle");
        return true;
    }

    /**
     * 調用目標方法之後, 但渲染視圖之前.
     * 可以對請求域中的屬性或視圖做出修改.
     */
    @Override
    public void postHandle(HttpServletRequest request,
                           HttpServletResponse response, Object handler,
                           ModelAndView modelAndView) throws Exception {
        System.out.println("[FirstInterceptor] postHandle");
    }

    /**
     * 渲染視圖之後被調用. 釋放資源
     */
    @Override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response,
                                Object handler, Exception ex) throws Exception {
        System.out.println("[FirstInterceptor] afterCompletion");
    }
}

攔截器配置:

單個攔截器的執行順序:

兩個攔截器的執行順序:

第二個攔截器的preHandle方法返回false時的執行順序:

總結:

preHandle按攔截器定義順序調用

postHandler按攔截器定義逆序調用

afterCompletion按攔截器定義逆序調用

postHandler在攔截器鏈內所有攔截器返成功調用

afterCompletion只有preHandle返回true才調用

15 異常處理

  • 系統中異常包括兩類:預期異常和運行時異常RuntimeException,前者通過捕獲異常從而獲取異常信息,後者主要通過規範代碼開發、測試通過手段減少運行時異常的發生。
  •  系統的dao、service、controller出現都通過throws Exception向上拋出,最後由springmvc前端控制器交由異常處理器進行異常處理,如下圖:

  • Spring MVC 通過 HandlerExceptionResolver 處理程序 的異常,包括 Handler 映射、數據綁定以及目標方法執行時發生的異常。
  • SpringMVC 提供的 HandlerExceptionResolver 的實現類

DispatcherServlet 默認裝配的 HandlerExceptionResolver :

  • 沒有使用 <mvc:annotation-driven/> 配置:

  • 使用了 <mvc:annotation-driven/> 配置:

15.1 ExceptionHandlerExceptionResolver的使用

  • 主要處理 Handler 中用 @ExceptionHandler 註解定義的方法。
  • @ExceptionHandler 註解定義的方法優先級問題:例如發生的是NullPointerException,但是聲明的異常有RuntimeException 和 Exception,此候會根據異常的最近繼承關係找到繼承深度最淺的那個 @ExceptionHandler註解方法,即標記了 RuntimeException 的方法
  • ExceptionHandlerMethodResolver 內部若找不到@ExceptionHandler 註解標記的方法的話,會找@ControllerAdvice 中的@ExceptionHandler 註解方法
1. 在 @ExceptionHandler 方法的入參中可以加入 Exception 類型的參數, 該參數即對應發生的異常對象
2. @ExceptionHandler 方法的入參中不能傳入 Map. 若希望把異常信息傳導頁面上, 需要使用 ModelAndView 作爲返回值
3. @ExceptionHandler 方法標記的異常有優先級的問題.
4. @ControllerAdvice: 如果在當前 Handler 中找不到 @ExceptionHandler 方法來出來當前方法出現的異常,
則將去 @ControllerAdvice 標記的類中查找 @ExceptionHandler 標記的方法來處理異常.

實例:數學異常

1 前端頁面

2. handler處理

  • 接收頁面請求的方法

  • 處理異常的方法

3 也可以另外創建一個類,實現全局異常的捕捉,類上需要使用 @ControllerAdvice註解

15.2 ResponseStatusExceptionResolver的使用

  • 在異常及異常父類中找到 @ResponseStatus 註解,然 後使用這個註解的屬性進行處理。
  • 定義一個 @ResponseStatus 註解修飾的異常類

  • 若在處理器方法中拋出了上述異常:若ExceptionHandlerExceptionResolver 不解析述異常。由於觸發的異常 UnauthorizedException 帶有@ResponseStatus註解。因此會被ResponseStatusExceptionResolver 解析到。最後響應HttpStatus.UNAUTHORIZED 代碼給客戶端。HttpStatus.UNAUTHORIZED 代表響應碼401,無權限。關於其他的響應碼請參考 HttpStatus 枚舉類型源碼。

使用實例

1. 頁面:

2. 接收頁面請求的handler方法

3 .定義一個 @ResponseStatus 註解修飾的異常類,處理異常

頁面請求後的結果:

4.把@ResponseStatus 註解放在handler方法上

i 不等於10時頁面的請求結果:

15.3 DefaultHandlerExceptionResolver

使用無需其他配置,默認包含

測試方法不支持異常:

1.頁面發送get請求

2.目標方法限定爲post

異常信息:

15.4 SimpleMappingExceptionResolver

如果希望對所有異常進行統一處理,可以使用 SimpleMappingExceptionResolver,它將異常類名映射爲視圖名,即發生異常時使用對應的視圖報告異常。【將指定的異常和顯示頁面進行綁定】

實例:

1.請求頁面:

2.接收請求的handler

3.異常配置頁面

3.配置的errors.jsp顯示異常信息

顯示結果:

16 Spring和SpringMVC

需要進行 Spring 整合 SpringMVC 嗎 ?  還是否需要再加入 Spring 的 IOC 容器 ?
是否需要再 web.xml 文件中配置啓動 Spring IOC 容器的 ContextLoaderListener ?
        
1. 需要: 通常情況下, 類似於數據源, 事務, 整合其他框架都是放在 Spring 的配置文件中(而不是放在 SpringMVC 的配置文件中).
實際上放入 Spring 配置文件對應的 IOC 容器中的還有 Service 和 Dao. 
2. 不需要: 都放在 SpringMVC 的配置文件中. 也可以分多個 Spring 的配置文件, 然後使用 import 節點導入其他的配置文件

問題: 若 Spring 的 IOC 容器和 SpringMVC 的 IOC 容器掃描的包有重合的部分, 就會導致有的 bean 會被創建 2 次.
解決:
        1. 使 Spring 的 IOC 容器掃描的包和 SpringMVC 的 IOC 容器掃描的包沒有重合的部分. 
        2. 使用 exclude-filter 和 include-filter 子節點來規定只能掃描的註解

Spring 的 IOC 容器不應該掃描 SpringMVC中的 bean, 對應的SpringMVC 的 IOC 容器不應該掃描 Spring 中的 bean

  • SpringMVC的IOC容器springmvc.xml配置:只掃描指定的註解

  • Spring的IOC容器beans.xml配置:不掃描指定的註解

在 Spring MVC 配置文件中引用業務層的 Bean:

  • 多個 Spring IOC 容器之間可以設置爲父子關係, 以實現良好的解耦。
  • Spring MVC WEB 層容器可作爲 “業務層” Spring容器的子容器:
    • SpringMVC 的 IOC 容器中的 bean 可以來引用 Spring IOC 容器中的 bean. 
    • 返回來呢 ? 反之則不行. Spring IOC 容器中的 bean 卻不能來引用 SpringMVC IOC 容器中的 bean!

17 SpringMVC 對比 Struts2 

①. Spring MVC 的入口是 Servlet, 而 Struts2 是 Filter 
②. Spring MVC 會稍微比 Struts2 快些. Spring MVC 是基於方法設計, 而 Sturts2 是基於類, 每次發一次請求都會實
例一個 Action.
③. Spring MVC 使用更加簡潔, 開發效率Spring MVC確實 比 struts2 高: 支持 JSR303, 處理 ajax 的請求更方便
④. Struts2 的OGNL 表達式使頁面的開發效率相比Spring MVC 更高些.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章