1. 程式人生 > 實用技巧 >解決powershell7.1的Get-Help找不到對應的詳細幫助

解決powershell7.1的Get-Help找不到對應的詳細幫助

第1章 SpringMVC 概述

SpringMVC 概述

1) Spring 為展現層提供的基於 MVC 設計理念的優秀的 Web 框架,是目前最主流的MVC 框架之一

2)Spring3.0 後全面超越 Struts2,成為最優秀的 MVC 框架。

3)Spring MVC 通過一套 MVC 註解,讓 POJO 成為處理請求的控制器,而無須實現任何介面。

4)支援 REST 風格的 URL 請求。 Restful

5)採用了鬆散耦合可插拔元件結構,比其他 MVC 框架更具擴充套件性和靈活性。

SpringMVC是什麼

1)一種輕量級的、基於MVC的Web層應用框架。偏前端而不是基於業務邏輯層。Spring框架的一個後續產品。

2)Spring框架結構圖(新版本):

1.3 SpringMVC能幹什麼

  1. 天生與Spring框架整合,如:(IOC,AOP)

  2. 支援Restful風格

  3. 進行更簡潔的Web層開發

  4. 支援靈活的URL到頁面控制器的對映

  5. 非常容易與其他檢視技術整合,如:Velocity、FreeMarker等等

  6. 因為模型資料不存放在特定的API裡,而是放在一個Model裡(Map資料結構實現,因此很容易被其他框架使用)

  7. 非常靈活的資料驗證、格式化和資料繫結機制、能使用任何物件進行資料繫結,不必實現特定框架的API

  8. 更加簡單、強大的異常處理

  9. 對靜態資源的支援

  10. 支援靈活的本地化、主題等解析

1.4 SpringMVC怎麼玩

  1. 將Web層進行了職責解耦,基於請求-響應模型

  2. 常用主要元件

    DispatcherServlet:前端控制器

    Controller:處理器/頁面控制器,做的是MVC中的C的事情,但控制邏輯轉移到前端控制器了,用於對請求進行處理

    HandlerMapping :請求對映到處理器,找誰來處理,如果對映成功返回一個HandlerExecutionChain物件(包含一個Handler處理器(頁面控制器)物件、多個HandlerInterceptor攔截器物件)

    View Resolver : 檢視解析器,找誰來處理返回的頁面。把邏輯檢視解析為具體的View,進行這種策略模式,很容易更換其他檢視技術:如InternalResourceViewResolver將邏輯檢視名對映為JSP檢視

    LocalResolver:本地化、國際化

    MultipartResolver:檔案上傳解析器

    HandlerExceptionResolver:異常處理器

1.5 永遠的HelloWorld

  1. 新建Web工程,加入 jar 包

spring-aop-4.0.0.RELEASE.jar

spring-beans-4.0.0.RELEASE.jar

spring-context-4.0.0.RELEASE.jar

spring-core-4.0.0.RELEASE.jar

spring-expression-4.0.0.RELEASE.jar

commons-logging-1.1.3.jar

spring-web-4.0.0.RELEASE.jar

spring-webmvc-4.0.0.RELEASE.jar

  1. 在 web.xml 中配置 DispatcherServlet
<!-- 配置SpringMVC核心控制器: -->
<servlet>
	<servlet-name>springDispatcherServlet</servlet-name>
	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
	<!-- 配置DispatcherServlet的初始化引數:設定檔案的路徑和檔名稱 -->
	<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>springDispatcherServlet</servlet-name>
	<url-pattern>/</url-pattern>
</servlet-mapping>

① 解釋配置檔案的名稱定義規則:

實際上也可以不通過 contextConfigLocation 來配置 SpringMVC 的配置檔案, 而使用預設的.預設的配置檔案為: /WEB-INF/-servlet.xml

1) 加入 Spring MVC 的配置檔案:springmvc.xml

  1. 增加名稱空間

  2. 增加配置

    <!-- 設定掃描元件的包: -->
    <!-- 配置對映解析器:如何將控制器返回的結果字串,轉換為一個物理的檢視檔案 -->
    <bean id="internalResourceViewResolver"
    	class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    	<property name="prefix" value="/WEB-INF/views/" />
    	<property name="suffix" value=".jsp" />
    </bean>
    
  3. 需要建立一個入口頁面,index.jsp

    <a href="${pageContext.request.contextPath }/helloworld">Hello World</a>
    
  4. 編寫處理請求的處理器,並標識為處理器

    @Controller  //宣告Bean物件,為一個控制器元件
    public class HelloWorldController {
     
    /**
     * 對映請求的名稱:用於客戶端請求;類似Struts2中action對映配置的action名稱
     * 1. 使用 @RequestMapping 註解來對映請求的 URL
     * 2. 返回值會通過檢視解析器解析為實際的物理檢視, 對於 InternalResourceViewResolver 檢視解析器, 
    	 * 會做如下的解析:
    	 *                 通過 prefix + returnVal + suffix 這樣的方式得到實際的物理檢視, 然後做轉發操作.
    	 *                 /WEB-INF/views/success.jsp
    	 */
    	@RequestMapping(value="/helloworld",method=RequestMethod.GET)
    	public String helloworld(){
    	     System.out.println("hello,world");
    	     return "success"; //結果如何跳轉呢?需要配置對映解析器
    	}        
    	}
    
    
  5. 編寫檢視

    /WEB-INF/views/success.jsp

    Sucess Page

  6. 部署測試:

http://localhost:8080/SpringMVC_01_HelloWorld/index.jsp

1.6 HelloWorld深度解析

  1. HelloWorld請求流程圖解:

  2. 一般請求的對映路徑名稱和處理請求的方法名稱最好一致(實質上方法名稱任意)

    @RequestMapping(value="/helloworld",method=RequestMethod.GET)
    public String helloworld(){
    	//public String abc123(){
    	System.out.println("hello,world");
    	return "success";
    }
    
    
  3. 處理請求方式有哪幾種

public enum RequestMethod {

GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE

}

  1. @RequestMapping可以應用在什麼地方

    @Target({ElementType.METHOD, ElementType.TYPE})

    @Retention(RetentionPolicy.RUNTIME)

    @Documented

    @Mapping

    public @interface RequestMapping {…}

6)流程分析:

基本步驟:

  1. 客戶端請求提交到DispatcherServlet

  2. 由DispatcherServlet控制器查詢一個或多個HandlerMapping,找到處理請求的Controller

  3. DispatcherServlet將請求提交到Controller(也稱為Handler)

  4. Controller呼叫業務邏輯處理後,返回ModelAndView

  5. DispatcherServlet查詢一個或多個ViewResoler檢視解析器,找到ModelAndView指定的檢視

  6. 檢視負責將結果顯示到客戶端

第2 章 @RequestMapping註解

2.1 @RequestMapping 對映請求註解

2.1.1 @RequestMapping 概念

1) SpringMVC使用@RequestMapping註解為控制器指定可以處理哪些 URL 請求

2) 在控制器的類定義及方法定義處都可標註 @RequestMapping

​ ① 標記在類上:提供初步的請求對映資訊。相對於 WEB 應用的根目錄

​ ② 標記在方法上:提供進一步的細分對映資訊。相對於標記在類上的 URL。

3) 若類上未標註 @RequestMapping,則方法處標記的 URL 相對於 WEB 應用的根目錄

4) 作用:DispatcherServlet 截獲請求後,就通過控制器上 @RequestMapping 提供的對映資訊確定請求所對應的處理方法。

2.1.2 @ RequestMapping原始碼參考

package org.springframework.web.bind.annotation;

@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
	String[] value() default {};

	RequestMethod[] method() default {};

	String[] params() default {};

	String[] headers() default {};

	String[] consumes() default {};

	String[] produces() default {};
}

2.2 RequestMapping 可標註的位置

2.2.1 實驗程式碼

定義頁面連結、控制器方法

\<a href="springmvc/helloworld"\>test \@RequestMapping\</a\>
@Controller // 宣告Bean物件,為一個控制器元件
@RequestMapping("/springmvc")
public class HelloWorldController {
	/**
	 * 對映請求的名稱:用於客戶端請求;類似Struts2中action對映配置的,action名稱 1 使用@RequestMapping 註解來對映請求的
	 * URL 2 返回值會通過檢視解析器解析為實際的物理檢視, 對於 InternalResourceViewResolver 檢視解析器, 會做如下的解析:
	 * 通過 prefix + returnVal + 字尾 這樣的方式得到實際的物理檢視, 然會做轉發操作.
	 * /WEB-INF/views/success.jsp
	 */
	@RequestMapping(value = "/helloworld")
	public String helloworld() {
		System.out.println("hello,world");
		return "success"; // 結果如何跳轉呢?需要配置檢視解析器
	}
}

2.3 RequestMapping對映請求方式

2.3.1 標準的 HTTP 請求報頭

2.3.2 對映請求引數、請求方法或請求頭

1)@RequestMapping 除了可以使用請求 URL 對映請求外,還可以使用請求方法、請求引數及請求頭對映請求

2)@RequestMapping 的 value【重點】、method【重點】、params【瞭解】 及 heads【瞭解】 分別表示請求 URL、請求方法、請求引數及請求頭的對映條件,他們之間是與的關係,聯合使用多個條件可讓請求對映更加精確化。

3)params 和 headers支援簡單的表示式:

param1: 表示請求必須包含名為 param1 的請求引數

!param1: 表示請求不能包含名為 param1 的請求引數

param1 != value1: 表示請求包含名為 param1 的請求引數,但其值不能為 value1

{"param1=value1", "param2"}: 請求必須包含名為 param1 和param2 的兩個請求引數,且 param1 引數的值必須為 value1

2.3.3 實驗程式碼

  1. 定義控制器方法
@Controller
@RequestMapping("/springmvc")
public class SpringMVCController {
	@RequestMapping(value="/testMethord",method=RequestMethod.POST)
	public String testMethord(){
		System.out.println("testMethord...");
		return "success";
}
}
  1. 以get方式請求
<a href="springmvc/testMethord"\>testMethord\</a\>

發生請求錯誤

  1. 以POST方式請求
<form action="springmvc/testMethord" method="post">
<input type="submit" value="submit">
</form>

2.4 RequestMapping對映請求引數&請求頭

2.4.1 RequestMapping_請求引數&請求頭【瞭解】

//瞭解: 可以使用 params 和 headers 來更加精確的對映請求. params 和 headers 支援簡單的表示式.
@RequestMapping(value="/testParamsAndHeaders",
params= {"username","age!=10"}, headers = { "Accept-Language=en-US,zh;q=0.8" })
public String testParamsAndHeaders(){
	System.out.println("testParamsAndHeaders...");
	return "success";
}

2.4.2 實驗程式碼

  1. 請求URL
<!--設定請求引數和請求頭資訊 -->        
<a href="springmvc/testParamsAndHeaders">testParamsAndHeaders</a>
  1. 測試:使用火狐或Chrom瀏覽器debug測試

  2. 測試有引數情況(不正確):

<a href="springmvc/testParamsAndHeaders">testParamsAndHeaders</a>

警告: No matching handler method found for servlet request: path '/springmvc/testParamsAndHeaders', method 'GET', parameters map[[empty]]

<a href="springmvc/testParamsAndHeaders?username=atguigu&age=10">testParamsAndHeaders</a>

警告: No matching handler method found for servlet request: path '/springmvc/testParamsAndHeaders', method 'GET', parameters map['username' -> array['atguigu'], 'age' -> array['10']]

  • <a href="springmvc/testParamsAndHeaders?age=11">testParamsAndHeaders</a>

警告: No matching handler method found for servlet request: path '/springmvc/testParamsAndHeaders', method 'GET', parameters map['age' -> array['11']]

  1. 測試有引數情況(正確):
<a href="springmvc/testParamsAndHeaders?username=atguigu&age=15"\>testParamsAndHeaders\</a>

2.5 RequestMapping支援Ant 路徑風格

2.5.1 Ant

  1. Ant 風格資源地址支援 3 種匹配符:【瞭解】

    ?:匹配檔名中的一個字元

    *:匹配檔名中的任意字元

    **:** 匹配多層路徑

  2. @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

2.5.2 實驗程式碼

  1. 定義控制器方法
	//Ant 風格資源地址支援 3 種匹配符
	//@RequestMapping(value="/testAntPath/*/abc")
	//@RequestMapping(value="/testAntPath/**/abc")
	@RequestMapping(value="/testAntPath/abc??")
	public String testAntPath(){
		System.out.println("testAntPath...");
		return "success";
}

  1. 頁面連結
<!-- Ant 風格資源地址支援 3 種匹配符 -->
<a href="springmvc/testAntPath/*/abc">testAntPath</a>
<a href="springmvc/testAntPath/xxx/yyy/abc">testAntPath</a>
<a href="springmvc/testAntPath/abcxx">testAntPath</a>

2.6 RequestMapping對映請求佔位符PathVariable註解

2.6.1 @PathVariable

帶佔位符的URL 是 Spring3.0 新增的功能,該功能在 SpringMVC 向 REST 目標挺進發展過程中具有里程碑的意義

通過 @PathVariable 可以將 URL 中佔位符引數繫結到控制器處理方法的入參中

URL 中的 {xxx} 佔位符可以通過 @PathVariable("xxx") 繫結到操作方法的入參中。

2.6.2 實驗程式碼

  1. 定義控制器方法
//@PathVariable 註解可以將請求URL路徑中的請求引數,傳遞到處理請求方法的入參中
瀏覽器的請求:  testPathVariable/1001
@RequestMapping(value="/testPathVariable/{id}")
public String testPathVariable(@PathVariable("id") Integer id){
	System.out.println("testPathVariable...id="+id);
	return "success";
}

  1. 請求連結
<!-- 測試 @PathVariable -->
<a href="springmvc/testPathVariable/1">testPathVariable</a>

第3章 REST

3.1參考資料:

1)理解本真的REST架構風格: http://kb.cnblogs.com/page/186516/

2)REST: http://www.infoq.com/cn/articles/rest-introduction

REST是什麼?

1) 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 用來刪除資源。

  1. URL風格

示例:

/order/1 HTTP GET :得到 id = 1 的 order gerOrder?id=1

/order/1 HTTP DELETE****:刪除 id = 1的 order deleteOrder?id=1

/order HTTP PUT:更新order

/order HTTP POST:新增 order

擴充套件: 開放平臺 支付功能: 你應該傳送什麼URL ,需要傳遞什麼引數, 需要有哪些驗證 ,響應哪些資料.

3)HiddenHttpMethodFilter

瀏覽器 form 表單只支援 GET 與 POST 請求,而DELETE、PUT 等 method 並不

支援,Spring3.0 添加了一個過濾器,可以將這些請求轉換為標準的 http 方法,使

得支援 GET、POST、PUT 與 DELETE 請求。

3.3 HiddenHttpMethodFilter過濾器原始碼分析

  1. 為什麼請求隱含引數名稱必須叫做”_method”
  1. hiddenHttpMethodFilter 的處理過程

3.4 實驗程式碼

  1. 配置HiddenHttpMethodFilter過濾器
<!-- 支援REST風格的過濾器:可以將POST請求轉換為PUT或DELETE請求 -->
<filter>
	<filter-name>HiddenHttpMethodFilter</filter-name>
	<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
	<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

  1. 程式碼

    /**
    * 1.測試REST風格的  GET,POST,PUT,DELETE 操作
     * 以CRUD為例:
     * 新增: /order POST
     * 修改: /order/1 PUT           update?id=1
     * 獲取: /order/1 GET                get?id=1
     * 刪除: /order/1 DELETE        delete?id=1
     
     * 2.如何傳送PUT請求或DELETE請求?
     * ①.配置HiddenHttpMethodFilter
     * ②.需要傳送POST請求
     * ③.需要在傳送POST請求時攜帶一個 name="_method"的隱含域,值為PUT或DELETE
     
     * 3.在SpringMVC的目標方法中如何得到id值呢?
     *   使用@PathVariable註解
     */
    @RequestMapping(value="/testRESTGet/{id}",method=RequestMethod.GET)
    public String testRESTGet(@PathVariable(value="id") Integer id){
    	System.out.println("testRESTGet id="+id);
    	return "success";
    }
     
    @RequestMapping(value="/testRESTPost",method=RequestMethod.POST)
    public String testRESTPost(){
    	System.out.println("testRESTPost");
    	return "success";
    }
     
    @RequestMapping(value="/testRESTPut/{id}",method=RequestMethod.PUT)
    public String testRESTPut(@PathVariable("id") Integer id){
    	System.out.println("testRESTPut id="+id);
    	return "success";
    }
     
    @RequestMapping(value="/testRESTDelete/{id}",method=RequestMethod.DELETE)
    public String testRESTDelete(@PathVariable("id") Integer id){
    	System.out.println("testRESTDelete id="+id);
    	return "success";
    }
    
    
    
  2. 請求連結

<!-- 實驗1 測試 REST風格 GET 請求 -->
<a href="springmvc/testRESTGet/1">testREST GET</a><br/><br/>
 
<!-- 實驗2 測試 REST風格 POST 請求 -->
<form action="springmvc/testRESTPost" method="POST">
	<input type="submit" value="testRESTPost">
</form>
 
<!-- 實驗3 測試 REST風格 PUT 請求 -->
<form action="springmvc/testRESTPut/1" method="POST">
	<input type="hidden" name="_method" value="PUT">
	<input type="submit" value="testRESTPut">
</form>
 
<!-- 實驗4 測試 REST風格 DELETE 請求 -->
<form action="springmvc/testRESTDelete/1" method="POST">
	<input type="hidden" name="_method" value="DELETE">
	<input type="submit" value="testRESTDelete">
</form>

第4章 處理請求資料

請求資料 : 請求引數 cookie資訊 請求頭資訊…..

JavaWEB : HttpServletRequest

Request.getParameter(引數名); Request.getParameterMap();

Request.getCookies();

Request.getHeader();

4.1請求處理方法簽名

  1. Spring MVC 通過分析處理方法的簽名(方法名+ 引數列表),HTTP請求資訊繫結到處理方法的相應形參中。

  2. Spring MVC對控制器處理方法簽名的限制是很寬鬆的,幾乎可以按喜歡的任何方式對方法進行簽名。

  3. 必要時可以對方法及方法入參標註相應的註解( @PathVariable、@RequestParam、@RequestHeader 等)、

  4. Spring MVC 框架會將 HTTP請求的資訊繫結到相應的方法入參中,並根據方法的返回值型別做出相應的後續處理。

4.2 @RequestParam註解

1)在處理方法入參處使用 @RequestParam 可以把請求引數傳遞給請求方法

2)value:引數名

3)required:是否必須。預設為 true,
表示請求引數中必須包含對應的引數,若不存在,將丟擲異常

4)defaultValue: 預設值,當沒有傳遞引數時使用該值

4.2.1 實驗程式碼

  1. 增加控制器方法
/**
 * @RequestParam 註解用於對映請求引數
 *         value 用於對映請求引數名稱
 *         required 用於設定請求引數是否必須的
 *         defaultValue 設定預設值,當沒有傳遞引數時使用該值
 */
@RequestMapping(value="/testRequestParam")
public String testRequestParam(
	@RequestParam(value="username") String username,
	@RequestParam(value="age",required=false,defaultValue="0") int age){
	System.out.println("testRequestParam - username="+username +",age="+age);
	return "success";
}

  1. 增加頁面連結
<!--測試 請求引數 @RequestParam 註解使用 -->
<a href="springmvc/testRequestParam?username=atguigu&age=10">testRequestParam</a>

4.3 @RequestHeader 註解

  1. 使用 @RequestHeader 繫結請求報頭的屬性值
  2. 請求頭包含了若干個屬性,伺服器可據此獲知客戶端的資訊,通過 @RequestHeader即可將請求頭中的屬性值繫結到處理方法的入參中

4.3.1 實驗程式碼

//瞭解: 對映請求頭資訊 用法同 @RequestParam
@RequestMapping(value="/testRequestHeader")
public String testRequestHeader(@RequestHeader(value="Accept-Language") String al){
System.out.println("testRequestHeader - Accept-Language:"+al);
return "success";
}
<!-- 測試 請求頭@RequestHeader 註解使用 -->
<a href="springmvc/testRequestHeader">testRequestHeader</a>

4.4 @CookieValue 註解

  1. 使用@CookieValue 繫結請求中的 Cookie 值

  2. @CookieValue 可讓處理方法入參繫結某個 Cookie 值

4.4.1實驗程式碼

  1. 增加控制器方法
//瞭解:@CookieValue: 對映一個 Cookie 值. 屬性同 @RequestParam
@RequestMapping("/testCookieValue")
public String testCookieValue(@CookieValue("JSESSIONID") String sessionId) {
System.out.println("testCookieValue: sessionId: " + sessionId);
return "success";
}

  1. 增加頁面連結
<!--測試 請求Cookie @CookieValue 註解使用 -->
<a href="springmvc/testCookieValue">testCookieValue</a>

4.5 使用POJO作為引數

  1. 使用 POJO 物件繫結請求引數值

  2. Spring MVC 會按請求引數名和 POJO
    屬性名進行自動匹配,自動為該物件填充屬性值
    支援級聯屬性。如:dept.deptId、dept.address.tel

4.5.1實驗程式碼

  1. 增加控制器方法、表單頁面

    /**
     * Spring MVC 會按請求引數名和 POJO 屬性名進行自動匹配, 自動為該物件填充屬性值。
     * 支援級聯屬性
     *                 如:dept.deptId、dept.address.tel 等
     */
    @RequestMapping("/testPOJO")
    public String testPojo(User user) {
    	System.out.println("testPojo: " + user);
    	return "success";
    }
    
    

  2. 增加實體類

  3. 執行結果:

  4. 如果中文有亂碼,需要配置字元編碼過濾器,且配置其他過濾器之前,

    <!-- 配置字符集 -->
    	<filter>
    		<filter-name>encodingFilter</filter-name>
    		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    		<init-param>
    			<param-name>encoding</param-name>
    			<param-value>UTF-8</param-value>
    		</init-param>
    		<init-param>
    			<param-name>forceEncoding</param-name>
    			<param-value>true</param-value>
    		</init-param>
    	</filter>
    	<filter-mapping>
    		<filter-name>encodingFilter</filter-name>
    		<url-pattern>/*</url-pattern>
    	</filter-mapping>
    
    

4.6 使用Servlet原生API作為引數

  1. MVC 的 Handler 方法可以接受哪些 ServletAPI 型別的引數

  2. HttpServletRequest

  3. HttpServletResponse

  4. HttpSession

  5. java.security.Principal

  6. Locale

  7. InputStream

  8. OutputStream

  9. Reader

  10. Writer

  11. 原始碼參考:AnnotationMethodHandlerAdapter L866

@Override
protected Object resolveStandardArgument(Class<?> parameterType, NativeWebRequest webRequest) throws Exception {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
 
if (ServletRequest.class.isAssignableFrom(parameterType) ||MultipartRequest.class.isAssignableFrom(parameterType)) {
Object nativeRequest = webRequest.getNativeRequest(parameterType);
if (nativeRequest == null) {
throw new IllegalStateException(
"Current request is not of type [" + parameterType.getName() + "]: " + request);
}
return nativeRequest;
}
else if (ServletResponse.class.isAssignableFrom(parameterType)) {
this.responseArgumentUsed = true;
Object nativeResponse = webRequest.getNativeResponse(parameterType);
if (nativeResponse == null) {
throw new IllegalStateException(
"Current response is not of type [" + parameterType.getName() + "]: " + response);
}
return nativeResponse;
}
else if (HttpSession.class.isAssignableFrom(parameterType)) {
return request.getSession();
}
else if (Principal.class.isAssignableFrom(parameterType)) {
return request.getUserPrincipal();
}
else if (Locale.class.equals(parameterType)) {
return RequestContextUtils.getLocale(request);
}
else if (InputStream.class.isAssignableFrom(parameterType)) {
return request.getInputStream();
}
else if (Reader.class.isAssignableFrom(parameterType)) {
return request.getReader();
}
else if (OutputStream.class.isAssignableFrom(parameterType)) {
this.responseArgumentUsed = true;
eturn response.getOutputStream();
}
else if (Writer.class.isAssignableFrom(parameterType)) {
this.responseArgumentUsed = true;
return response.getWriter();
}
return super.resolveStandardArgument(parameterType, webRequest);
}

4.6.1 實驗程式碼

/**
 * 可以使用 Serlvet 原生的 API 作為目標方法的引數 具體支援以下型別
 * 
 * HttpServletRequest 
 * HttpServletResponse 
 * HttpSession
 * java.security.Principal 
 * Locale InputStream 
 * OutputStream 
 * Reader 
 * Writer
 * @throws IOException 
 */
@RequestMapping("/testServletAPI")
public void testServletAPI(HttpServletRequest request,HttpServletResponse response, Writer out) throws IOException {
System.out.println("testServletAPI, " + request + ", " + response);
out.write("hello springmvc");
//return "success";
}

<!-- 測試 Servlet API 作為處理請求引數 --\> 
<a href="springmvc/testServletAPI"\>testServletAPI</a>

第5章 處理響應資料

javaWEB: request.setAttribute(xxx)
request.getRequestDispatcher(“地址”).forward(req,resp);

5.1 SpringMVC 輸出模型資料概述

5.1.1提供了以下幾種途徑輸出模型資料

  1. ModelAndView: 處理方法返回值型別為 ModelAndView 時,方法體即可通過該物件新增模型資料

  2. Map 或 Model: 入參為 org.springframework.ui.Model、org.springframework.ui.ModelMap 或 java.uti.Map 時,處理方法返回時,Map
    中的資料會自動新增到模型中。

5.2處理模型資料之 ModelAndView

ModelAndView介紹

控制器處理方法的返回值如果為 ModelAndView, 則其既包含檢視資訊,也包含模型數

據信息。

  1. 兩個重要的成員變數:

    private Object view; 檢視資訊

    private ModelMap model; 模型資料

3)新增模型資料:

MoelAndView addObject(String attributeName, Object attributeValue) 設定模型資料

ModelAndView addAllObject(Map<String, ?> modelMap)

4)設定檢視:

void setView(View view) 設定檢視物件

void setViewName(String viewName) 設定檢視名字

5)獲取模型資料

` protected Map<String, Object> getModelInternal() 獲取模型資料

public ModelMap getModelMap()

public Map<String, Object> getModel()

5.2.2 實驗程式碼

  1. 增加控制器方法
/**
 * 目標方法的返回型別可以是ModelAndView型別
 *                 其中包含檢視資訊和模型資料資訊
 */
@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView(){
System.out.println("testModelAndView");
String viewName = "success";
ModelAndView mv = new ModelAndView(viewName );
mv.addObject("time",new Date().toString()); //實質上存放到request域中 
return mv;
}

  1. 增加頁面連結
<!--測試 ModelAndView 作為處理返回結果 -->
<a href="springmvc/testModelAndView">testModelAndView</a>

  1. 增加成功頁面,顯示資料
time: ${requestScope.time }
  1. 斷點除錯

5.2.2 原始碼解析

5.3 處理模型資料之 Map Model

Map介紹

1)Spring MVC 在內部使用了一個 org.springframework.ui.Model 介面儲存模型資料具體使用步驟

2)Spring MVC在呼叫方法前會建立一個隱含的模型物件作為模型資料的儲存容器。

3)如果方法的入參為 Map 或 Model 型別,Spring MVC 會將隱含模型的引用傳遞給這些入參。

4)在方法體內,開發者可以通過這個入參物件訪問到模型中的所有資料,也可以向模型中新增新的屬性資料

5.3.2 實驗程式碼

  1. 增加控制器方法
//目標方法的返回型別也可以是一個Map型別引數(也可以是Model,或ModelMap型別)
@RequestMapping("/testMap")
public String testMap(Map<String,Object> map){ //【重點】
	System.out.println(map.getClass().getName());
	//org.springframework.validation.support.BindingAwareModelMap
	map.put("names", Arrays.asList("Tom","Jerry","Kite"));
	return "success";

  1. 增加頁面連結
<!-- 測試 Map 作為處理返回結果 -->
<a href="springmvc/testMap">testMap</a>

  1. 增加成功頁面,顯示結果
names: ${requestScope.names }
  1. 顯示結果截圖

  2. 注意問題:Map集合的泛型,key為String,Value為Object,而不是String

  3. 測試引數型別

//目標方法的返回型別也可以是一個Map型別引數(也可以是Model,或ModelMap型別)
@RequestMapping("/testMap2")
public String testMap2(Map<String,Object> map,Model model,ModelMap modelMap){
System.out.println(map.getClass().getName());
map.put("names", Arrays.asList("Tom","Jerry","Kite"));
model.addAttribute("model", "org.springframework.ui.Model");
modelMap.put("modelMap", "org.springframework.ui.ModelMap");
 
System.out.println(map == model);
System.out.println(map == modelMap);
System.out.println(model == modelMap);
 
System.out.println(map.getClass().getName());
System.out.println(model.getClass().getName());
System.out.println(modelMap.getClass().getName());
 
/*
true
true
true
org.springframework.validation.support.BindingAwareModelMap
org.springframework.validation.support.BindingAwareModelMap
org.springframework.validation.support.BindingAwareModelMap
    */ 
return "success";
}

  1. 類層次結構
  2. 推薦:Map, 便於框架移植。
  3. 原始碼參考
public class BindingAwareModelMap extends ExtendedModelMap {
 
@Override
public Object put(String key, Object value) {
removeBindingResultIfNecessary(key, value);
return super.put(key, value);
}
 
@Override
public void putAll(Map<? extends String, ?> map) {
for (Map.Entry<? extends String, ?> entry : map.entrySet()) {
removeBindingResultIfNecessary(entry.getKey(), entry.getValue());
}
super.putAll(map);
}
 
private void removeBindingResultIfNecessary(Object key, Object value) {
if (key instanceof String) {
String attributeName = (String) key;
if (!attributeName.startsWith(BindingResult.MODEL_KEY_PREFIX)) {
String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + attributeName;
BindingResult bindingResult = (BindingResult) get(bindingResultKey);
if (bindingResult != null && bindingResult.getTarget() != value) {
remove(bindingResultKey);
}
}
}
}
}

第6章 檢視解析

6.1 SpringMVC如何解析檢視概述

  1. 不論控制器返回一個String,ModelAndView,View都會轉換為ModelAndView物件,由檢視解析器解析檢視,然後,進行頁面的跳轉。

  2. 檢視解析原始碼分析:重要的兩個介面

  3. 斷點除錯原始碼

  4. 流程圖

6.2 檢視和檢視解析器

1) 請求處理方法執行完成後,最終返回一個 ModelAndView 物件。對於那些返回 String,View 或 ModeMap 等型別的處理方法,Spring MVC 也會在內部將它們裝配成一個 ModelAndView 物件,它包含了邏輯名和模型物件的檢視

2) Spring MVC 藉助檢視解析器ViewResolver)得到最終的檢視物件(View),最終的檢視可以是 JSP ,也可能是 Excel、JFreeChart等各種表現形式的檢視

3) 對於最終究竟採取何種檢視物件對模型資料進行渲染,處理器並不關心,處理器工作重點聚焦在生產模型資料的工作上,從而實現 MVC 的充分解耦

6.3 檢視

  1. 檢視的作用是渲染模型資料,將模型裡的資料以某種形式呈現給客戶。

  2. 為了實現檢視模型和具體實現技術的解耦,Spring 在org.springframework.web.servlet 包中定義了一個高度抽象的 View 介面:

  3. 檢視物件由檢視解析器負責例項化。由於檢視是無狀態的,所以他們不會有執行緒安全的問題

6.4 常用的檢視實現類

6.5 JstlView

  1. 若專案中使用了JSTL,則SpringMVC 會自動把檢視由InternalResourceView轉為
    JstlView(斷點除錯,將JSTL的jar包增加到專案中,檢視解析器會自動修改為JstlView)
  2. 若使用 JSTL 的 fmt 標籤則需要在 SpringMVC 的配置檔案中配置國際化資原始檔
  3. 若希望直接響應通過 SpringMVC 渲染的頁面,可以使用 mvc:view-controller標籤實現

6.5.1 實驗程式碼

1)增加jstl標籤 jar包(斷點除錯,這時的View物件就是JstlView

6.6 檢視解析器

  1. SpringMVC 為邏輯檢視名的解析提供了不同的策略,可以在 SpringMVC
    上下文中配置一種或多種解析策略並指定他們之間的先後順序。每一種對映策略對應一個具體的檢視解析器實現類。

  2. 檢視解析器的作用比較單一:將邏輯檢視解析為一個具體的檢視物件。

  3. 所有的檢視解析器都必須實現 ViewResolver 介面:

6.7 常用的檢視解析器實現類

  1. 程式設計師可以選擇一種檢視解析器或混用多種檢視解析器

  2. 每個檢視解析器都實現了 Ordered 介面並開放出一個 order 屬性,可以通過 order
    屬性指定解析器的優先順序
    order 越小優先順序越高

  3. SpringMVC
    會按檢視解析器順序的優先順序對邏輯檢視名進行解析,直到解析成功並返回檢視物件,否則將丟擲
    ServletException 異常

  4. InternalResourceViewResolver

    1. JSP 是最常見的檢視技術,可以使用InternalResourceViewResolve作為檢視解析器:

mvc:view-controller標籤

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

<mvc:view-controller path="/success" view-name="success"/>

2)請求的路徑:

http://localhost:8080/SpringMVC_02_View/success

3)配置<mvc:view-controller>會導致其他請求路徑失效

解決辦法:

mvc:annotation-driven/

6.9重定向

1) 關於重定向

① 一般情況下,控制器方法返回字串型別的值會被當成邏輯檢視名處理

② 如果返回的字串中帶 forward: redirect: 字首時,SpringMVC 會對他們進行特殊處理:將 forward: 和 redirect: 當成指示符,其後的字串作為 URL 來處理

③ redirect:success.jsp:會完成一個到 success.jsp 的重定向的操作

④ forward:success.jsp:會完成一個到 success.jsp 的轉發操作

2)定義頁面連線

<a href="springmvc/testRedirect">testRedirect</a>

  1. 定義控制器方法
@RequestMapping("/testRedirect")
public String testRedirect(){
System.out.println("testRedirect");
return "redirect:/index.jsp";
//return "forward:/index.jsp";
}

  1. 原始碼分析:重定向原理
  1. 原始碼分析:重定向原理

·

return "forward:/index.jsp"

第7章 綜合案例RESTRUL_CRUD

7.1 RESTRUL_CRUD_需求

7.1.1 顯示所有員工資訊

  1. URI:emps
  2. 請求方式:GET
  3. 顯示效果

7.1.2 新增操作-去往新增頁面

  1. 顯示新增頁面:
  2. URI:emp
  3. 請求方式:GET
  4. 顯示效果

7.1.3 新增操作-新增員工

  1. 新增員工資訊:
  2. URI:emp
  3. 請求方式:POST
  4. 顯示效果:完成新增,重定向到 list 頁面。

7.1.4 刪除操作

  1. URL:emp/{id}

  2. 請求方式:DELETE

  3. 刪除後效果:對應記錄從資料表中刪除

7.1.5 修改操作-去往修改頁面

  1. URI:emp/{id}

  2. 請求方式:GET

  3. 顯示效果:回顯表單

7.1.6 修改操作-修改員工

  1. URI:emp

  2. 請求方式:PUT

  3. 顯示效果:完成修改,重定向到 list 頁面。

7.1.7 相關的類

省略了Service層,為了教學方便

  1. 實體類:Employee、Department
  2. Handler:EmployeeHandler
  3. Dao:EmployeeDao、DepartmentDao

7.1.8 相關的頁面

  1. list.jsp

  2. input.jsp

  3. edit.jsp

7.2 搭建開發環境

  1. 拷貝jar包

com.springsource.net.sf.cglib-2.2.0.jar

com.springsource.org.aopalliance-1.0.0.jar

com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar

spring-aop-4.0.0.RELEASE.jar

spring-aspects-4.0.0.RELEASE.jar

commons-logging-1.1.3.jar

spring-beans-4.0.0.RELEASE.jar

spring-context-4.0.0.RELEASE.jar

spring-core-4.0.0.RELEASE.jar

spring-expression-4.0.0.RELEASE.jar

spring-jdbc-4.0.0.RELEASE.jar

spring-orm-4.0.0.RELEASE.jar

spring-tx-4.0.0.RELEASE.jar

spring-web-4.0.0.RELEASE.jar

spring-webmvc-4.0.0.RELEASE.jar

  1. 建立配置檔案:springmvc.xml 增加context,mvc,beans名稱空間。
<?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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
 
<!-- 配置掃描的包:com.atguigu.springmvc.crud -->
<context:component-scan base-package="com.atguigu.springmvc"/>
 
<!-- 配置檢視解析器:預設採用轉發 -->
<bean id="internalResourceViewResolver" 
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"></property>
</bean> 
</beans>

  1. 配置核心控制器:web.xml
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<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>springDispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>


  1. 將 POST 請求轉換為 PUT 或 DELETE 請求
  <filter>
          <filter-name>HiddenHttpMethodFilter</filter-name>
          <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
  </filter>
  <filter-mapping>
          <filter-name>HiddenHttpMethodFilter</filter-name>
          <url-pattern>/*</url-pattern>
  </filter-mapping>

  1. 建立相關頁面

    /WEB-INF/views/list.jsp

    index.jsp

6.增加實體類

  1. 增加DAO類

7.3 RESTRUL_CRUD_顯示所有員工資訊

  1. 增加頁面連結
<a href="empList">To Employee List</a>
  1. 增加處理器
package com.atguigu.springmvc.crud.handlers;
 
import java.util.Map;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
 
import com.atguigu.springmvc.crud.dao.EmployeeDao;
 
@Controller
public class EmployeeHandler {
 
@Autowired
private EmployeeDao employeeDao ;
 
@RequestMapping("/empList")
public String empList(Map<String,Object> map){
map.put("empList", employeeDao.getAll());        //預設存放到request域中        
return "list";
} 
}

  1. SpringMVC中沒遍歷的標籤,需要使用jstl標籤進行集合遍歷增加jstl標籤庫jar包
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>    
    
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 
Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>

<c:if test="${empty requestScope.empList }">
對不起,沒有找到任何員工!
</c:if>
<c:if test="${!empty requestScope.empList }">
<table border="1" cellpadding="10" cellspacing="0">                
<tr>
<td>EmpId</td>
<td>LastName</td>
<td>Gender</td>
<td>Email</td>
<td>DepartmentName</td>
<td>Edit</td>
<td>Delete</td>
</tr>
<c:forEach items="${requestScope.empList }" var="emp">
<tr>
<td>${emp.id }</td>
<td>${emp.lastName }</td>
<td>${emp.gender==0?"Female":"Male" }</td>
<td>${emp.email }</td>
<td>${emp.department.departmentName }</td>
<td><a href="">Edit</a></td>
<td><a href="">Delete</a></td>
</tr>                
</c:forEach>        
</table>
</c:if>
 
</body>
</html>

7.4 RESTRUL_CRUD_新增操作

  1. 在list.jsp上增加連線
<a href="empInput">Add Employee</a>
  1. 增加處理器方法
@RequestMapping(value="/empInput",method=RequestMethod.GET)
public String empInput(Map<String,Object> map){
map.put("deptList", departmentDao.getDepartments());
//解決錯誤:java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'command' available as request attribute
Employee employee = new Employee();
//map.put("command", employee);
map.put("employee", employee);
return "add";
}

  1. 顯示新增頁面
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8" import="java.util.*"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 
Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
 
<!-- 
1.為什麼使用SpringMVC的form標籤
① 快速開發
② 表單回顯
2.可以通過modelAttribute指定繫結的模型屬性,
若沒有指定該屬性,則預設從request域中查詢command的表單的bean
如果該屬性也不存在,那麼,則會發生錯誤。
 -->
 <form:form action="empAdd" method="POST" modelAttribute="employee">
         LastName : <form:input path="lastName"/><br><br>
         Email : <form:input path="email"/><br><br>
         <%
                 Map<String,String> map = new HashMap<String,String>();
                 map.put("1", "Male");
                 map.put("0","Female");
                 request.setAttribute("genders", map);
         %>
        Gender : <br><form:radiobuttons path="gender" items="${genders }" delimiter="<br>"/><br><br>
         DeptName : 
                 <form:select path="department.id" 
                                                 items="${deptList }" 
                                                 itemLabel="departmentName" 
                                                 itemValue="id"></form:select><br><br>
                 <input type="submit" value="Submit"><br><br>
 </form:form> 
</body>
</html>

  1. 顯示錶單資訊時,會報錯:

    HTTP Status 500 - 
     
    type Exception report
    message 
    description The server encountered an internal error () that prevented it from fulfilling this request.
    exception 
    org.apache.jasper.JasperException: An exception occurred processing JSP page /WEB-INF/views/add.jsp at line 18
    15:                         ② 表單回顯
    16:          -->
    17:          <form:form action="empAdd" method="POST">
    18:                  LastName : <form:input path="lastName"/>
    19:                  Email : <form:input path="email"/>
    20:                  <%
    21:                          Map<String,String> map = new HashMap<String,String>();
    Stacktrace:
            org.apache.jasper.servlet.JspServletWrapper.handleJspException(JspServletWrapper.java:505)
            org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:410)
            org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:337)
            org.apache.jasper.servlet.JspServlet.service(JspServlet.java:266)
            javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
            org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java:209)
            org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:266)
            org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1225)
            org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1012)
            org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959)
            org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:876)
            org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:931)
            org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:822)
            javax.servlet.http.HttpServlet.service(HttpServlet.java:690)
            org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:807)
            javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
            org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)
            org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:108)
    root cause 
    java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'command' available as request attribute
            org.springframework.web.servlet.support.BindStatus.<init>(BindStatus.java:141)
    
    

7.5 使用Spring的表單標籤

  1. 通過 SpringMVC 的表單標籤可以實現將模型資料中的屬性和 HTML
    表單元素相繫結,以實現表單資料更便捷編輯和表單值的回顯

  2. form 標籤

    • 一般情況下,通過 GET 請求獲取表單頁面,而通過 POST
      請求提交表單頁面
      因此獲取表單頁面和提交表單頁面的 URL 是相同的

    • 只要滿足該最佳條件的契約<form:form> 標籤就無需通過 action
      屬性指定表單提交的 URL

    • 可以通過 modelAttribute屬性指定繫結的模型屬性,若沒有指定該屬性,則預設從 request 域物件中讀取
      command* 的表單 bean,如果該屬性值也不存在,則會發生錯誤。

  3. SpringMVC 提供了多個表單元件標籤,如 <form:input/>、<form:select/>
    等,用以繫結表單欄位的屬性值,它們的共有屬性如下:

    • path表單欄位,對應 html 元素的 name 屬性,支援級聯屬性

    • htmlEscape:是否對錶單值的 HTML 特殊字元進行轉換,預設值為 true

    • cssClass:表單元件對應的 CSS 樣式類名

    • cssErrorClass:表單元件的資料存在錯誤時,採取的 CSS 樣式

  4. form:input、form:password、form:hidden、form:textarea:對應 HTML 表單的
    text、password、hidden、textarea 標籤

  5. form:radiobutton:單選框元件標籤,當表單 bean 對應的屬性值和 value
    值相等時,單選框被選中

  6. form:radiobuttons:單選框組標籤,用於構造多個單選框

    • items:可以是一個 List、String[] 或 Map

    • itemValue:指定 radio 的 value 值。可以是集合中 bean 的一個屬性值

    • itemLabel:指定 radio 的 label 值

    • delimiter:多個單選框可以通過 delimiter 指定分隔符

  7. form:checkbox:複選框元件。用於構造單個複選框

  8. form:checkboxs:用於構造多個複選框。使用方式同 form:radiobuttons 標籤

  9. form:select:用於構造下拉框元件。使用方式同 form:radiobuttons 標籤

  10. form:option:下拉框選項元件標籤。使用方式同 form:radiobuttons 標籤

  11. form:errors:顯示錶單元件或資料校驗所對應的錯誤

    • <form:errors path= “*” /> :顯示錶單所有的錯誤

    • <form:errors path= “user*” /> :顯示所有以 user
      為字首的屬性對應的錯誤

    • <form:errors path= “username” /> :顯示特定表單物件屬性的錯誤

7.6 新增員工實驗程式碼

  1. 表單
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8" import="java.util.*"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
 "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
 
<!-- 
1.為什麼使用SpringMVC的form標籤
① 快速開發
② 表單回顯
2.可以通過modelAttribute指定繫結的模型屬性,
若沒有指定該屬性,則預設從request域中查詢command的表單的bean
如果該屬性也不存在,那麼,則會發生錯誤。
 -->
 <form:form action="empAdd" method="POST" modelAttribute="employee">
         LastName : <form:input path="lastName" /><br><br>
         Email : <form:input path="email" /><br><br>
         <%
                 Map<String,String> map = new HashMap<String,String>();
                 map.put("1", "Male");
                 map.put("0","Female");
                 request.setAttribute("genders", map);
         %>
         Gender : <br><form:radiobuttons path="gender" items="${genders }" delimiter="<br>"/><br><br>
         DeptName : 
                 <form:select path="department.id" 
                                                 items="${deptList }" 
                                                 itemLabel="departmentName" 
                                                 itemValue="id"></form:select><br><br>
                 <input type="submit" value="Submit"><br><br>
 </form:form> 
</body>
</html>

  1. 控制器方法
@Controller
public class EmployeeHandler {
@RequestMapping(value="/empAdd",method=RequestMethod.POST)
public String empAdd(Employee employee){
employeeDao.save(employee);
return "redirect:/empList";
}
}

7.7 RESTRUL_CRUD_刪除操作&處理靜態資源

7.7.1 刪除實驗程式碼

  1. 頁面連結
<td><a href="/empDelete/${emp.id }">Delete</a></td>
  1. 控制器方法
@RequestMapping(value="/empDelete/{id}" ,method=RequestMethod.DELETE)
public String empDelete(@PathVariable("id") Integer id){
employeeDao.delete(id);
return "redirect:/empList";
}

7.7.2 HiddenHttpMethodFilter過濾器

發起請求,無法執行,因為delete請求必須通過post請求轉換為delete請求,藉助:HiddenHttpMethodFilter過濾器

7.7.3 需要使用jQuery來轉換請求方式

  1. 加入jQuery庫檔案

/scripts/jquery-1.9.1.min.js

  1. jQuery庫檔案不起作用
警告: No mapping found for HTTP request with URI [/SpringMVC_03_RESTFul_CRUD/scripts/jquery-1.9.1.min.js] in DispatcherServlet with name 'springDispatcherServlet'
  1. 解決辦法,SpringMVC 處理靜態資源

    1. 為什麼會有這樣的問題:

      優雅的 REST 風格的資源URL 不希望帶 .html 或 .do 等字尾,若將
      DispatcherServlet 請求對映配置為 /, 則 Spring MVC 將捕獲 WEB
      容器的所有請求, 包括靜態資源的請求, SpringMVC
      會將他們當成一個普通請求處理, 因找不到對應處理器將導致錯誤。

      ②解決: 在 SpringMVC 的配置檔案中配置 <mvc:default-servlet-handler/>

  2. 配置後,原來的請求又不好使了

需要配置<mvc:annotation-driven />

7.7.4 關於<mvc:default-servlet-handler/>作用

<!-- 
<mvc:default-servlet-handler/> 將在 SpringMVC 上下文中定義一個 DefaultServletHttpRequestHandler,
它會對進入 DispatcherServlet 的請求進行篩查,如果發現是沒有經過對映的請求,
就將該請求交由 WEB 應用伺服器預設的 Servlet 處理,如果不是靜態資源的請求,才由 DispatcherServlet 繼續處理
一般 WEB 應用伺服器預設的 Servlet 的名稱都是 default。
若所使用的 WEB 伺服器的預設 Servlet 名稱不是 default,則需要通過 default-servlet-name 屬性顯式指定        
參考:CATALINA_HOME/config/web.xml
    <servlet>
        <servlet-name>default</servlet-name>
        <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
        <init-param>
            <param-name>debug</param-name>
            <param-value>0</param-value>
        </init-param>
        <init-param>
            <param-name>listings</param-name>
            <param-value>false</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
該標籤屬性default-servlet-name預設值是"default",可以省略。        
<mvc:default-servlet-handler/>        
 -->
<mvc:default-servlet-handler default-servlet-name="default"/>

7.7.5 通過jQuery轉換為DELETE請求

<td><a class="delete" href="empDelete/${emp.id }">Delete</a></td>
<form action="" method="post">
<input type="hidden" name="_method" value="DELETE"/>
</form>
<script type="text/javascript" src="scripts/jquery-1.9.1.min.js"></script>
<script type="text/javascript">
$(function(){
$(".delete").click(function(){
var href = $(this).attr("href");
$("form").attr("action",href).submit();
return false ;
});
});
</script>

7.7.6 刪除操作流程圖解

7.8 RESTRUL_CRUD_修改操作

7.8.1 根據id查詢員工物件,表單回顯

  1. 頁面連結
<td><a href="empEdit/${emp.id }">Edit</a></td>
  1. 控制器方法
//修改員工 - 表單回顯
@RequestMapping(value="/empEdit/{id}",method=RequestMethod.GET)
public String empEdit(@PathVariable("id") Integer id,Map<String,Object> map){
map.put("employee", employeeDao.get(id));
map.put("deptList",departmentDao.getDepartments());
return "edit";
}

  1. 修改頁面
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8" import="java.util.*"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
 "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
 
<!-- 
1.為什麼使用SpringMVC的form標籤
① 快速開發
② 表單回顯
2.可以通過modelAttribute指定繫結的模型屬性,
若沒有指定該屬性,則預設從request域中查詢command的表單的bean
如果該屬性也不存在,那麼,則會發生錯誤。
修改功能需要增加絕對路徑,相對路徑會報錯,路徑不對
 -->
 <form:form action="${pageContext.request.contextPath }/empUpdate" 
method="POST" modelAttribute="employee">                        
<%--
         LastName : <form:input path="lastName" /><br><br>
          --%>
                 <form:hidden path="id"/>
                 <input type="hidden" name="_method" value="PUT">
                 <%-- 這裡不要使用form:hidden標籤,否則會報錯。 
<form:hidden path="_method" value="PUT"/>
                         Spring的隱含標籤,沒有value屬性,同時,path指定的值,在模型物件中也沒有這個屬性(_method),所以回顯時會報錯。
                         org.springframework.beans.NotReadablePropertyException: 
                         Invalid property '_method' of bean class [com.atguigu.springmvc.crud.entities.Employee]: 
                         Bean property '_method' is not readable or has an invalid getter method: 
                         Does the return type of the getter match the parameter type of the setter?
                  --%>
                          
         Email : <form:input path="email" /><br><br>
         <%
                 Map<String,String> map = new HashMap<String,String>();
                 map.put("1", "Male");
                 map.put("0","Female");
                 request.setAttribute("genders", map);
         %>
         Gender : <br><form:radiobuttons path="gender" items="${genders }" delimiter="<br>"/><br><br>
         DeptName : 
                 <form:select path="department.id" 
                                                 items="${deptList }" 
                                                 itemLabel="departmentName" 
                                                 itemValue="id"></form:select><br><br>
                 <input type="submit" value="Submit"><br><br>
 </form:form>
</body>
</html>

7.8.2 提交表單,修改資料

  1. 控制器方法
@RequestMapping(value="/empUpdate",method=RequestMethod.PUT)
public String empUpdate(Employee employee){
employeeDao.save(employee);              
return "redirect:/empList";
}

第8章 處理JSON

javaWEB: Gson、fastJson、JsonLib、jackson…

8.1 返回JSON

  1. 加入 jar 包:

    http://wiki.fasterxml.com/JacksonDownload/ 下載地址

    jackson-annotations-2.1.5.jar

    jackson-core-2.1.5.jar

    jackson-databind-2.1.5.jar

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

@ResponseBody  //SpringMVC對JSON的支援
@RequestMapping("/testJSON")
public Collection<Employee> testJSON(){
return employeeDao.getAll();
}

  1. 增加頁面程式碼:index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
 "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="scripts/jquery-1.9.1.min.js"></script>
<script type="text/javascript">
$(function(){ 
$("#testJSON").click(function(){
 
var url = this.href ;
var args = {};
$.post(url,args,function(data){
for(var i=0; i<data.length; i++){
var id = data[i].id;
var lastName = data[i].lastName ;
alert(id+" - " + lastName);
}
});
 
return false ;
});                
});
</script>
 
</head>
<body>
 
<a href="empList">To Employee List</a>
<br><br>
 
<a id="testJSON" href="testJSON">testJSON</a>
 
</body>
</html>

  1. 測試

8.2 HttpMessageConverter原理

8.2.1 HttpMessageConverter<T>

  1. HttpMessageConverter<T> 是 Spring3.0新新增的一個介面,負責將請求資訊轉換為一個物件(型別為 T),將物件(型別為T)輸出為響應資訊

  2. 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,HttpInputMessageinputMessage):將請求資訊流轉換為 T 型別的物件。
    • void write(T t,MediaType contnetType,HttpOutputMessgaeoutputMessage):將T型別的物件寫到響應流中,同時指定相應的媒體型別為contentType。

3)DispatcherServlet 預設裝配 RequestMappingHandlerAdapter ,而 RequestMappingHandlerAdapter 預設裝配如下 HttpMessageConverter:

  1. 加入 jackson jar 包後, RequestMappingHandlerAdapter裝配的 HttpMessageConverter 如下:

預設情況下陣列長度是6個;增加了jackson的包後多了一個MappingJackson2HttpMessageConverter

8.3 使用HttpMessageConverter

  1. 使用 HttpMessageConverter<T>
    將請求資訊轉化並繫結到處理方法的入參中或將響應結果轉為對應型別的響應資訊,Spring
    提供了兩種途徑

    • 使用 @RequestBody / @ResponseBody 對處理方法進行標註

    • 使用 HttpEntity<T> / ResponseEntity<T>
      作為處理方法的入參或返回值

  2. 當控制器處理方法使用到 @RequestBody/@ResponseBody 或HttpEntity<T>/ResponseEntity<T> 時, Spring 首先根據請求頭或響應頭的
    Accept 屬性選擇匹配的 HttpMessageConverter
    ,進而根據引數型別或泛型型別的過濾得到匹配的 HttpMessageConverter,若找不到可用的 HttpMessageConverter 將報錯

  3. @RequestBody 和 @ResponseBody 不需要成對出現

  4. Content-Disposition:attachment; filename=abc.pdf

  5. @RequestBody @ResponseBody

  6. 實驗程式碼

    <form action="testHttpMessageConverter" method="post" enctype="multipart/form-data">
    檔案: <input type="file" name="file"/><br><br>
    描述: <input type="text" name="desc"/><br><br>
    <input type="submit" value="提交"/>
    </form>
    
    
    
    
    @ResponseBody  //@ResponseBody:是將內容或物件作為Http響應正文返回
    @RequestMapping("/testHttpMessageConverter")
    //@RequestBody:是將Http請求正文插入方法中,修飾目標方法的入參
    public String testHttpMessageConverter(@RequestBody String body){
    System.out.println("body="+body);
    return "Hello," + new Date();  //不再查詢跳轉的頁面
    }
    
    
  7. HttpEntity ResponseEntity

  8. 實驗程式碼

    /files/abc.txt    準備一個下載的檔案
    <a href="testResponseEntity">abc.txt</a>
    
    
    @RequestMapping("testResponseEntity")
    public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws IOException{
     
    ServletContext servletContext = session.getServletContext();
    InputStream resourceAsStream = servletContext.getResourceAsStream("/files/abc.txt");
    byte[] body = new byte[resourceAsStream.available()] ;
    resourceAsStream.read(body);
     
    MultiValueMap<String, String> headers = new HttpHeaders();
    headers.add("Content-Disposition", "attachment;filename=abc.txt");
     
    HttpStatus statusCode = HttpStatus.OK;
     
    ResponseEntity<byte[]> responseEntity = new ResponseEntity<byte[]>(body, headers, statusCode);
     
    return responseEntity ;
    }
    
    

  9. 原始碼參考

  • HttpHeaders

  • HttpStatus

第9章 檔案上傳

9.1 檔案上傳

  1. Spring MVC 為檔案上傳提供了直接的支援,這種支援是通過即插即用的
    MultipartResolver* 實現的。

  2. Spring 用 Jakarta Commons FileUpload 技術實現了一個 MultipartResolver
    實現類:CommonsMultipartResolver

  3. Spring MVC 上下文中預設沒有裝配MultipartResovler,因此預設情況下不能處理檔案的上傳工作,如果想使用 Spring的檔案上傳功能,需現在上下文中配置 MultipartResolver

  4. 配置 MultipartResolverdefaultEncoding: 必須和使用者 JSP 的 pageEncoding 屬性一致,以便正確解析表單的內容,為了讓 CommonsMultipartResolver 正確工作,必須先將 Jakarta CommonsFileUpload 及 Jakarta Commons io 的類包新增到類路徑下。

9.2 檔案上傳示例

  1. 拷貝jar包

    commons-fileupload-1.2.1.jar

    commons-io-2.0.jar

  2. 配置檔案上傳解析器

<!-- 配置檔案上傳解析器
id必須是"multipartResolver",否則,會報錯誤:
java.lang.IllegalArgumentException: Expected MultipartHttpServletRequest: is a MultipartResolver configured?
 -->
<bean id="multipartResolver"
 class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="UTF-8"></property>
<property name="maxUploadSize" value="1024000"></property>
</bean>

  1. 上傳頁面
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
 "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="testUpload" method="post" enctype="multipart/form-data">
檔案: <input type="file" name="file"/><br><br>
描述: <input type="text" name="desc"/><br><br>
<input type="submit" value="提交"/>
</form>
</body>
</html>

  1. 控制器方法
package com.atguigu.springmvc.crud.handlers;
 
import java.io.IOException;
import java.io.InputStream;
 
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
 
@Controller
public class UploadHandler {
 
@RequestMapping(value="/testUpload",method=RequestMethod.POST)
public String testUpload(@RequestParam(value="desc",required=false) String desc, @RequestParam("file") MultipartFile multipartFile) throws IOException{                
System.out.println("desc : "+desc);
System.out.println("OriginalFilename : "+multipartFile.getOriginalFilename());
InputStream inputStream = multipartFile.getInputStream();
System.out.println("inputStream.available() : "+inputStream.available());
System.out.println("inputStream : "+inputStream);

return "success"; //增加成功頁面: /views/success.jsp
}
}

9.3 思考多個檔案上傳?

第10章 攔截器

10.1 自定義攔截器概述

  1. Spring
    MVC也可以使用攔截器對請求進行攔截處理,使用者可以自定義攔截器來實現特定的功能,自定義的攔截器可以實現HandlerInterceptor介面,也可以繼HandlerInterceptorAdapter
    介面卡類

  2. preHandle():這個方法在業務處理器處理請求之前被呼叫,在該方法中對使用者請求
    request進行處理。如果程式設計師決定該攔截器對請求進行攔截處理後還要呼叫其他的攔截器,或者是業務處理器去進行處理,則返回true;如果程式設計師決定不需要再呼叫其他的元件去處理請求,則返回false。

    1. postHandle():這個方法在業務處理器處理完請求後,但是DispatcherServlet
      向客戶端返回響應前被呼叫
      ,在該方法中對使用者請求request進行處理。

    2. afterCompletion():這個方法在 DispatcherServlet完全處理完請求後被呼叫,可以在該方法中進行一些資源清理的操作。

10.2 實驗程式碼(單個攔截器)

  1. 自定義攔截器類
package com.atguigu.springmvc.interceptors;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
 
public class FirstHandlerInterceptor implements HandlerInterceptor {
 
@Override
public void afterCompletion(HttpServletRequest arg0,
HttpServletResponse arg1, Object arg2, Exception arg3) throws Exception {
System.out.println(this.getClass().getName() + " - afterCompletion");
}
 
@Override
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1,
Object arg2, ModelAndView arg3) throws Exception {
System.out.println(this.getClass().getName() + " - postHandle");
}
 
@Override
public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1,
Object arg2) throws Exception {
System.out.println(this.getClass().getName() + " - preHandle");
return true;
} 
}

  1. 配置攔截器
<mvc:interceptors>
<!-- 宣告自定義攔截器 -->
<bean id="firstHandlerInterceptor"
      class="com.atguigu.springmvc.interceptors.FirstHandlerInterceptor"></bean>
</mvc:interceptors>

  1. 斷點除錯攔截器執行流程

  1. 攔截器方法執行順序(小總結)

10.3 實驗程式碼(多個攔截器)

  1. 自定義攔截器類(兩個)

  1. 配置自定義攔截器

    <mvc:interceptors>
    <!-- 宣告自定義攔截器 -->
    <bean id="firstHandlerInterceptor"
      class="com.atguigu.springmvc.interceptors.FirstHandlerInterceptor"></bean>
    <!-- 配置攔截器引用 -->
    <mvc:interceptor>                        
    <mvc:mapping path="/empList"/>
    <!-- <mvc:exclude-mapping path="/empList"/> -->
    <bean id="secondHandlerInterceptor"
             class="com.atguigu.springmvc.interceptors.SecondHandlerInterceptor"></bean>
    </mvc:interceptor>
    </mvc:interceptors>
    兩個都是返回true :
    com.atguigu.springmvc.interceptors.FirstHandlerInterceptor - preHandle
    com.atguigu.springmvc.interceptors.SecondHandlerInterceptor - preHandle
    ************************************biz method*******************************
    com.atguigu.springmvc.interceptors.SecondHandlerInterceptor - postHandle
    com.atguigu.springmvc.interceptors.FirstHandlerInterceptor - postHandle
    com.atguigu.springmvc.interceptors.SecondHandlerInterceptor - afterCompletion
    com.atguigu.springmvc.interceptors.FirstHandlerInterceptor - afterCompletion
    兩個都是返回false:
    com.atguigu.springmvc.interceptors.FirstHandlerInterceptor - preHandle
    true,false
    com.atguigu.springmvc.interceptors.FirstHandlerInterceptor - preHandle
    com.atguigu.springmvc.interceptors.SecondHandlerInterceptor - preHandle
    com.atguigu.springmvc.interceptors.FirstHandlerInterceptor - afterCompletion
    false,true 
    com.atguigu.springmvc.interceptors.FirstHandlerInterceptor - preHandle
    
    

10.4 多個攔截方法的執行順序

  1. 關於執行順序
com.atguigu.springmvc.interceptors.FirstHandlerInterceptor - preHandle
com.atguigu.springmvc.interceptors.SecondHandlerInterceptor – preHandle
************************************biz method*******************************
com.atguigu.springmvc.interceptors.SecondHandlerInterceptor - postHandle
com.atguigu.springmvc.interceptors.FirstHandlerInterceptor - postHandle
com.atguigu.springmvc.interceptors.SecondHandlerInterceptor - afterCompletion
com.atguigu.springmvc.interceptors.FirstHandlerInterceptor - afterCompletion

  1. 執行順序圖解

  2. 從原始碼的執行角度分析流程:

    boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    if (getInterceptors() != null) {
    for (int i = 0; i < getInterceptors().length; i++) {
    HandlerInterceptor interceptor = getInterceptors()[i];
    if (!interceptor.preHandle(request, response, this.handler)) {
    triggerAfterCompletion(request, response, null);
    return false;
    }
    this.interceptorIndex = i;
    }
    }
    return true;
    }
     
    void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
    if (getInterceptors() == null) {
    return;
    }
    for (int i = getInterceptors().length - 1; i >= 0; i--) {
    HandlerInterceptor interceptor = getInterceptors()[i];
    interceptor.postHandle(request, response, this.handler, mv);
    }
    }
     
    void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)
    throws Exception {
     
    if (getInterceptors() == null) {
    return;
    }
    for (int i = this.interceptorIndex; i >= 0; i--) {
    HandlerInterceptor interceptor = getInterceptors()[i];
    try {
    interceptor.afterCompletion(request, response, this.handler, ex);
    }
    catch (Throwable ex2) {
    logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
    }
    }
    }
     
    
    

  3. 原始碼分析:分析interceptorIndex的值情況

第11章 異常處理

java: throws throw try ..catch..finally

11.1異常處理概述

  1. Spring MVC 通過 HandlerExceptionResolver 處理程式的異常,包括 Handler
    對映、資料繫結以及目標方法執行時發生的異常。
  2. SpringMVC 提供的 HandlerExceptionResolver 的實現類

11.2 HandlerExceptionResolver

1)DispatcherServlet 預設裝配的 HandlerExceptionResolver :

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

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

11.3 異常處理_DefaultHandlerExceptionResolver

1)對一些特殊的異常進行處理,比如:

  • NoSuchRequestHandlingMethodException、

    • HttpRequestMethodNotSupportedException

    • HttpMediaTypeNotSupportedException、

    • HttpMediaTypeNotAcceptableException等。

2)javadoc

org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
Default implementation of the HandlerExceptionResolver interface that resolves standard Spring exceptions and translates them to corresponding HTTP status codes. 
This exception resolver is enabled by default in the org.springframework.web.servlet.DispatcherServlet.


11.3.1 實驗程式碼

  1. 增加頁面連結:GET請求
<a href="testDefaultHandlerExceptionResolver">testDefaultHandlerExceptionResolver</a>

增加處理器方法

//@RequestMapping(value="/testDefaultHandlerExceptionResolver")
@RequestMapping(value="/testDefaultHandlerExceptionResolver",method=RequestMethod.POST)  //不支援GET請求
public String testDefaultHandlerExceptionResolver(){
System.out.println("testDefaultHandlerExceptionResolver...");
return "success";
}

  1. 出現異常錯誤

  1. 出現異常交給DefaultHandlerExceptionResolver處理
@Override
protected ModelAndView doResolveException(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
 
try {
if (ex instanceof NoSuchRequestHandlingMethodException) {
return handleNoSuchRequestHandlingMethod((NoSuchRequestHandlingMethodException) ex, request, response,
handler);
}
else if (ex instanceof HttpRequestMethodNotSupportedException) {
return handleHttpRequestMethodNotSupported((HttpRequestMethodNotSupportedException) ex, request,
response, handler);
}
else if (ex instanceof HttpMediaTypeNotSupportedException) {
return handleHttpMediaTypeNotSupported((HttpMediaTypeNotSupportedException) ex, request, response,
handler);
}
else if (ex instanceof HttpMediaTypeNotAcceptableException) {
return handleHttpMediaTypeNotAcceptable((HttpMediaTypeNotAcceptableException) ex, request, response,
handler);
}
else if (ex instanceof MissingServletRequestParameterException) {
return handleMissingServletRequestParameter((MissingServletRequestParameterException) ex, request,
response, handler);
}
else if (ex instanceof ServletRequestBindingException) {
return handleServletRequestBindingException((ServletRequestBindingException) ex, request, response,
handler);
}
else if (ex instanceof ConversionNotSupportedException) {
return handleConversionNotSupported((ConversionNotSupportedException) ex, request, response, handler);
}
else if (ex instanceof TypeMismatchException) {
return handleTypeMismatch((TypeMismatchException) ex, request, response, handler);
}
else if (ex instanceof HttpMessageNotReadableException) {
return handleHttpMessageNotReadable((HttpMessageNotReadableException) ex, request, response, handler);
}
else if (ex instanceof HttpMessageNotWritableException) {
return handleHttpMessageNotWritable((HttpMessageNotWritableException) ex, request, response, handler);
}
else if (ex instanceof MethodArgumentNotValidException) {
return handleMethodArgumentNotValidException((MethodArgumentNotValidException) ex, request, response, handler);
}
else if (ex instanceof MissingServletRequestPartException) {
return handleMissingServletRequestPartException((MissingServletRequestPartException) ex, request, response, handler);
}
else if (ex instanceof BindException) {
return handleBindException((BindException) ex, request, response, handler);
}
else if (ex instanceof NoHandlerFoundException) {
return handleNoHandlerFoundException((NoHandlerFoundException) ex, request, response, handler);
}
}
catch (Exception handlerException) {
logger.warn("Handling of [" + ex.getClass().getName() + "] resulted in Exception", handlerException);
}
return null;
}

11.4 異常處理_SimpleMappingExceptionResolver

1)如果希望對所有異常進行統一處理,可以使用
SimpleMappingExceptionResolver,它將異

常類名對映為檢視名,即發生異常時使用對應的檢視報告異常

11.4.1實驗程式碼

  1. 增加頁面連結
<a href="testSimpleMappingExceptionResolver?i=1">testSimpleMappingExceptionResolver</a>
  1. 增加控制器方法
@RequestMapping("/testSimpleMappingExceptionResolver")
public String testSimpleMappingExceptionResolver(@RequestParam("i") int i){
System.out.println("testSimpleMappingExceptionResolver..."); 
String[] s = new String[10]; 
System.out.println(s[i]); 
return "success";
}

  1. 出現異常情況:引數i的值大於10

  2. 配置異常解析器:自動將異常物件資訊,存放到request範圍內

  3. 原始碼分析

  4. @Override
    protected ModelAndView doResolveException(HttpServletRequest request,
     HttpServletResponse response,Object handler, Exception ex) {
     
    // Expose ModelAndView for chosen error view.
    String viewName = determineViewName(ex, request);
    if (viewName != null) {
    // Apply HTTP status code for error views, if specified.
    // Only apply it if we're processing a top-level request.
    Integer statusCode = determineStatusCode(request, viewName);
    if (statusCode != null) {
    applyStatusCodeIfPossible(request, response, statusCode);
    }
    return getModelAndView(viewName, ex, request);
    }else {
    return null;
    }
    }
    /**
     * Return a ModelAndView for the given view name and exception.
     * <p>The default implementation adds the specified exception attribute.
     * Can be overridden in subclasses.
     * @param viewName the name of the error view
     * @param ex the exception that got thrown during handler execution
     * @return the ModelAndView instance
     * @see #setExceptionAttribute
     */
    protected ModelAndView getModelAndView(String viewName, Exception ex) {
    ModelAndView mv = new ModelAndView(viewName);
    if (this.exceptionAttribute != null) {
    if (logger.isDebugEnabled()) {
    logger.debug("Exposing Exception as model attribute '" + this.exceptionAttribute + "'");
    }
    mv.addObject(this.exceptionAttribute, ex);
    }
    return mv;
    }
    
    

第12章 執行流程圖解

12.1 流程圖

12.2 Spring工作流程描述

  1. 使用者向伺服器傳送請求,請求被SpringMVC 前端控制器 DispatcherServlet捕獲;

  2. DispatcherServlet對請求URL進行解析,得到請求資源識別符號(URI):

    判斷請求URI對應的對映

    1. 不存在:

      • 再判斷是否配置了mvc:default-servlet-handler:

      • 如果沒配置,則控制檯報對映查詢不到,客戶端展示404錯誤

      • 如果有配置,則執行目標資源(一般為靜態資源,如:JS,CSS,HTML)

    2. 存在:

      • 執行下面流程
  3. 根據該URI,呼叫HandlerMapping獲得該Handler配置的所有相關的物件(包括Handler物件以及Handler物件對應的攔截器),最後以HandlerExecutionChain物件的形式返回;

  4. DispatcherServlet 根據獲得的Handler,選擇一個合適的HandlerAdapter。

  5. 如果成功獲得HandlerAdapter後,此時將開始執行攔截器的preHandler(...)方法【正向】

  6. 提取Request中的模型資料,填充Handler入參,開始執行Handler(Controller)方法,處理請求。在填充Handler的入參過程中,根據你的配置,Spring將幫你做一些額外的工作:

    1. HttpMessageConveter:
      將請求訊息(如Json、xml等資料)轉換成一個物件,將物件轉換為指定的響應資訊

    2. 資料轉換:對請求訊息進行資料轉換。如String轉換成Integer、Double等

    3. 資料根式化:對請求訊息進行資料格式化。
      如將字串轉換成格式化數字或格式化日期等

    4. 資料驗證:
      驗證資料的有效性(長度、格式等),驗證結果儲存到BindingResult或Error中

  7. Handler執行完成後,向DispatcherServlet 返回一個ModelAndView物件;

  8. 此時將開始執行攔截器的postHandle(...)方法【逆向】

  9. 根據返回的ModelAndView(此時會判斷是否存在異常:如果存在異常,則執行HandlerExceptionResolver進行異常處理)選擇一個適合的ViewResolver(必須是已經註冊到Spring容器中的ViewResolver)返回給DispatcherServlet,根據Model和View,來渲染檢視

  10. 在返回給客戶端時需要執行攔截器的AfterCompletion方法【逆向】

  11. 將渲染結果返回給客戶端

12.3 原始碼解析

12.3.1 搭建環境

  1. 拷貝jar包
spring-aop-4.0.0.RELEASE.jar
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar
commons-logging-1.1.3.jar
spring-web-4.0.0.RELEASE.jar
spring-webmvc-4.0.0.RELEASE.jar

  1. 配置檔案web.xml
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<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>springDispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

  1. 配置檔案springmvc.xml
<?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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
 
<!-- 設定掃描元件的包 -->
<context:component-scan base-package="com.atguigu.springmvc"/>
 
<!-- 配置檢視解析器 -->
<bean id="internalResourceViewResolver"
    class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
 
</beans>

12.3.2 完成HelloWorld

  1. 頁面連結
<a href="helloworld">Hello World</a>
  1. 控制器方法

    package com.atguigu.springmvc.handler;
     
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
     
    @Controller
    public class HelloWorldHandler { 
    @RequestMapping("/helloworld")
    public String testHello(){ 
    System.out.println("Hello,SpringMVC..."); 
    return "success";
    } 
    }
    
    
  2. 成功頁面:/views/success.jsp

<h3>Success Page</h3>

12.3.3 Debug實驗

  1. 正常流程,執行出結果

  2. 沒有配置<mvc:default-servlet-handler/>,測試,直接報404

3.配置<mvc:default-servlet-handler/>,測試,會去查詢目標資源

4.測試,依然發生錯誤,這時,需要配置:<mvc:annotation-driven/>,否則,對映解析不好使。

12.3.4 Debug流程分析

  1. HandlerExecutionChain mappedHandler;包含了攔截器和處理器方法;

    DispatcherServlet L902 916

  1. HandlerMapping
 org.springframework.web.servlet.HandlerMapping
Interface to be implemented by objects that define a mapping between requests and handler objects. 
This class can be implemented by application developers, although this is not necessary, as org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping and org.springframework.web.servlet.handler.SimpleUrlHandlerMapping are included in the framework. The former is the default if no HandlerMapping bean is registered in the application context. 
HandlerMapping implementations can support mapped interceptors but do not have to. A handler will always be wrapped in a HandlerExecutionChain instance, optionally accompanied by some HandlerInterceptor instances. The DispatcherServlet will first call each HandlerInterceptor's preHandle method in the given order, finally invoking the handler itself if all preHandle methods have returned true. 
The ability to parameterize this mapping is a powerful and unusual capability of this MVC framework. For example, it is possible to write a custom mapping based on session state, cookie state or many other variables. No other MVC framework seems to be equally flexible. 
Note: Implementations can implement the org.springframework.core.Ordered interface to be able to specify a sorting order and thus a priority for getting applied by DispatcherServlet. Non-Ordered instances get treated as lowest priority.

  1. 沒有配置<mvc:default-servlet-handler/>,<mvc:annotation-driven/>,傳送一個不存在資源的請求路徑,mappedHandler為null
  1. 配置<mvc:default-servlet-handler/>,<mvc:annotation-driven/>,傳送一個不存在資源的請求路徑

12.3.5 斷點

第13章 Spring整合SpringMVC

13.1 Spring 與SpringMVC的整合問題:

  1. 需要進行 Spring 整合 SpringMVC 嗎 ?

  2. 還是否需要再加入 Spring 的 IOC 容器 ?

  3. 是否需要在web.xml 檔案中配置啟動 Spring IOC 容器的 ContextLoaderListener ?

需要: 通常情況下, 類似於資料來源, 事務, 整合其他框架都是放在 Spring 的配置檔案
中(而不是放在 SpringMVC 的配置檔案中).

實際上放入 Spring 配置檔案對應的 IOC 容器中的還有 Service 和 Dao.

不需要: 都放在 SpringMVC 的配置檔案中. 也可以分多個 Spring 的配置檔案, 然後使

用 import 節點匯入其他的配置檔案

13.2 Spring整合SpringMVC_解決方案配置監聽器

  1. 監聽器配置
<!-- 配置啟動 Spring IOC 容器的 Listener -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:beans.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

  1. 建立Spring的bean的配置檔案:beans.xml
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
 
<!-- 設定掃描元件的包 -->
<context:component-scan base-package="com.atguigu.springmvc"/>
 
<!-- 配置資料來源, 整合其他框架, 事務等. -->
 
</beans>

  1. springmvc配置檔案:springmvc.xml
<?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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
 
<!-- 設定掃描元件的包 -->
<context:component-scan base-package="com.atguigu.springmvc"/> 
<!-- 配置檢視解析器 -->
<bean id="internalResourceViewResolver"
   class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
 
<mvc:default-servlet-handler/> 
<mvc:annotation-driven/> 
</beans>

在HelloWorldHandler、UserService類中增加構造方法,啟動伺服器,檢視構造器執行情況。

問題: 若 Spring 的 IOC 容器和 SpringMVC 的 IOC 容器掃描的包有重合的部分,
就會導致有的 bean 會被建立 2 次.

解決:

使 Spring 的 IOC 容器掃描的包和 SpringMVC 的 IOC 容器掃描的包沒有重合的部分.

使用 exclude-filter 和 include-filter 子節點來規定只能掃描的註解

springmvc.xml
<context:component-scan base-package="com.atguigu.springmvc" use-default-filters="false">
<context:include-filter type="annotation"
           expression="org.springframework.stereotype.Controller"/>
<context:include-filter type="annotation"
           expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
</context:component-scan>

beans.xml
<context:component-scan base-package="com.atguigu.springmvc">
<context:exclude-filter type="annotation"
        expression="org.springframework.stereotype.Controller"/>
<context:exclude-filter type="annotation"
        expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
</context:component-scan>
<!-- 配置資料來源, 整合其他框架, 事務等. -->

13.3 SpringIOC 容器和 SpringMVC IOC 容器的關係

SpringMVC 的 IOC 容器中的 bean 可以來引用 Spring IOC 容器中的 bean.

返回來呢 ? 反之則不行. Spring IOC 容器中的 bean 卻不能來引用 SpringMVC IOC
容器中的 bean

  1. 在 Spring MVC 配置檔案中引用業務層的 Bean

  2. 多個 Spring IOC 容器之間可以設定為父子關係,以實現良好的解耦。

  3. Spring MVC WEB 層容器可作為 “業務層” Spring 容器的子容器:

    即 WEB 層容器可以引用業務層容器的 Bean,而業務層容器卻訪問不到 WEB 層容器的
    Bean

13.4 SpringMVC對比Struts2

1) Spring MVC 的入口是 Servlet, 而 Struts2 是 FilterSpring MVC 會稍微比 Struts2
快些.

2) Spring MVC 是基於方法設計, 而 Sturts2 是基於類,

每次發一次請求都會例項一個 Action.

  1. Spring MVC 使用更加簡潔, 開發效率Spring MVC確實比 struts2 高: 支援 JSR303,
    處 理ajax 的請求更方便

  2. Struts2 的 OGNL 表示式使頁面的開發效率相比 Spring MVC 更高些.