第一天
第1章 SpringMVC 的基本概念
1.1 關於三層架構和 MVC
1.1.1 三層架構
我們的開發架構一般都是基於兩種形式,一種是 C/S 架構,也就是客戶端/服務器,另一種是 B/S 架構,也就 是瀏覽器服務器。在 JavaEE 開發中,幾乎全都是基於 B/S 架構的開發。那麼在 B/S 架構中,系統標準的三層架構包括:表現層、業務層、持久 層。三層架構在我們 的實際開發中使用 的非常多,所以我們 課程中的案例也都是 基於 三層架構設計的。 三層架構中,每一層各司其職,接下來我們就說說每層都負責哪些方面: 表現層: 也就是我們常說的 web 層。它負責接收客戶端請求,向客戶端響應結果,通常客戶端使用 http 協議請求 web 層,web 需要接收 http 請求,完成 http 響應。 表現層包括展示層和控制層:控制層負責接收請求,展示層負責結果的展示。 表現層依賴業務層,接收到客戶端請求一般會調用業務層進行業務處理,並將處理結果響應給客戶端。 表現層的設計一般都使用 MVC 模型。(MVC 是表現層的設計模型,和其他層沒有關係) 業務層: 也就是我們常說的 service 層。它負責業務邏輯處理,和我們開發項目的需求息息相關。web 層依賴業 務層,但是業務層不依賴 web 層。 業務層 在業務處理時可能會依賴持久層,如果要對數據持久化需要保證事務一致性。(也就是我們說的, 事務應該放到業務層來控制) 持久層: 也就是我們是常說的 dao 層。負責數據持久化,包括數據層即數據庫和數據訪問層,數據庫是對數據進 行持久化的載體,數據訪問層是業務層和持久層交互的接口,業務層需要通過數據訪問層將數據持久化到數據庫中。通俗的講,持久層就是和數據庫交互,對數據庫表進行曾刪改查的。
|
1.1.2 MVC模型
MVC 全名是 Model View Controller,是模型(model)-視圖(view)-控制器(controller)的縮寫, 是一種用於設計創建 Web 應用程序表現層的模式。MVC 中每個部分各司其職: Model(模型): 通常指的就是我們的數據模型。作用一般情況下用於封裝數據。 View(視圖): Controller(控制器): 是應用程序中處理用戶交互的部分。作用一般就是處理程序邏輯的。 它相對於前兩個不是很好理解,這裏舉個例子: 我們要保存一個用戶的信息,該用戶信息中包含了姓名,性別,年齡等等。 到模型之中。 的。 這時候表單輸入要求年齡必須是 1~100 之間的整數。姓名和性別不能爲空。並且把數據填充 此時除了 js 的校驗之外,服務器端也應該有數據準確性的校驗,那麼校驗就是控制器的該做 當校驗失敗後,由控制器負責把錯誤頁面展示給使用者。 如果校驗成功,也是控制器負責把數據填充到模型,並且調用業務層實現完整的業務需求。 |
1.2 SpringMVC 概述
1.2.1 SpringMVC是什麼
SpringMVC 是一種基於 Java 的實現 MVC 設計模型的請求驅動類型的輕量級 Web 框架,屬於 SpringFrameWork 的後續產品,已經融合在 Spring Web Flow 裏面。Spring 框架提供了構建 Web 應用程序的全功 能 MVC 模塊。使用 Spring 可插入的 MVC 架構,從而在使用Spring進行WEB開發時,可以選擇使用 Spring 的 Spring MVC 框架或集成其他 MVC 開發框架,如 Struts1(現在一般不用),Struts2 等。 SpringMVC 已經成爲目前最主流的 MVC 框架之一,並且隨着 Spring3.0 的發佈,全面超越 Struts2,成 爲最優秀的 MVC 框架。它通過一套註解,讓一個簡單的 Java 類成爲處理請求的控制器,而無須實現任何接口。同時它還支持 RESTful 編程風格的請求。 |
1.2.2 SpringMVC在三層架構的位置
1.2.3 SpringMVC的優勢
1、清晰的角色劃分: 前端控制器(DispatcherServlet) 請求到處理器映射(HandlerMapping) 處理器適配器(HandlerAdapter) 視圖解析器(ViewResolver) 處理器或頁面控制器(Controller) 驗證器( Validator) 命令對象(Command 請求參數綁定到的對象就叫命令對象) 表單對象(Form Object 提供給表單展示和提交到的對象就叫表單對象)。 2、分工明確,而且擴展點相當靈活,可以很容易擴展,雖然幾乎不需要。 3、由於命令對象就是一個 POJO,無需繼承框架特定 API,可以使用命令對象直接作爲業務對象。 4、和 Spring 其他框架無縫集成,是其它 Web 框架所不具備的。 5、可適配,通過 HandlerAdapter 可以支持任意的類作爲處理器。 6、可定製性,HandlerMapping、ViewResolver 等能夠非常簡單的定製。 7、功能強大的數據驗證、格式化、綁定機制。 9、本地化、主題的解析的支持,使我們更容易進行國際化和主題的切換。 置支持等等。 |
1.2.4 SpringMVC 和 Struts2 的優略分析
共同點: 它們都是表現層框架,都是基於 MVC 模型編寫的。 它們的底層都離不開原始 ServletAPI。 它們處理請求的機制都是一個核心控制器。
區別: Spring MVC 是基於方法設計的,而 Struts2 是基於類,Struts2 每次執行都會創建一個動作類。所 以 Spring MVC 會稍微比 Struts2 快些。 Spring MVC 使用更加簡潔,同時還支持 JSR303, 處理 ajax 的請求更方便 升,尤其是 struts2 的表單標籤,遠沒有 html 執行效率高 |
第2章 SpringMVC 的入門
2.1 SpringMVC 的入門案例
2.1.1 前期準備
下載開發包: https://spring.io/projects
其實spring mvc的jar包就在之前我們的spring框架開發包中。
創建一個 javaweb 工程
2.1.2 拷貝jar包
2.1.3 配置核心控制器-一個 Servlet
<?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">
<!-- 配置 spring mvc 的核心控制器 --> <servlet>
<servlet-name>SpringMVCDispatcherServlet</servlet-name> <servlet-class>
org.springframework.web.servlet.DispatcherServlet </servlet-class>
<!-- 配置初始化參數,用於讀取 SpringMVC 的配置文件 --> <init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:SpringMVC.xml</param-value> </init-param>
<!-- 配置 servlet 的對象的創建時間點:應用加載時創建。 取值只能是非 0 正整數,表示啓動順序 -->
<load-on-startup>1</load-on-startup> </servlet>
<servlet-mapping>
<servlet-name>SpringMVCDispatcherServlet</servlet-name>
<url-pattern>/</url-pattern> </servlet-mapping>
</web-app>
2.1.4 創建 spring mvc 的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 配置創建 spring 容器要掃描的包 -->
<context:component-scan base-package="com.itheima">
</context:component-scan>
<!-- 配置視圖解析器 --> bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/pages/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
2.1.5 編寫控制器並使用註解配置
/**
* spring mvc的入門案例 * @author 黑馬程序員
* @Company http://www.ithiema.com * @Version 1.0
*/
@Controller("helloController")
public class HelloController {
@RequestMapping("/hello") public String sayHello() {
System.out.println("HelloController 的 sayHello 方法執行了。。。。");
return "success"; }
}
2.1.6 測試
2.2 入門案例的執行過程及原理分析
2.2.1 案例的執行過程
1、服務器啓動,應用被加載。讀取到 web.xml 中的配置創建 spring 容器並且初始化容器中的對象。 從入門案例中可以看到的是:HelloController 和 InternalResourceViewResolver,但是遠不 止這些。 2、瀏覽器發送請求,被 DispatherServlet 捕獲,該 Servlet 並不處理請求,而是把請求轉發出去。轉發 的路徑是根據請求 URL,匹配@RequestMapping 中的內容。 3、匹配到了後,執行對應方法。該方法有一個返回值。 4、根據方法的返回值,藉助 InternalResourceViewResolver 找到對應的結果視圖。 5、渲染結果視圖,響應瀏覽器。 |
2.2.2 SpringMVC的請求響應流程
2.3 入門案例中涉及的組件
2.3.1 DispatcherServlet:前端控制器
用戶請求到達前端控制器,它就相當於 mvc 模式中的 c,dispatcherServlet 是整個流程控制的中心,由 它調用其它組件處理用戶的請求,dispatcherServlet 的存在降低了組件之間的耦合性。
2.3.2 HandlerMapping:處理器映射器
HandlerMapping 負責根據用戶請求找到 Handler 即處理器,SpringMVC 提供了不同的映射器實現不同的 映射方式,例如:配置文件方式,實現接口方式,註解方式等。
2.3.3 Handler:處理器
它就是我們開發中要編寫的具體業務控制器。由 DispatcherServlet 把用戶請求轉發到 Handler。由 Handler 對具體的用戶請求進行處理。
2.3.4 HandlAdapter:處理器適配器
通過 HandlerAdapter 對處理器進行執行,這是適配器模式的應用,通過擴展適配器可以對更多類型的處理器進行執行。
2.3.5 View Resolver:視圖解析器
View Resolver 負責將處理結果生成 View 視圖,View Resolver 首先根據邏輯視圖名解析成物理視圖名即具體的頁面地址,再生成 View 視圖對象,最後對 View 進行渲染將處理結果通過頁面展示給用戶。
2.3.6 View:視圖
SpringMVC 框架提供了很多的 View 視圖類型的支持,包括:jstlView、freemarkerView、pdfView 等。我們最常用的視圖就是 jsp。
一般情況下需要通過頁面標籤或頁面模版技術將模型數據通過頁面展示給用戶,需要由程序員根據業務需求開 發具體的頁面。
2.3.7 <mvc:annotation-driven>說明
在 SpringMVC 的各個組件中,處理器映射器、處理器適配器、視圖解析器稱爲 SpringMVC 的三大組件。 使用<mvc:annotation-driven>自動加載 RequestMappingHandlerMapping(處理映射器)和 RequestMappingHandlerAdapter ( 處 理 適 配 器 ) , 可 用 在 SpringMVC.xml 配 置 文 件 中 使 用 <mvc:annotation-driven>替代註解處理器和適配器的配置。 |
它就相當於在 xml 中配置了:
<!-- 上面的標籤相當於 如下配置-->
<!-- Begin -->
<!-- HandlerMapping -->
<bean
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerM apping">
</bean>
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
</bean>
<!-- HandlerAdapter -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerA dapter">
</bean>
<bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter">
</bean>
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter">
</bean>
<!-- HadnlerExceptionResolvers -->
<bean
class="org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExcept ionResolver">
</bean>
<bean class="org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolv er">
</bean>
<bean class="org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver" >
</bean>
<!-- End -->
2.4 RequestMapping 註解
2.4.1 使用說明
源碼:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME) @Documented
@Mapping
public @interface RequestMapping { }
作用:
用於建立請求 URL 和處理請求方法之間的對應關係。
出現位置: 類上: 賬戶模塊: /account/add /account/update /account/delete ... 訂單模塊:
屬性: 紅色的部分就是把 RequsetMappding 寫在類上,使我們的 URL 更加精細。 方法上: 請求 URL 的第二級訪問目錄。 method:用於指定請求的方式。 params:用於指定限制請求參數的條件。它支持簡單的表達式。要求請求參數的 key 和 value 必須和 配置的一模一樣。 params = {"accountName"},表示請求參數必須有 accountName params = {"moeny!100"},表示請求參數中money不能是100。 headers:用於指定限制請求消息頭的條件。 注意: 例如: params = {"accountName"},表示請求參數必須有 accountName params = {"moeny!100"},表示請求參數中money不能是100。 headers:用於指定限制請求消息頭的條件。 注意: |
2.4.2 使用示例
2.4.2.1 出現位置的示例:
控制器代碼 :
/**
* RequestMapping 註解出現的位置
* @author 黑馬程序員
* @Company http://www.ithiema.com * @Version 1.0
*/
@Controller("accountController") @RequestMapping("/account") public class AccountController {
@RequestMapping("/findAccount") public String findAccount() {
System.out.println("查詢了賬戶。。。。");
return "success"; }
}
@RequestMapping("/findAccount") public String findAccount() {
System.out.println("查詢了賬戶。。。。");
return "success"; }
}
jsp 中的代碼:
<%@ page language="java" contentType="text/html; charset=UTF-8"
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>requestmapping 的使用</title>
</head>
<body>
<!-- 第一種訪問方式 -->
<a href="${pageContext.request.contextPath}/account/findAccount">
查詢賬戶
</a>
<br/>
<!-- 第二種訪問方式 -->
<a href="account/findAccount">查詢賬戶</a>
</body>
</html>
<!--注意:當我們使用此種方式配置時,在 jsp 中第二種寫法時,不要在訪問 URL 前面加/,否則無法找到資源。-->
2.4.2.2 method 屬性的示例:
控制器代碼:
/**
* 保存賬戶
* @return
*/ @RequestMapping(value="/saveAccount",method=RequestMethod.POST) public String saveAccount() {
System.out.println("保存了賬戶");
return "success"; }
jsp 代碼:
<!-- 請求方式的示例 -->
<a href="account/saveAccount">保存賬戶,get請求</a> <br/>
<form action="account/saveAccount" method="post">
<input type="submit" value="保存賬戶,post請求">
</form>
注意:
當使用 get 請求時,提示錯誤信息是 405,信息是方法不支持 get 方式請求
2.4.2.3 params 屬性的示例:
控制器的代碼:
/**
* 刪除賬戶 * @return
*/
@RequestMapping(value="/removeAccount",params= {"accountName","money>100"}) public String removeAccount() {
System.out.println("刪除了賬戶");
return "success";
}
jsp 中的代碼:
<!-- 請求參數的示例 -->
<a href="account/removeAccount?accountName=aaa&money>100">刪除賬戶,金額 100</a>
<br/>
<a href="account/removeAccount?accountName=aaa&money>150">刪除賬戶,金額 150</a>
注意:
當我們點擊第一個超鏈接時,可以訪問成功。
當我們點擊第二個超鏈接時,無法訪問。如下圖:
第3章 請求參數的綁定
3.1 綁定說明
3.1.1 綁定的機制
我們都知道,表單中請求參數都是基於 key=value 的。
SpringMVC 綁定請求參數的過程是通過把表單提交請求參數,作爲控制器中方法參數進行綁定的。 例如:
<a href="account/findAccount?accountId=10">查詢賬戶</a>
中請求參數是: accountId=10
/**
* 查詢賬戶 * @return */
@RequestMapping("/findAccount")
public String findAccount(Integer accountId) {
System.out.println("查詢了賬戶。。。。"+accountId); return "success";
}
3.1.2 支持的數據類型:
基本類型參數:
包括基本類型和 String 類型
POJO 類型參數:
包括實體類,以及關聯的實體類
數組和集合類型參數 :
包括 List 結構和 Map 結構的集合(包括數組) SpringMVC 綁定請求參數是自動實現的,但是要想使用,必須遵循使用要求。
3.1.3 使用要求:
如果是基本類型或者 String 類型: 要求我們的參數名稱必須和控制器中方法的形參名稱保持一致。(嚴格區分大小寫)
如果是 POJO 類型,或者它的關聯對象:
要求表單中參數名稱和 POJO 類的屬性名稱保持一致。並且控制器方法的參數類型是 POJO 類型。
如果是集合類型 ,有兩 種方式:
第一種:
要求集合類型的請求參數必須在 POJO 中。在表單中請求參數名稱要和 POJO 中集合屬性名稱相同。 給 List 集合中的元素賦值,使用下標。
給 Map 集合中的元素賦值,使用鍵值對。
第二種:
接收的請求參數是 json 格式數據。需要藉助一個註解實現。
注意: 它還可以實現一些數據類型自動轉換。內置轉換器全都在:
org.springframework.core.convert.support 包下。
有:
java.lang.Boolean -> java.lang.String : ObjectToStringConverter
java.lang.Character -> java.lang.Number : CharacterToNumberFactory
java.lang.Character -> java.lang.String : ObjectToStringConverter
java.lang.Enum -> java.lang.String : EnumToStringConverter
java.lang.Number -> java.lang.Character : NumberToCharacterConverter
java.lang.Number -> java.lang.Number : NumberToNumberConverterFactory
java.lang.Number -> java.lang.String : ObjectToStringConverter
java.lang.String -> java.lang.Boolean : StringToBooleanConverter
java.lang.String -> java.lang.Character : StringToCharacterConverter
java.lang.String -> java.lang.Enum : StringToEnumConverterFactory
java.lang.String -> java.lang.Number : StringToNumberConverterFactory
java.lang.String -> java.util.Locale : StringToLocaleConverter
java.lang.String -> java.util.Properties StringToPropertiesConverter
java.lang.String -> java.util.UUID : StringToUUIDConverter
java.util.Locale -> java.lang.String : ObjectToStringConverter
java.util.Properties -> java.lang.String : PropertiesToStringConverter
java.util.UUID -> java.lang.String : ObjectToStringConverter
......
如遇特殊類型轉換要 求,需要我們自己編寫自定義類型轉換器。
3.1.4 使用示例
3.1.4.1 基本類型和 String 類型作爲參數
jsp 代碼:
<!-- 基本類型示例 -->
<a href="account/findAccount?accountId=10&accountName=zhangsan">查詢賬戶</a>
控制器代碼:
/**
* 查詢賬戶 * @return
*/
@RequestMapping("/findAccount")
public String findAccount(Integer accountId,String accountName) {
System.out.println("查詢了賬戶。。。。"+accountId+","+ );
return "success"; }
運行結果:
3.1.4.2 POJO 類型作爲參數
實體類代碼:
/**
* 賬戶信息
* @author 黑馬程序員
* @Company http://www.ithiema.com
* @Version 1.0
*/
public class Account implements Serializable {
private Integer id; private String name; private Float money; private Address address; //getters and setters
}
/**
* 地址的實體類
* @author 黑馬程序員
* @Company http://www.ithiema.com * @Version 1.0
*/
public class Address implements Serializable {
private String provinceName; private String cityName; //getters and setters
}
jsp 代碼:
<!-- pojo 類型演示 -->
<form action="account/saveAccount" method="post">
賬戶名稱:<input type="text" name="name" ><br/>
賬戶金額:<input type="text" name="money" ><br/>
賬戶省份:<input type="text" name="address.provinceName" ><br/>
賬戶城市:<input type="text" name="address.cityName" ><br/> <input type="submit" value="保存">
</form>
控制器代碼:
/**
* 保存賬戶
* @param account * @return
*/
@RequestMapping("/saveAccount")
public String saveAccount(Account account) {
System.out.println("保存了賬戶。。。。"+account);
return "success"; }
運行結果:
3.1.4.3 POJO 類中包含集合類型參數
實體類代碼:
/**
* 用戶實體類
* @author 黑馬程序員
* @Company http://www.ithiema.com
* @Version 1.0
*/
public class User implements Serializable {
private String username; private String password; private Integer age;
private List<Account> accounts;
private Map<String,Account> accountMap;
//getters and setters
@Override
public String toString() {
return "User [username=" + username + ", password=" + password + ", age=" + age + ",\n accounts=" + accounts
+ ",\n accountMap=" + accountMap + "]";
}
}
jsp 代碼:
<!-- POJO 類包含集合類型演示 -->
<form action="account/updateAccount" method="post">
用戶名稱:<input type="text" name="username" ><br/>
用戶密碼:<input type="password" name="password" ><br/>
用戶年齡:<input type="text" name="age" ><br/>
賬戶1名稱:<input type="text" name="accounts[0].name" ><br/>
賬戶1金額:<input type="text" name="accounts[0].money" ><br/>
賬戶2名稱:<input type="text" name="accounts[1].name" ><br/>
賬戶2金額:<input type="text" name="accounts[1].money" ><br/>
賬戶3名稱:<input type="text" name="accountMap['one'].name" ><br/>
賬戶3金額:<input type="text" name="accountMap['one'].money" ><br/>
賬戶4名稱:<input type="text" name="accountMap['two'].name" ><br/>
賬戶4金額:<input type="text" name="accountMap['two'].money" ><br/>
<input type="submit" value="保存">
</form>
控制器代碼:
/**
* 更新賬戶 * @return */
@RequestMapping("/updateAccount")
public String updateAccount(User user) {
System.out.println("更新了賬戶。。。。"+user);
return "success"; }
運行結果:
3.1.4.4 請求參數亂碼問題
post 請求方式:
<!-- 配置 springMVC 編碼過濾器 --> <filter>
<filter-name>CharacterEncodingFilter</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>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 在 springmvc 的配置文件中可以配置,靜態資源不過濾:-->
<!-- location表示路徑,mapping表示文件,**表示該目錄下的文件以及子目錄的文件 --> <mvc:resources location="/css/" mapping="/css/**"/>
<mvc:resources location="/images/" mapping="/images/**"/> <mvc:resources location="/scripts/" mapping="/javascript/**"/>
get 請求方式:
tomacat 對 GET 和 POST 請求處理方式是不同的,GET 請求的編碼問題,要改 tomcat 的 server.xml 配置文件,如下:
<Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>
改爲:
<Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"
useBodyEncodingForURI="true"/>
如果遇到 ajax 請求仍然亂碼,請把:
useBodyEncodingForURI="true"改爲 URIEncoding="UTF-8"
3.2特殊情況
3.2.1 自定義類型轉換器
3.2.1.1 使用場景:
jsp 代碼:
<!-- 特殊情況之:類型轉換問題 -->
<a href="account/deleteAccount?date=2018-01-01">根據日期刪除賬戶</a>
控制器代碼:
/**
* 刪除賬戶
* @return
*/
@RequestMapping("/deleteAccount")
public String deleteAccount(String date) {
System.out.println("刪除了賬戶。。。。"+date);
return "success"; }
運行結果:
當我們把控制器中方法參數的類型改爲 Date 時:
/**
* 刪除賬戶 * @return */
@RequestMapping("/deleteAccount")
public String deleteAccount(Date date) {
System.out.println("刪除了賬戶。。。。"+date);
return "success"; }
運行結果:
異常提示:
Failed to bind request element: org.springframework.web.method.annotation.MethodArgumentTypeMismatchExcept ion: Failed to convert value of type 'java.lang.String' to required type 'java.util.Date'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.util.Date] for value '2018-01-01'; nested exception is java.lang.IllegalArgumentException |
3.2.1.2 使用步驟
第一步:定義一個類,實現 Converter 接口,該接口有兩個泛型。
public interface <S, T> {//S:表示接受的類型,T:表示目標類型 /**
* 實現類型轉換的方法 */
Converter
@Nullable
T convert(S source);
}
/**
* 自定義類型轉換器
* @author 黑馬程序員
* @Company http://www.ithiema.com * @Version 1.0
*/
public class StringToDateConverter implements Converter<String, Date> {
/**
* 用於把 String 類型轉成日期類型 */
@Override
public Date convert(String source) { DateFormat format = null;
try {
if(StringUtils.isEmpty(source)) {
throw new NullPointerException("請輸入要轉換的日期");
}
format = new SimpleDateFormat("yyyy-MM-dd"); Date date = format.parse(source);
return date;
} catch (Exception e) {
throw new RuntimeException("輸入日期有誤"); }
}
}
第二步:在 spring 配置文件中配置類型轉換器。spring 配置類型轉換器的機制是,將自定義的轉換器註冊到類型轉換服務中去。
<!-- 配置類型轉換器工廠 --> <bean id="converterService"
class="org.springframework.context.support.ConversionServiceFactoryBean"> <!-- 給工廠注入一個新的類型轉換器 -->
<property name="converters">
<array>
<!-- 配置自定義類型轉換器 -->
<bean class="com.itheima.web.converter.StringToDateConverter"></bean>
</array>
</property>
</bean>
第三步:在 annotation-driven 標籤中引用配置的類型轉換服務
<!-- 引用自定義類型轉換器 --> <mvc:annotation-driven
conversion-service="converterService"></mvc:annotation-driven>
運行結果:
3.2.2 使用 ServletAPI 對象作爲方法參數
SpringMVC 還支持使用原始 ServletAPI 對象作爲控制器方法的參數。支持原始 ServletAPI 對象有:
HttpServletRequest HttpServletResponse HttpSession java.security.Principal Locale InputStream OutputStream Reader Writer |
我們可以把上述對象 ,直接寫在控制的方法參數中使用。
部分示例代碼:
jsp 代碼:
<!-- 原始 ServletAPI 作爲控制器參數 -->
<a href="account/testServletAPI">測試訪問ServletAPI</a>
控制器中的代碼:
/**
* 測試訪問 testServletAPI
* @return
*/
@RequestMapping("/testServletAPI")
public String testServletAPI(HttpServletRequest request,
HttpServletResponse response,
System.out.println(request); System.out.println(response); System.out.println(session); return "success";
session) {
HttpSession
}
執行結果:
第4章 常用註解
4.1RequestParam
4.1.1 使用說明
作用:
把請求中指定名稱的參數給控制器中的形參賦值。
屬性:
value:請求參數中的名稱。
required:請求參數中是否必須提供此參數。默認值:true。表示必須提供,如果不提供將報錯
4.1.2 使用示例
jsp 中的代碼:
<!-- requestParams 註解的使用 -->
<a href="springmvc/useRequestParam?name=test">requestParam註解</a>
控制器中的代碼:
/**
* requestParams 註解的使用 * @param username
* @return
*/
@RequestMapping("/useRequestParam")
public String useRequestParam(@RequestParam("name")String username,
@RequestParam(value="age",required=false)Integer age){ System.out.println(username+","+age);
return "success"; }
運行結果:
4.2RequestBody
4.2.1 使用說明
作用:
用於獲取請求體內容。直接使用得到是 key=value&key=value...結構的數據。
get 請求方式不適用。
屬性:
required:是否必須有請求體。默認值是:true。當取值爲 true 時,get 請求方式會報錯。如果取值爲 false,get 請求得到是 null。
4.2.2 使用示例
post 請求 jsp 代碼:
<!-- request body 註解 -->
<form action="springmvc/useRequestBody" method="post">
用戶名稱:<input type="text" name="username" ><br/> 用戶密碼:<input type="password" name="password" ><br/>
用戶年齡:<input type="text" name="age" ><br/>
<input type="submit" value="保存"> </form>
get 請求 jsp 代碼:
<a href="springmvc/useRequestBody?body=test">requestBody註解get請求</a>
控制器代碼:
/**
* RequestBody註解 * @param user
* @return
*/
@RequestMapping("/useRequestBody")
public String useRequestBody(@RequestBody(required=false) String body){
System.out.println(body);
return "success"; }
post 請求運行結果:
get 請求運行結果:
4.3 PathVaribale
4.3.1 使用說明
作用:用於綁定 url 中的佔位符。例如:請求 url 中 /delete/{id},這個{id}就是 url 佔位符。
url 支持佔位符是 spring3.0 之後加入的。是 springmvc 支持 rest 風格 URL 的一個重要標誌。
屬性:
value:用於指定 url 中佔位符名稱。
required:是否必須提供佔位符。
4.3.2 使用示例
jsp 代碼:
<!-- PathVariable 註解 -->
<a href="springmvc/usePathVariable/100">pathVariable 註解</a>
控制器代碼:
/**
* PathVariable註解 * @param user
* @return
*/
@RequestMapping("/usePathVariable/{id}")
public String usePathVariable(@PathVariable("id") Integer id){
System.out.println(id);
return "success"; }
運行結果:
4.3.3 REST 風格 URL
什麼是 rest: |
REST(英文:Representational State Transfer,簡稱 REST)描述了一個架構樣式的網絡系統, 比如 web 應用程序。它首次出現在 2000 年 Roy Fielding 的博士論文中,他是 HTTP 規範的主要編寫者之 一。在目前主流的三種 Web 服務交互方案中,REST 相比於 SOAP(Simple Object Access protocol,簡單 對象訪問協議)以及 XML-RPC 更加簡單明瞭,無論是對 URL 的處理還是對 Payload 的編碼,REST 都傾向於用更 加簡單輕量的方法設計和實現。值得注意的是 REST 並沒有一個明確的標準,而更像是一種設計的風格。 它本身並沒有什麼實用性,其核心價值在於如何設計出符合 REST 風格的網絡接口。 restful 的優點 它結構清晰、符合標準、易於理解、擴展方便,所以正得到越來越多網站的採用。 restful 的特性: 資源(Resources):網絡上的一個實體,或者說是網絡上的一個具體信息。 它可以是一段文本、一張圖片、一首歌曲、一種服務,總之就是一個具體的存在。可以用一個 URI(統一 資源定位符)指向它,每種資源對應一個特定的 URI 。要 比如,文本可以用 txt 格式表現,也可以用 HTML 格式、XML 格式、JSON 格式表現,甚至可以採用二 進制格式。 HTTP 協議,是一個無狀態協議,即所有的狀態都保存在服務器端。因此,如果客戶端想要操作服務器, 必須通過某種手段,讓服務器端發生“狀態轉化”(State Transfer)。而這種轉化是建立在表現層之上的,所以 就是 “表現層狀態轉化”。具體說,就是 HTTP 協議裏面,四個表示操作方式的動詞:GET、POST、PUT、 DELETE。它們分別對應四種基本操作:GET 用來獲取資源,POST 用來新建資源,PUT 用來更新資源,DELETE 用來 刪除資源。 restful 的示例: /account/1 HTTP GET : 得到 id = 1 的 account /account/1 HTTP DELETE: 刪除 id = 1 的 account /account/1 HTTP PUT: 更新 id = 1 的 account /account HTTP POST: 新增 account |
4.3.4 基於 HiddentHttpMethodFilter 的示例
作用:
由於瀏覽器 form 表單只支持 GET 與 POST 請求,而DELETE、PUT 等 method 並不支持,Spring3.0 添 加了一個過濾器,可以將瀏覽器請求改爲指定的請求方式,發送給我們的控制器方法,使得支持 GET、POST、PUT
與 DELETE 請求。
使用方法:
第一步:在 web.xml 中配置該過濾器。
第二步:請求方式必須使用 post 請求。
第三步:按照要求提供_method 請求參數,該參數的取值就是我們需要的請求方式。
源碼分析:
jsp 中示例代碼:
<!-- 保存 -->
<form action="springmvc/testRestPOST" method="post">
用戶名稱:<input type="text" name="username"><br/>
<!-- <input type="hidden" name="_method" value="POST"> --> <input type="submit" value="保存">
</form>
<hr/>
<!-- 更新 -->
<form action="springmvc/testRestPUT/1" method="post">
用戶名稱:<input type="text" name="username"><br/>
<input type="hidden" name="_method" value="PUT">
<input type="submit" value="更新">
</form>
<hr/>
<!-- 刪除 -->
<form action="springmvc/testRestDELETE/1" method="post">
<input type="hidden" name="_method" value="DELETE">
<input type="submit" value="刪除">
</form>
<hr/>
<!-- 查詢一個 -->
<form action="springmvc/testRestGET/1" method="post">
<input type="hidden" name="_method" value="GET">
<input type="submit" value="查詢">
</form>
控制器中示例代碼:
/**
* post請求:保存
* @param username * @return
*/
@RequestMapping(value="/testRestPOST",method=RequestMethod.POST)
public String testRestfulURLPOST(User user){ System.out.println("rest post"+user); return "success";
}
/**
* put請求:更新
* @param username
* @return
*/
@RequestMapping(value="/testRestPUT/{id}",method=RequestMethod.PUT)
public String testRestfulURLPUT(@PathVariable("id")Integer id,User user){
System.out.println("rest put "+id+","+user);
return "success"; }
/**
* post請求:刪除
* @param username * @return
*/
@RequestMapping(value="/testRestDELETE/{id}",method=RequestMethod.DELETE) public String testRestfulURLDELETE(@PathVariable("id")Integer id){
System.out.println("rest delete "+id);
return "success"; }
/**
* post請求:查詢
* @param username * @return
*/
@RequestMapping(value="/testRestGET/{id}",method=RequestMethod.GET)
public String testRestfulURLGET(@PathVariable("id")Integer id){ System.out.println("rest get "+id);
return "success";
}
運行結果:
4.4.1 使用說明
4.4 RequestHeader
作用:
用於獲取請求消息頭。
屬性:
value:提供消息頭名稱
required:是否必須有此消息頭
注:
在實際開發中一般不怎麼用。
4.4.2 使用示例
jsp 中代碼:
<!-- RequestHeader 註解 -->
<a href="springmvc/useRequestHeader">獲取請求消息頭</a>
控制器中代碼:
/**
* RequestHeader 註解 * @param user
* @return
*/
@RequestMapping("/useRequestHeader")
public String useRequestHeader(@RequestHeader(value="Accept-Language",
required=false)String requestHeader){ System.out.println(requestHeader);
return "success";
}
運行結果:
4.5 CookieValue
4.5.1 使用說明
作用:
用於把指定 cookie 名稱的值傳入控制器方法參數。
屬性:
value:指定 cookie 的名稱。
required:是否必須有此 cookie。
4.5.2 使用示例
jsp 中的代碼:
<!-- CookieValue 註解 -->
<a href="springmvc/useCookieValue">綁定cookie的值</a>
控制器中的代碼:
/**
* Cookie註解註解 * @param user
* @return
*/
@RequestMapping("/useCookieValue")
public String useCookieValue(@CookieValue(value="JSESSIONID",required=false) String cookieValue){
System.out.println(cookieValue);
return "success"; }
運行結果:
4.6 ModelAttribute
4.6.1 使用說明
作用:
該註解是 SpringMVC4.3 版本以後新加入的。它可以用於修飾方法和參數。
出現在方法上,表示當前方法會在控制器的方法執行之前,先執行。它可以修飾沒有返回值的方法,也可 以修飾有具體返回值的方法。
出現在參數上,獲取指定的數據給參數賦值。
屬性:
value:用於獲取數據的 key。key 可以是 POJO 的屬性名稱,也可以是 map 結構的 key。
應用場景:
當表單提交數據不是完整的實體類數據時,保證沒有提交數據的字段使用數據庫對象原來的數據。
例如:
我們在編輯一個用戶時, 用戶有一個創建 信息字段,該字段 的值是不允許被 修改的。在提交表 單數
據是肯定沒有此字段的內容,一旦更新會把該字段內容置爲 null,此時就可以使用此註解解決問題。
4.6.2 使用示例
4.6.2.1 基於 POJO 屬性的基本使用:
jps 代碼:
<!-- ModelAttribute 註解的基本使用 -->
<a href="springmvc/testModelAttribute?username=test">測試modelattribute</a>
控制器代碼:
/**
* 被 ModelAttribute 修飾的方法 * @param user
*/
@ModelAttribute
public void showModel(User user) {
System.out.println("執行了 showModel 方法"+user.getUsername());
}
/**
* 接收請求的方法 * @param user
* @return
*/
@RequestMapping("/testModelAttribute")
public String testModelAttribute(User user) {
System.out.println("執行了控制器的方法"+user.getUsername());
return "success"; }
運行結果:
4.6.2.2 基於 Map 的應用場景示例
1:ModelAttribute 修飾方法帶返回值
需求:
修改用戶信息,要求 用戶的密碼不能修改
jsp 的代碼:
<!-- 修改用戶信息 -->
<form action="springmvc/updateUser" method="post"> 用戶名稱:<input type="text" name="username" ><br/>
用戶年齡:<input type="text" name="age" ><br/>
<input type="submit" value="保存"> </form>
控制的代碼:
/**
* 查詢數據庫中用戶信息 * @param user
*/
@ModelAttribute
public User showModel(String username) { //模擬去數據庫查詢
User abc = findUserByName(username); System.out.println("執行了 showModel 方法"+abc);
return abc; }
/**
* 模擬修改用戶方法 * @param user
* @return
*/
@RequestMapping("/updateUser")
public String testModelAttribute(User user) {
System.out.println("控制器中處理請求的方法:修改用戶:"+user);
return "success"; }
/**
* 模擬去數據庫查詢
* @param username
* @return
*/
private User findUserByName(String username) {
User user = new User(); user.setUsername(username);
user.setAge(19); user.setPassword("123456"); return user;
}
運行結果:
4.6.2.3 基於 Map 的應用場景示例 1:ModelAttribute 修飾方法不帶返回值
需求:
修改用戶信息,要求 用戶的密碼不能修改
jsp 中的代碼:
<!-- 修改用戶信息 -->
<form action="springmvc/updateUser" method="post">
用戶名稱:<input type="text" name="username" ><br/> 用戶年齡:<input type="text" name="age" ><br/>
<input type="submit" value="保存"> </form>
控制器中的代碼:
/**
* 查詢數據庫中用戶信息
* @param user
*/
@ModelAttribute
public void showModel(String username,Map<String,User> map) {
//模擬去數據庫查詢
User user = findUserByName(username);
System.out.println("執行了 showModel 方法"+user);
map.put("abc",user);
}
/**
* 模擬修改用戶方法
* @param user
* @return */
@RequestMapping("/updateUser")
public String testModelAttribute(@ModelAttribute("abc")User user) {
System.out.println("控制器中處理請求的方法:修改用戶:"+user);
return "success";
}
/**
* 模擬去數據庫查詢
* @param username
* @return */
private User findUserByName(String username) {
User user = new User();
user.setUsername(username);
user.setAge(19);
user.setPassword("123456");
return user;
}
運行結果:
4.7 SessionAttribute
4.7.1 使用說明
作用:
用於多次執行控制器方法間的參數共享。
屬性:
value:用於指定存入的屬性名稱
type:用於指定存入的數據類型。
4.7.2 使用示例
jsp 中的代碼:
<!-- SessionAttribute 註解的使用 -->
<a href="springmvc/testPut">存入SessionAttribute</a>
<hr/>
<a href="springmvc/testGet">取出SessionAttribute</a> <hr/>
<a href="springmvc/testClean">清除SessionAttribute</a>
控制器中的代碼:
/**
* SessionAttribute 註解的使用
* @author 黑馬程序員
* @Company http://www.ithiema.com
* @Version 1.0
*/
@Controller("sessionAttributeController")
@RequestMapping("/springmvc")
@SessionAttributes(value ={"username","password"},types={Integer.class}) public class SessionAttributeController {
/**
* 把數據存入 SessionAttribute
* @param model
* @return
* Model 是 spring 提供的一個接口,該接口有一個實現類 ExtendedModelMap
* 該類繼承了 ModelMap,而 ModelMap 就是 LinkedHashMap 子類
*/
@RequestMapping("/testPut")
public String testPut(Model model){
model.addAttribute("username", "泰斯特");
這幾個參數
model.addAttribute("password","123456");
model.addAttribute("age", 31);
//跳轉之前將數據保存到 username、password 和 age 中,因爲註解@SessionAttribute 中有
return "success";
}
@RequestMapping("/testGet")
public String testGet(ModelMap model){
System.out.println(model.get("username")+";"+model.get("password")+";"+model.get("age"));
return "success";
}
@RequestMapping("/testClean")
public String complete(SessionStatus sessionStatus){
sessionStatus.setComplete();
}
return "success"; }
運行結果: