目錄
前言
1. MVC架構模式
2. 三層架構
3. SpringMVC
1. 第一個SpringMVC項目(Hello World)
2. 獲取請求參數(shù)
3. 在域?qū)ο螅╮equest碑幅、session走趋、application)中共享數(shù)據(jù)
4. 視圖和視圖解析器
5. RESTful(REST風格)
6. Converter類型轉(zhuǎn)換器
7. Formatter格式化器
8. json數(shù)據(jù)轉(zhuǎn)換(Java對象<--轉(zhuǎn)換-->json數(shù)據(jù))
SpringMVC:Spring提供的基于MVC設計模式的輕量級Web開發(fā)框架(本質(zhì):對Servlet的進一步封裝)师逸。
前言
- MVC(Model-View-Controller奏司,模型-視圖-控制器)
是一種架構模式坷虑,將界面和業(yè)務邏輯分離卸察,使代碼具有更高的可擴展性既忆、可復用性柠座、可維護性以及靈活性邑雅。
1. Model(模型層) 應用的主體部分,由以下2部分組成:
1. 實體類Bean(通常與數(shù)據(jù)庫中的某個表對應)
用于存儲業(yè)務數(shù)據(jù)妈经。
2. 業(yè)務處理Bean(Service淮野、Dao)
用于處理業(yè)務邏輯、數(shù)據(jù)庫訪問吹泡。
2. View(視圖層)
(由HTML骤星、JSP、CSS荞胡、JavaScript等組成)頁面妈踊,負責用戶交互、展示數(shù)據(jù)泪漂。
3. Controller(控制器)
通常為Servlet廊营,負責接收用戶請求(讓模型層處理數(shù)據(jù),并將處理好的數(shù)據(jù)傳給視圖層萝勤,展示最終的頁面給用戶)露筒。
本身并不做任何業(yè)務處理,只負責調(diào)度View層和Model層敌卓。
大致工作流程如下:
1. 用戶發(fā)送請求到服務器慎式。若請求路徑與web.xml中配置的DispatcherServlet前端控制器的url-pattern相匹配,則該請求會被DispatcherServlet攔截趟径。
2. DispatcherServlet會讀取SpringMVC.xml配置文件瘪吏,通過組件掃描獲取到所有的Controller控制器。將請求信息和所有控制器方法標識的@RequestMapping注解的value蜗巧、method等屬性值進行匹配掌眠,若匹配成功則將請求交給對應控制器方法進行處理。
3. 控制器方法中調(diào)用相應的Model層處理請求幕屹,Model層處理完后將結果(模型數(shù)據(jù))返回到控制器方法蓝丙。
5. 控制器方法返回一個字符串類型的邏輯視圖名级遭,會被視圖解析器轉(zhuǎn)為真正的視圖,并將模型數(shù)據(jù)渲染到視圖中返回給用戶渺尘。
優(yōu)點
1. 降低代碼耦合性
在MVC模式中挫鸽,三層之間相互獨立、各司其職鸥跟。
一旦某一層的需求發(fā)生了變化丢郊,只需要更改相應層中的代碼即可,而不會對其他層中的代碼造成影響医咨。
2. 有利于分工合作
在MVC模式中蚂夕,將應用劃分成三層,可以更好地實現(xiàn)開發(fā)分工(前端專注于視圖層腋逆、熟悉業(yè)務的后臺開發(fā)Model層、不熟悉業(yè)務的后臺開發(fā)Controller層)侈贷。
3. 提高可重用性
在MVC模式中惩歉,多個視圖可以共享同一個模型。
缺點
1. 增加了系統(tǒng)結構和實現(xiàn)的復雜性
對于簡單的應用俏蛮,如果也嚴格遵循MVC模式撑蚌,按照模型、視圖與控制器對系統(tǒng)進行劃分搏屑,會增加系統(tǒng)結構的復雜性争涌,并可能產(chǎn)生過多的更新操作,降低運行效率辣恋。因此亮垫,并不適合中小型項目。
2. 視圖與控制器間的聯(lián)系過于緊密
雖然視圖與控制器是相互分離的伟骨,但它們之間聯(lián)系卻是十分緊密的饮潦。視圖沒有控制器的存在,其應用是很有限的携狭,反之亦然继蜡,這樣就妨礙了它們的獨立重用。
3. 視圖對模型數(shù)據(jù)的低效率訪問
視圖可能需要多次調(diào)用才能獲得足夠的顯示數(shù)據(jù)逛腿。
對未變化數(shù)據(jù)的不必要的頻繁訪問稀并,也將損害操作性能。
- 三層架構
1. 表示層(UI)相當于:MVC模式的View層+Controller層
負責用戶交互单默、展示數(shù)據(jù)碘举、接收用戶請求,將請求交給業(yè)務邏輯層(BLL)和數(shù)據(jù)訪問層(DAL)進行處理雕凹,把處理好的數(shù)據(jù)傳給頁面并展示給用戶殴俱。
2. 業(yè)務邏輯層(BLL)相當于:MVC模式的Model層的一部分(不包含Dao和實體類)
起到承上啟下的作用政冻,接收表示層傳遞來的請求,根據(jù)業(yè)務對數(shù)據(jù)進行處理线欲。
3. 數(shù)據(jù)訪問層(DAL)相當于:MVC模式的Model層的一部分(只包含了Dao接口及實現(xiàn))
用于實現(xiàn)數(shù)據(jù)庫訪問(增刪改查等操作)明场。
- SpringMVC
Spring提供的基于MVC設計模式的輕量級Web開發(fā)框架(為表示層開發(fā)提供的一整套完備的解決方案)。
工作流程
1. 用戶通過瀏覽器發(fā)起一個HTTP請求李丰,該請求會被DispatcherServlet(前端控制器)攔截苦锨;
2. DispatcherServlet會調(diào)用HandlerMapping(處理器映射器);
3. HandlerMapping負責找到具體的處理器(Handler)及攔截器趴泌,并以HandlerExecutionChain執(zhí)行鏈的形式返回給DispatcherServlet舟舒。
4. DispatcherServlet將執(zhí)行鏈返回的Handler信息發(fā)送給HandlerAdapter(處理器適配器);
5. HandlerAdapter根據(jù)Handler信息找到并執(zhí)行相應的Handler(即:控制器方法)對請求進行處理;
6. Handler執(zhí)行完后會將一個ModelAndView對象(SpringMVC的底層對象沾乘,包括Model數(shù)據(jù)模型和View視圖信息)返回給HandlerAdapter歪架。
7. HandlerAdapter接收到ModelAndView對象返回給DispatcherServlet。
8. DispatcherServlet接收到ModelAndView對象后夺鲜,會請求ViewResolver(視圖解析器)對視圖進行解析;
9. ViewResolver解析完成后呐舔,會將View視圖并返回給DispatcherServlet币励;
10. DispatcherServlet接收到具體的View視圖后,進行視圖渲染珊拼,將Model中的模型數(shù)據(jù)填充到View視圖中的request域食呻,生成最終的View(視圖);
11. 視圖負責將結果顯示到瀏覽器(客戶端)澎现。
特點
1. SpringMVC是Spring框架的子框架(可以與IoC容器等Spring其他功能無縫對接)仅胞。
2. SpringMVC支持各種視圖技術(如:JSP、Thymeleaf剑辫、FreeMaker等)饼问。
3. SpringMVC基于原生的Servlet實現(xiàn)(通過功能強大的前端控制器DispatcherServlet,對請求和響應進行統(tǒng)一處理)揭斧。
4. SpringMVC對表示層各細分領域需要解決的問題全方位覆蓋莱革,并提供一整套全面的解決方案。
5. 代碼簡潔讹开,提高開發(fā)效率盅视。
6. 采用松耦合、可插拔的組件結構(即插即用:想使用什么功能就配置相應組件)旦万。高度可配置性闹击、擴展性、靈活性成艘。
7. 性能卓著赏半,適合大型贺归、超大型互聯(lián)網(wǎng)項目的開發(fā)。
8. 支持注解断箫、REST風格拂酣。
常用組件 | 提供者 | 描述 |
---|---|---|
DispatcherServlet | 框架 | 前端控制器(最核心的組件,本質(zhì)是一個Servlet)仲义,負責統(tǒng)一處理和分發(fā)請求婶熬。SpringMVC的流程控制中心,控制整個流程的執(zhí)行埃撵,對各個組件進行統(tǒng)一調(diào)度(降低組件之間的耦合性赵颅,有利于組件之間的拓展)。 |
HandlerMapping | 框架 | 處理器映射器暂刘,根據(jù)請求的url饺谬、method等信息查找相應的Handler(即控制器方法)。 |
Handler | 開發(fā)員 | 處理器(Controller控制器方法)谣拣,對用戶的請求進行處理商蕴。 |
HandlerAdapter | 框架 | 處理器適配器,調(diào)用(根據(jù)HandlerMapping找到的Handler處理器的)具體的控制器方法芝发。 |
ViewResolver | 框架 | 視圖解析器(常用:ThymeleafViewResolver、InternalResourceViewResolver)苛谷,通過ModelAndView對象中的View信息將邏輯視圖名解析為真正的視圖View辅鲸。 |
View | 開發(fā)員 | 視圖(View對象本身由框架提供,但視圖所對應的前端頁面需要開發(fā)員編寫)腹殿,將Model模型數(shù)據(jù)通過頁面展示給用戶独悴。 |
可以看到,開發(fā)人員只需負責創(chuàng)建:控制器(及控制器方法)锣尉、html頁面刻炒。
1. 第一個SpringMVC項目(Hello World)
1. 創(chuàng)建JavaWeb項目(Dynamic Web Project)
選擇Target runtime選項:設置本地的Tomcat服務器版本和JDK版本。
2. 添加依賴包(到webapp/WEB-INF/lib目錄下)
Spring框架相關
1. 核心容器模塊的4個依賴包(core拇厢、beans爱谁、context、expression)孝偎、commons-logging-xxx.jar
2. AOP依賴包(spring-webmvc-aop.jar)
3. SpringMVC依賴包(spring-web-xxx.jar访敌、spring-webmvc-xxx.jar)
Thymeleaf(該技術用于取代jsp)相關
1. attoparser-xxx.jar
2. slf4j-api-xxx.jar
3. thymeleaf-xxx.jar
4. thymeleaf-spring5-xxx.jar
5. unbescape-xxx.jar
下載attoparser-xxx.jar、slf4j-api-xxx.jar衣盾、thymeleaf-xxx.jar寺旺、thymeleaf-spring5-xxx.jar爷抓、unbescape-xxx.jar
3. 配置DispatcherServlet(在web.xml配置文件中)
<!-- 配置SpringMVC的前端控制器(攔截用戶的所有請求,進行統(tǒng)一處理) -->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--
配置SpringMVC配置文件的位置和名稱(當配置文件不在WEB-INF目錄下時需要配置)阻塑。項目啟動時默認會從WEB-INF目錄下尋找配置文件蓝撇,默認的命名規(guī)則為{servlet-name}-servlet.xml。
-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springMVC.xml</param-value>
</init-param>
<!--
如果希望項目啟動時就加載并初始化該Servlet時 配置叮姑。 默認情況下唉地,所有的Servlet(包括DispatcherServlet)都是在第一次調(diào)用時才會被加載(降低了項目啟動時間,但增加了用戶第一次請求所需的時間)传透。
load-on-startup元素取值規(guī)則如下:
1. 取值必須是一個整數(shù)耘沼;
2. 當值小于0或者沒有指定時,則表示容器在該Servlet被首次請求時才會被加載朱盐;
3. 當值大于0或等于0時群嗤,表示容器在項目啟動時就加載并初始化該Servlet,取值越小優(yōu)先級越高兵琳;
4. 當取值相同時狂秘,容器就會自行選擇順序進行加載。
-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<!--
設置springMVC的前端控制器所能處理(攔截)的請求的請求路徑躯肌。
/會攔截所有請求(除了.jsp)者春。
-->
<url-pattern>/</url-pattern>
</servlet-mapping>
4. 創(chuàng)建springMVC.xml配置文件(src目錄下)
web.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
https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 開啟組件掃描(會掃描包路徑下的所有控制器類清女,包括子包) -->
<context:component-scan base-package="com.sst.cx"></context:component-scan>
<!-- 配置Thymeleaf視圖解析器(配置為:映射到/WEB-INF/templates/目錄下的.html文件) -->
<bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
<property name="order" value="1"/>
<property name="characterEncoding" value="UTF-8"/>
<property name="templateEngine">
<bean class="org.thymeleaf.spring5.SpringTemplateEngine">
<property name="templateResolver">
<bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
<!-- 視圖前綴 -->
<property name="prefix" value="/WEB-INF/templates/"/>
<!-- 視圖后綴 -->
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML5"/>
<property name="characterEncoding" value="UTF-8"/>
</bean>
</property>
</bean>
</property>
</bean>
</beans>
5. 創(chuàng)建Controller
在com.sst.cx.controller下創(chuàng)建HelloController.java
package com.sst.cx.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloController {
@RequestMapping("/")
public String sayHello() {
// 邏輯視圖名钱烟,真正的視圖為:視圖前綴+index+視圖后綴(即:/WEB-INF/template/index.html)
return "index";
}
}
說明:
1. @Controller注解:
將一個普通類標識為Controller控制器類,類中的每一個處理請求的方法被稱為“控制器方法”嫡丙∷┫控制器方法在處理完請求后,通常會返回一個字符串類型的邏輯視圖名曙博,通過視圖解析器(在springMVC配置文件中設置視圖解析器)將邏輯視圖名解析為真正的視圖頁面拥刻,并響應給客戶端展示。
SpringMVC的配置文件的<context:component-scan base-package="com.sst.cx"/>標簽會掃描包路徑下的所有使用@Controller注解標注的Controller控制器類父泳。
2. @RequestMapping注解:
1. 用來標注控制器方法時:將請求地址和處理請求的控制器方法建立映射關系般哼。DispatcherServelt攔截請求后會根據(jù)映射關系將請求分發(fā)給指定的控制器方法進行處理。
2. 用來標注控制器類時:所有控制器方法的請求地址的父路徑惠窄。
常用屬性:
1. value屬性
設置(控制器方法關聯(lián)的)請求地址(可匹配多個請求地址)逝她,會和控制器方法建立映射關聯(lián)。
例:
@RequestMapping(value = "/") 只有該屬性時可簡寫為:@RequestMapping("/")
@RequestMapping(value = {"/register", "/login"})
支持Ant風格的通配符路徑:
1. ? 表示任意的單個字符睬捶。
例:@RequestMapping(value = "/testuser?")
2. * 表示任意的 0 個或多個字符黔宛。
例:@RequestMapping(value = "/testuser*")
3. ** 表示任意的一層或多層目錄(只能作為前綴)。
例:@RequestMapping(value = "/**/testuser")
2. name屬性
設置控制器方法的功能描述(用來干什么)
3. method屬性(沒有設置該屬性時,默認支持所有請求方式)
設置控制器方法支持的請求方式(常用:GET臀晃、POST觉渴、DELETE、PUT)徽惋。
例:
@RequestMapping(value = "/toUser",method = RequestMethod.GET)
@RequestMapping(value = "/toUser",method = {RequestMethod.GET,RequestMethod.POST})
5. params屬性
用于指定請求中的參數(shù)案淋,只有當請求中攜帶了符合條件的參數(shù)時,控制器方法才會對該請求進行處理险绘。4種方式:
1. "param" 請求中必須攜帶名為param的參數(shù)踢京。
2. "!param" 請求中不能攜帶名為param的參數(shù)。
3. "param=value" 請求中必須攜帶名為param的參數(shù)宦棺,且參數(shù)值必須為:value瓣距。
4. "param!=value" 請求中不能攜帶參數(shù):param=value(即攜帶參數(shù)param時,其值不能是value)代咸。
例:
@RequestMapping(value = "/testParam", params = {"name=張三", "age=12"})
6. headers屬性
用于設置請求中請求頭信息蹈丸,只有當請求中攜帶指定的請求頭信息時,控制器方法才會處理該請求呐芥。4種方式:
1. "header" 請求中必須攜帶請求頭信息:header 逻杖。
2. "!header" 請求中不能攜帶請求頭信息:header 。
3. "header=value" 請求中必須攜帶請求頭信息:header=value思瘟。
4. "header!=value" 請求中不能攜帶請求頭信息:header=value荸百。
例
@RequestMapping(value = "toUser",headers = {"Content-Type=application/json", "Referer=http://www.baidu.com"})
6. 在/WEB-INF/template/目錄下創(chuàng)建index.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>首頁</title>
</head>
<body>
<h1 th:text="世界你好"></h1>
<!-- <a th:href="@{/register}">注冊</a> -->
</body>
</html>
7. 部署到Tomcat服務器中,并啟動Tomcat服務器滨攻。
在瀏覽器中訪問http://localhost:8080/HelloSpringMVC/
2. 獲取請求參數(shù)
可以通過設置控制器方法的形參來接收/獲取用戶請求中的參數(shù)(4種方式)够话。
方式1. 為請求中的每個參數(shù)添加一個對應的(區(qū)分大小寫)同名形參(不適合參數(shù)過多的情況)
SpringMVC會自動將請求參數(shù)封裝到對應的形參中。當請求中包含多個同名的請求參數(shù)時:以字符串形式(將參數(shù)值以,分隔铡买,如:"a,b")接收;或 以元素類型為字符串的數(shù)組形式(如:{"a","b"})接收霎箍。
無視數(shù)據(jù)類型(可以使用String字符串類型的形參接收所有的請求參數(shù)奇钞;也可以使用對應數(shù)據(jù)類型的參數(shù)來接收請求參數(shù),無須進行數(shù)據(jù)類型轉(zhuǎn)換)漂坏。
例(瀏覽器輸入http://localhost:8080/hello?name=zhangsan&age=12)
@RequestMapping("/hello")
public String requestParam(String name, Integer age) {
System.out.println("nmae:" + name + "age: "+age);
return "index";
}
方式2. 使用@RequestParam注解為請求中的參數(shù)和控制器方法的形參建立映射關系景埃。
SpringMVC會自動將請求參數(shù)封裝到對應的形參中(通常用于解決方式1中不一致的情況)。
@RequestParam注解的常用屬性
1. name屬性(value屬性的別名)等價于value屬性
設置請求中的參數(shù)名顶别。
2. value屬性
設置請求中的參數(shù)名谷徙。
3. required屬性
設置請求參數(shù)名是否必須(默認為true),不包含時會報異常驯绎。只要求其設置參數(shù)名完慧,并未要求其設置參數(shù)值。
4. defaultValue屬性
設置請求參數(shù)的默認值剩失。
注意: defaultValue屬性會使 required ="true" 失效(即將required屬性自動設置為false)屈尼。
例
@RequestMapping("/hello")
public String requestParam(@RequestParam("name") String userName, @RequestParam("age") String userAge) {
System.out.println("nmae:" + userName + "age: "+userAge);
return "index";
}
方式3. 添加一個HttpServletRequest類型的形參册着。
SpringMVC會自動將請求參數(shù)封裝到HttpServletRequest對象中。
例
@RequestMapping("/hello")
public String requestParam(HttpServletRequest request) {
String name = request.getParameter("name");
String age = request.getParameter("age");
return "index";
}
方式4. 添加一個實體類類型的形參(推薦)脾歧。
SpringMVC會自動將請求參數(shù)封裝到該實體類對象的同名屬性中甲捏。
例
@RequestMapping("/hello")
public String requestParam(User user) {
String name = user.getName();
Integer age = user.getAge();
return "index";
}
解決獲取請求參數(shù)的亂碼問題
在post請求中傳遞的參數(shù)為中文時,控制器方法獲取到的參數(shù)值可能會出現(xiàn)亂碼鞭执。
解決:
在web.xml文件中司顿,添加(通常都會添加CharacterEncodingFilter來避免亂碼)
<!-- 請求和響應的字符串過濾器 -->
<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>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
3. 在域?qū)ο螅╮equest、session兄纺、application)中共享數(shù)據(jù)
控制器方法處理請求時大溜,會交給Model層處理,Model層將處理完請求后的結果(模型數(shù)據(jù))返回給Controller囤热。Controller在接收到Model層返回的模型數(shù)據(jù)后猎提,將模型數(shù)據(jù)通過域?qū)ο螅ǚ掌髟趦?nèi)存上創(chuàng)建的一塊存儲空間,用于不同動態(tài)Web資源之間的數(shù)據(jù)傳遞和共享)共享的方式傳遞給View視圖進行渲染旁蔼,將最終的頁面返回給客戶端展示锨苏。
域?qū)ο笾泄蚕頂?shù)據(jù)的常用方式:
方式1. 使用原生ServletAPI向request、session棺聊、application域?qū)ο笾泄蚕頂?shù)據(jù)伞租。
會導致控制器與Servlet容器耦合度過高(不推薦)
例(request)
在控制器方法中添加一個HttpServletRequest類型的形參。
@RequestMapping("/")
public String testServletAPI(HttpServletRequest request) {
request.setAttribute("name", "張三");
return "index";
}
例(session)
在控制器方法中添加一個HttpSession類型的形參限佩。
@RequestMapping("/")
public String testServletAPI(HttpSession session) {
session.setAttribute("name", "張三");
return "index";
}
例(application)
在控制器方法中添加一個HttpSession類型的形參葵诈,通過它獲取到application域?qū)ο蟆? @RequestMapping("/")
public String testServletAPI(HttpSession session) {
ServletContext application = session.getServletContext();
application.setAttribute("name", "張三");
return "index";
}
方式2. 使用Model、Map祟同、ModelMap向request域?qū)ο笾泄蚕頂?shù)據(jù)作喘。
例(Model)
在控制器方法中添加一個Model類型的形參。
@RequestMapping("/")
public String testModelMap(Model model) {
model.addAttribute("name", "張三");
return "index";
}
例(Map)
在控制器方法中添加一個Map類型的形參晕城。
@RequestMapping("/")
public String testModelMap(Map<String, Object> map) {
map.put("name", "張三");
return "index";
}
例(ModelMap)
在控制器方法中添加一個ModelMap類型的形參泞坦。
@RequestMapping("/")
public String testModelMap(ModelMap modelMap) {
modelMap.addAttribute("name", "張三");
return "index";
}
方式3. 使用ModelAndView向request域?qū)ο笾泄蚕頂?shù)據(jù)。
控制器方法支持ModelAndView砖顷、ModelMap贰锁、View、String多種類型的返回值滤蝠,最終都會被SpringMVC封裝成一個ModelAndView對象豌熄。ModelAndView對象由model(模型數(shù)據(jù):需要渲染到視圖的數(shù)據(jù))和 view(視圖:通常為邏輯視圖名)組成。
ModelAndView的常用方法:
1. 添加Model模型數(shù)據(jù)
ModelAndView addObject(String attributeName, @Nullable Object attributeValue)
ModelAndView addObject(Object attributeValue)
ModelAndView addAllObjects(@Nullable Map<String, ?> modelMap)
2. 設置視圖
void setViewName(@Nullable String viewName)
void setView(@Nullable View view)
例
在控制器方法中創(chuàng)建ModelAndView對象并返回物咳。
@RequestMapping("/")
public ModelAndView testModelAndView() {
ModelAndView mav = new ModelAndView();
// 設置Model(向請求域共享數(shù)據(jù))
mav.addObject("name", "hello,張三");
// 設置視圖(實現(xiàn)頁面跳轉(zhuǎn))
mav.setViewName("index");
return mav;
}
4. 視圖和視圖解析器
SpringMVC的控制器方法支持ModelAndView锣险、ModelMap、View、String多種類型的返回值囱持,最終都會被封裝成一個ModelAndView對象(由model模型數(shù)據(jù)和view視圖組成)夯接。View是邏輯視圖名,需要通過視圖解析器解析為真正的視圖纷妆,并將model模型數(shù)據(jù)填入視圖中盔几。
SpringMVC的核心理念是將View視圖與Model模型進行解耦,其工作重心聚焦在Model模型數(shù)據(jù)上掩幢,并不關心最終究竟采用何種視圖技術(Thymeleaf逊拍、JSP、FreeMarker际邻、Velocity芯丧、Excel等,根據(jù)需求自由選擇)對模型數(shù)據(jù)進行渲染世曾。
視圖
用于渲染頁面:將Model模型數(shù)據(jù)填入頁面中缨恒,最終生成HTML、JSP轮听、Excel表格骗露、Word文檔、PDF文檔血巍、JSON數(shù)據(jù)等形式的文件萧锉,并展示給用戶。
1. SpringMVC提供了一個View視圖接口述寡,接口中定義了以下2個方法:
1. default String getContentType();
獲取HTTP響應文件的類型(如:HTML柿隙、JSON、PDF等)鲫凶。
2. void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception;(視圖的核心方法)
負責將Model模型數(shù)據(jù)渲染到視圖中禀崖。
參數(shù)說明:model(模型數(shù)據(jù))、request(請求對象)螟炫、response(響應對象)波附。
2. 常用的View接口實現(xiàn)類(視圖類)
1. ThymeleafView(Thymeleaf視圖)
當項目中使用的視圖技術為Thymeleaf時,則需要使用該視圖類不恭。
2. InternalResourceView(轉(zhuǎn)發(fā)視圖)
實現(xiàn)請求的轉(zhuǎn)發(fā)跳轉(zhuǎn)叶雹。
3. RedirectView(重定向視圖)
實現(xiàn)請求的重定向跳轉(zhuǎn)财饥。
4. FreeMarkerView(FreeMarker視圖)
5. MappingJackson2JsonView(JSON視圖)
6. AbstractPdfView(PDF視圖)
3. 視圖分類
1. 邏輯視圖
最大的特點:控制器方法返回的ModelAndView中的view不是真正的視圖對象换吧,而是一個字符串類型的邏輯視圖名,需要通過一個ViewResolver視圖解析器解析為真正的物理視圖對象钥星。
方式1
直接在控制器方法中返回字符串類型的邏輯視圖名沾瓦,然后通過Model、Map、ModelMap等對象攜帶Model模型數(shù)據(jù)到視圖中贯莺。
例
@RequestMapping("/testView")
public String testView(Model model) {
model.addAttribute("product","模型數(shù)據(jù)")
return "success";
}
方式2
在控制器方法中通過ModelAndView提供的setViewName()方法設置邏輯視圖名风喇,然后通過ModelAndView的addObject()方法攜帶Model模型數(shù)據(jù)到視圖中。
例
@RequestMapping("/testView")
public ModelAndView testView() {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("productList");
List<Product> productList = productService.getProductList();
modelAndView.addObject(productList);
return modelAndView;
}
注:對于Thymeleaf缕探、JSP等類型的邏輯視圖魂莫,其控制器方法返回的view并不是必須為字符串類型的邏輯視圖名,也可以是一個真正的View視圖對象(通過ModelAndView提供的方法構造)爹耗,此時這個視圖也不需要視圖解析器的解析 而是直接渲染耙考。
2. 非邏輯視圖
控制方法返回的是一個真正的視圖對象(不需要視圖解析器解析,可直接渲染)潭兽,而不是邏輯視圖名倦始。
例(MappingJackson2JsonView將數(shù)據(jù)模型轉(zhuǎn)換為JSON視圖并展現(xiàn)給用戶)
@RequestMapping("/testJsonView")
public ModelAndView testJsonView(Integer productId) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("url", "www.baidu.com");
// 設置ModelAndView的View對象
modelAndView.setView(new MappingJackson2JsonView());
return modelAndView;
}
4. 視圖控制器標簽
如果控制器方法中只是簡單返回一個邏輯視圖名,而沒有返回Model數(shù)據(jù)山卦,則可使用<mvc:view-controller>視圖控制器標簽代替該方法鞋邑。
注意:在springMVC.xml中添加了視圖控制器標簽后,必須添加<mvc:annotation-driven />標簽開啟注解驅(qū)動账蓉,否則其他控制器方法的請求映射將全部失效枚碗。
例:
@RequestMapping("/addPage")
public String addPage() {
return "base/add";
}
等價于
在springMVC.xml中使用<mvc:view-controller path="/addPage" view-name="base/add"></mvc:view-controller>
視圖解析器(ViewResolver)
提供了邏輯視圖名與實際視圖的映射關系,并負責將邏輯視圖名解析為一個具體的視圖對象剔猿。
1. SpringMVC提供了ViewResolver視圖解析器接口(視圖解析器必須實現(xiàn)該接口)视译。
public interface ViewResolver {
@Nullable
View resolveViewName(String viewName, Locale locale) throws Exception;
}
2. ViewResolver接口的常用實現(xiàn)類(視圖解析器)每一個解析器都對應一個視圖技術
1. BeanNameViewResolver
將邏輯視圖名解析為 一個Bean(視圖的名稱就是Bean的id)。
2. InternalResourceViewResolver
將邏輯視圖名解析為 一個資源文件归敬。
如:"success.jsp" 會被映射為success.jsp文件酷含。
3. FreeMarkerViewResolver
將邏輯視圖名解析為 一個FreeMarker模板文件。
4. ThymeleafViewResolver
將邏輯視圖名解析為 一個Thymeleaf模板文件汪茧。
3. 使用某個視圖解析器時
只需在SpringMVC.xml配置文件中椅亚,將它以Bean組件的形式注入到Ioc容器中。否則SpringMVC會使用默認的InternalResourceViewResolver進行解析舱污。
4. 配置多個視圖解析器
對于不同類型的視圖對象玩郊,需要在springMVC.xml中配置不同的視圖解析器(根據(jù)order屬性來指定解析優(yōu)先級順序,order越小優(yōu)先級越高)來完成視圖的實例化工作挽鞠。
SpringMVC會遍歷所有視圖解析器闹究,并按照其優(yōu)先級依次對邏輯視圖名進行解析,直到解析成功并返回視圖對象為止珠插。
示例(同時配置Thymeleaf惧磺、JSP視圖解析器):
<?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/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 開啟組件掃描 -->
<context:component-scan base-package="com.sst.cx"></context:component-scan>
<!-- 配置 Thymeleaf 視圖解析器 -->
<bean id="viewResolver"
class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
<!-- 邏輯視圖名的規(guī)則,以 th捻撑、base/ 開頭的邏輯視圖名才會被該解析器解析 -->
<property name="viewNames" value="th*,base/*"/>
<!-- 視圖解析器的優(yōu)先級磨隘,值越小缤底,優(yōu)先級越高 -->
<property name="order" value="2"/>
<!-- 定義視圖文件的字符集 -->
<property name="characterEncoding" value="UTF-8"/>
<property name="templateEngine">
<bean class="org.thymeleaf.spring5.SpringTemplateEngine">
<property name="templateResolver">
<bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
<!-- 設置視圖前綴 -->
<property name="prefix" value="/WEB-INF/templates/"/>
<!-- 設置視圖后綴 -->
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML5"/>
<property name="characterEncoding" value="UTF-8"/>
</bean>
</property>
</bean>
</property>
</bean>
<!-- 配置 JSP 視圖解析器 -->
<bean id="viewResolver1" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 邏輯視圖名的規(guī)則 -->
<property name="viewNames" value="jsp/*"/>
<!-- 視圖解析器的優(yōu)先級,值越小番捂,優(yōu)先級越高 -->
<property name="order" value="1"/>
<property name="viewClass"
value="org.springframework.web.servlet.view.InternalResourceView"/>
<!-- 視圖前綴 -->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!-- 視圖后綴 -->
<property name="suffix" value=".jsp"/>
</bean>
</beans>
靜態(tài)資源映射
在SpringMVC.xml配置文件中个唧,實現(xiàn)靜態(tài)資源的映射(訪問靜態(tài)資源)。
方式1
<!--
靜態(tài)資源映射(用于配置靜態(tài)資源的訪問路徑)
location屬性:本地靜態(tài)資源文件所在目錄设预。/js/**表示js目錄及js子目錄徙歼。
mapping屬性:訪問靜態(tài)資源的路徑前綴。
-->
<mvc:resources mapping="/js/" location="/js/**"></mvc:resources>
方式2
<!--
將靜態(tài)資源交給Tomcat提供的默認Servlet進行處理鳖枕。
使用此標簽時鲁沥,必須設置<mvc:annotation-driven/>,否則只能訪問靜態(tài)資源耕魄,無法再訪問其他請求画恰。
-->
<mvc:default-servlet-handler/>
請求轉(zhuǎn)發(fā)與重定向
請求轉(zhuǎn)發(fā)
在控制器方法中返回邏輯視圖名時,使用"forward:"關鍵字進行請求轉(zhuǎn)發(fā)操作(不會被SpringMVC配置的視圖解析器解析吸奴,而是會將前綴“forward:”去掉允扇,以剩余部分作為最終路徑,經(jīng)過相應的控制器方法處理后则奥,交由視圖解析器解析為最終顯示的頁面 展示到客戶端)考润。
方式1. 在控制器方法中返回: "forward:"關鍵字作為前綴的字符串。
@RequestMapping("/testDispatcher")
public String testDispatcher() {
return "forward:/login";
}
方式2. 在控制器方法中返回:ModelAndView(邏輯視圖名為: "forward:"關鍵字作為前綴的字符串)读处。
@RequestMapping("/testDispatcher")
public ModelAndView testDispatcher() {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("forward:/login"); // 設置邏輯視圖名
return modelAndView;
}
重定向
在控制器方法中返回邏輯視圖名時糊治,使用"redirect:"關鍵字進行重定向操作。(不會被SpringMVC配置的視圖解析器解析罚舱,而是會將前綴“redirect:”去掉井辜,以剩余部分作為最終路徑從客戶端重新請求,經(jīng)過相應的控制器方法處理后管闷,交由視圖解析器解析為最終顯示的頁面 展示到客戶端)粥脚。
方式1. 在控制器方法中返回: "redirect:"關鍵字作為前綴的字符串。
@RequestMapping("/testDispatcher")
public String testDispatcher() {
return "redirect:/login";
}
方式2. 在控制器方法中返回:ModelAndView(邏輯視圖名為: "redirect:"關鍵字作為前綴的字符串)包个。
@RequestMapping("/testRedirect")
public ModelAndView testDispatcher() {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("redirect:/login"); // 設置邏輯視圖名
return modelAndView;
}
5. RESTful(REST風格)
REST(Resource Representational State Transfer)表現(xiàn)層資源狀態(tài)轉(zhuǎn)移
描述了服務器與客戶端進行交互時資源的狀態(tài)轉(zhuǎn)換刷允。REST本身并不是一個實用的概念,真正實用的是如何設計RESTFul接口(即:通過什么樣的手段讓資源在服務器端發(fā)生狀態(tài)轉(zhuǎn)移)碧囊。
1. Resource(資源)
把Web項目部署到Tomcat服務器后树灶,項目中的所有內(nèi)容(類、html文件糯而、css文件天通、js文件、數(shù)據(jù)庫表歧蒋、文本土砂、圖片、音頻等)在都可以被稱為這個服務器中的資源谜洽。而服務器則可以看作是由許許多多離散的資源組成的萝映。
這些資源都可以通過一個URI(統(tǒng)一資源標識符) 進行標識,任何對于該資源的操作都不能改變其URI阐虚。想要獲取這個資源序臂,只需訪問它的URI即可。
2. Representation(資源的表述)
資源在某個特定時刻的狀態(tài)的描述(資源的具體表現(xiàn)形式)实束,可以有多種格式(如:html奥秆、xml、json咸灿、純文本构订、圖片、視頻避矢、音頻等)悼瘾。
通常情況下,服務端與客戶端資源的表述所有使用的格式往往是不同的(如:在服務端資源可能是數(shù)據(jù)庫中的一段純文本审胸、一個xml文件亥宿、數(shù)據(jù)庫中的一張表,而客戶端則可能是表現(xiàn)為html頁面砂沛、json烫扼、音頻、視頻)碍庵。
3. State Transfer(資源狀態(tài)轉(zhuǎn)換)
客戶端與服務端進行交互時映企,資源從一種表現(xiàn)形式轉(zhuǎn)換到另一種表現(xiàn)形式的過程。但是 HTTP協(xié)議是一種無狀態(tài)協(xié)議(無法保存任何狀態(tài))静浴,因此如果客戶端想要獲取服務器上的某個資源卑吭,就必須通過某種手段讓資源在服務器端發(fā)生“狀態(tài)轉(zhuǎn)化”,而這種狀態(tài)轉(zhuǎn)化又是建立在應用的UI表現(xiàn)層上的马绝。這就是【表現(xiàn)層資源狀態(tài)轉(zhuǎn)移】的含義豆赏。
RESTful(REST風格)
在傳統(tǒng)的項目開發(fā)中,通常會將 操作資源的動詞(由開發(fā)員定義富稻、沒有統(tǒng)一規(guī)范) 寫進URL中掷邦。例(通過用戶ID獲取用戶信息的請求,不同的開發(fā)員定義的URL不同):
1. http://localhost:8080/helloSpringMVC/getUserById?id=1
2. http://localhost:8080/helloSpringMVC/user/getById?id=1
3. http://localhost:8080/helloSpringMVC/getUserInfo?id=1
4. http://localhost:8080/helloSpringMVC/a/b?id=1
RESTFul提倡使用統(tǒng)一的風格來設計URL椭赋,規(guī)則如下:
RESTFul是當前比較流行的互聯(lián)網(wǎng)軟件架構模式抚岗,利用HTTP協(xié)議的特性規(guī)定了一套統(tǒng)一的資源獲取方式,以實現(xiàn)客戶端與服務端的數(shù)據(jù)訪問與交互哪怔。
使用RESTful的Web服務稱為REST服務(RESTful Web Services)宣蔚。
1. URL只用來標識和定位資源向抢,不得包含任何與操作相關的動詞。
例(訪問和用戶相關的資源)
http://localhost:8080/helloSpringMVC/user
2. 當請求中需要攜帶參數(shù)時胚委,RESTFul允許將參數(shù)通過斜杠/拼接到URL中挟鸠,而不是以前那樣使用問號?拼接鍵值對的方式來攜帶參數(shù)。
例
http://localhost:8080/helloSpringMVC/user/1
3. 客戶端通過HTTP協(xié)議提供的四個表示操作資源方式的動詞:GET(獲取資源)亩冬、POST(新建資源)艘希、PUT(更新資源) 和 DELETE(刪除資源),從而實現(xiàn)對服務器端資源狀態(tài)轉(zhuǎn)移硅急。
在SpringMVC項目中覆享,通過@RequestMapping+@PathVariable注解的方式 使用RESTful。
1. 通過@RequestMapping注解 設置路徑中的參數(shù)营袜。
通過占位符{xxx}表示傳遞的參數(shù)撒顿。占位符的位置要與請求URL中參數(shù)的位置保持一致,否則會傳錯參數(shù)荚板。
2. 通過@PathVariable注解 綁定參數(shù)
將占位符{xxx}所表示的參數(shù)綁定到控制器方法的形參核蘸。
3. 通過HiddenHttpMethodFilter對請求進行過濾
瀏覽器默認只支持發(fā)送GET和POST請求。在web.xml中使用SpringMVC提供的HiddenHttpMethodFilter對請求進行過濾(將POST請求轉(zhuǎn)換為PUT或DELETE請求)后則可處理PUT和DELETE請求啸驯。當為POST請求且存在_method參數(shù)值時客扎,會將POST請求轉(zhuǎn)換為_method參數(shù)指定的方法(PUT/DELETE)。
例
1. 在web.xml中罚斗,添加:
<!-- 同時存在時徙鱼,必須先配置CharacterEncodingFilter,再配置HiddenHttpMethodFilter -->
<!-- 處理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>
2. 控制器方法:
// 新增商品
@RequestMapping(value = "/product", method = RequestMethod.POST)
public String addProduct(Product product) {
productDao.addProduct(product);
return "redirect:/products";
}
// 刪除商品
@RequestMapping(value = "/product", method = RequestMethod.DELETE)
public String deleteProduct(String productId) {
productDao.deleteProduct(productId);
return "redirect:/products";
}
// 修改商品
@RequestMapping(value = "/product", method = RequestMethod.PUT)
public String updateProduct(Product product) {
productDao.updateProduct(product);
return "redirect:/products";
}
// 查看/回顯商品信息(action參數(shù)為get時為查看针姿;action參數(shù)為update時為修改頁中回顯數(shù)據(jù))
@RequestMapping("/product/{action}/{productId}")
public String getProductList(@PathVariable("action") String action, @PathVariable("productId") String productId, Model model) {
Product product = productDao.getProductById(productId);
model.addAttribute("product", product);
// 根據(jù)參數(shù)action判斷跳轉(zhuǎn)到商品詳細信息頁還是商品修改頁
if (action.equals("get")) {
return "product_info";
} else {
return "product_update";
}
}
3. html
增
<form th:action="@{/product}" method="post">
...
<input type="submit" value="新增商品">
</form>
刪
<form th:action="@{/product}" method="post">
<input type="hidden" name="_method" value="delete">
...
<input type="submit" value="刪除商品">
</form>
改
<form th:action="@{/product}" method="post">
<input type="hidden" name="_method" value="put">
...
<input type="submit" value="修改商品">
</form>
查
<a th:href="@{|/product/get/${product.getProductId()}|}">查看商品</a>
資源操作 | 傳統(tǒng)方式的URL | RESTFul的URL | HTTP請求方式 |
---|---|---|---|
獲取資源 SELECT | http://localhost:8080/helloSpringMVC/getUserById?id=1 | http://localhost:8080/helloSpringMVC/user/1 | GET |
新增資源 INSERT | http://localhost:8080/helloSpringMVC/saveUser | http://localhost:8080/helloSpringMVC/user | POST |
更新資源 UPDATE | http://localhost:8080/helloSpringMVC/updateUser | http://localhost:8080/helloSpringMVC/user | PUT |
刪除資源 DELETE | http://localhost:8080/helloSpringMVC/deleteUser?id=1 | http://localhost:8080/helloSpringMVC/user/1 | DELETE |
6. Converter類型轉(zhuǎn)換器
通過一些注解袱吆,控制器方法就能得到各種類型的請求參數(shù),這要歸功于SpringMVC的類型轉(zhuǎn)換機制距淫。Spring提供了Converter類型轉(zhuǎn)換器绞绒,在SpringMVC中 它的作用是在控制器方法對請求進行處理前,先將請求中的參數(shù)轉(zhuǎn)為指定的數(shù)據(jù)類型再傳給控制器方法的形參榕暇。
SpringMVC會自動使用內(nèi)置類型轉(zhuǎn)換器對基本類型(int蓬衡、long、float彤枢、double狰晚、boolean、char 等)缴啡、集合/數(shù)組類型 進行轉(zhuǎn)換(請求參數(shù)類型要和接收參數(shù)類型必須相兼容壁晒,否則會報400錯誤)。
內(nèi)置類型轉(zhuǎn)換器---標量轉(zhuǎn)換器
名稱 | 作用 |
---|---|
StringToBooleanConverter | String 轉(zhuǎn) boolean |
ObjectToStringConverter | Object 轉(zhuǎn) String(調(diào)用對象的toString方法進行轉(zhuǎn)換业栅,可覆寫該方法來自定義) |
StringToNumberConverterFactory | String 轉(zhuǎn) 數(shù)字(如:Integer秒咐、Long等) |
NumberToNumberConverterFactory | 數(shù)字子類型(基本類型)轉(zhuǎn) 數(shù)字類型(包裝類型) |
StringToCharacterConverter | String 轉(zhuǎn) Character(取字符串中的第一個字符) |
NumberToCharacterConverter | 數(shù)字子類型 轉(zhuǎn) Character |
CharacterToNumberFactory | Character 轉(zhuǎn) 數(shù)字子類型 |
StringToEnumConverterFactory | String 轉(zhuǎn) 枚舉類型(通過Enum.valueOf方法) |
EnumToStringConverter | 枚舉類型 轉(zhuǎn) String(返回枚舉對象的name值) |
StringToLocaleConverter | String 轉(zhuǎn) java.util.Locale |
PropertiesToStringConverter | java.util.Properties 轉(zhuǎn) String(默認使用ISO-8859-1解碼) |
StringToPropertiesConverter | String 轉(zhuǎn) java.util.Properties(默認使用ISO-8859-1編碼) |
內(nèi)置類型轉(zhuǎn)換器---集合谬晕、數(shù)組相關轉(zhuǎn)換器
名稱 | 作用 |
---|---|
ArrayToCollectionConverter | 任意數(shù)組 轉(zhuǎn) 任意集合(List、Set) |
CollectionToArrayConverter | 任意集合 轉(zhuǎn) 任意數(shù)組 |
ArrayToArrayConverter | 任意數(shù)組 轉(zhuǎn) 任意數(shù)組 |
CollectionToCollectionConverter | 集合之間的類型轉(zhuǎn)換 |
MapToMapConverter | Map之間的類型轉(zhuǎn)換 |
ArrayToStringConverter | 任意數(shù)組 轉(zhuǎn) String |
StringToArrayConverter | 字符串 轉(zhuǎn) 數(shù)組(默認以,分割携取,且去除字符串兩邊的空格) |
ArrayToObjectConverter | 任意數(shù)組 轉(zhuǎn) Object(如果目標類型和源類型兼容攒钳,直接返回源對象;否則返回數(shù)組的第一個元素并進行類型轉(zhuǎn)換) |
ObjectToArrayConverter | Object 轉(zhuǎn) 單元素數(shù)組 |
CollectionToStringConverter | 任意集合(List歹茶、Set) 轉(zhuǎn) String |
StringToCollectionConverter | String 轉(zhuǎn) 集合(默認以,分割,且去除字符串兩邊的空格) |
CollectionToObjectConverter | 任意集合 轉(zhuǎn) 任意Object(如果目標類型和源類型兼容你弦,直接返回源對象惊豺;否則返回集合的第一個元素并進行類型轉(zhuǎn)換) |
ObjectToCollectionConverter | Object 轉(zhuǎn) 單元素集合 |
自定義類型轉(zhuǎn)換器
對于復雜類型的轉(zhuǎn)換(如:String轉(zhuǎn)Date)和自定義格式數(shù)據(jù)的轉(zhuǎn)換,則需要根據(jù)需求開發(fā)自定義類型轉(zhuǎn)換器來進行轉(zhuǎn)換禽作。
步驟:
1. 創(chuàng)建自定義類型轉(zhuǎn)換器類(實現(xiàn)Spring提供的3個轉(zhuǎn)換器接口中的任意一個)尸昧。
1. Converter<S,T>接口
使用了泛型(S表示原類型,T表示目標類型)旷偿。
接口中定義了一個convert()方法烹俗,將原類型對象作為參數(shù)傳入,進行轉(zhuǎn)換之后返回目標類型對象萍程。
2. ConverterFactory<S,R>接口
用于將同系列的多個Converter封裝在一起幢妄。
如果希望將一種類型的對象轉(zhuǎn)換為另一種類型及其子類對象(如:將String轉(zhuǎn)為Number及Number的子類Integer、Double等類型)茫负,則需要一系列的Converter(如:StringToInteger蕉鸳、StringToDouble等)。
3. GenericConverter接口
根據(jù)源類對象及目標類對象的上下文信息進行類型轉(zhuǎn)換忍法。
例(實現(xiàn)Converter<S,T>接口潮尝,將String類型的參數(shù)轉(zhuǎn)換為Date類型):
// 自定義日期轉(zhuǎn)換器
public class MyDateConverter implements Converter<String, Date> {
private String datePatten = "yyyy-MM-dd";
@Override
public Date convert(String source) {
System.out.println("前端頁面?zhèn)鬟f過來的時間為:" + source);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(datePatten);
try {
return simpleDateFormat.parse(source);
} catch (ParseException e) {
throw new IllegalArgumentException("無效的日期格式,請使用正確的日期格式" + datePatten);
}
}
}
2. 配置自定義類型轉(zhuǎn)換器
創(chuàng)建自定義類型轉(zhuǎn)換器類后饿序,需要在SpringMVC.xml配置文件中進行配置才能生效勉失。
例:
<!-- 注冊自定義類型轉(zhuǎn)換器,會覆蓋默認注冊的ConversionService(FormattingConversionServiceFactoryBean類型) -->
<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>
<!-- 配置自定義類型轉(zhuǎn)換器 -->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.sst.cx.converter.MyDateConverter"></bean>
</set>
</property>
</bean>
<mvc:annotation-driven/>(通常都需要在SpringMVC.xml配置文件中添加)說明:
開啟注解驅(qū)動來簡化SpringMVC的相關配置:
1. 自動向SpringMVC中注冊3個Bean:RequestMappingHandlerMapping原探、RequestMappingHandlerAdapter(這2個組件:請求分發(fā)給控制器方法的必須組件乱凿;為讀寫json數(shù)據(jù)提供支持),ExceptionHandlerExceptionResolver(異常處理組件)咽弦。
2. 默認注冊了一個ConversionService(項目啟動時會自動通過FormattingConversionServiceFactoryBean類創(chuàng)建并初始化一個FormattingConversionService類型的實例:ConversionService告匠,給@DateTimeFormat、@NumberFormat注解提供支持)滿足大多數(shù)的類型轉(zhuǎn)換需求离唬。
7. Formatter格式化器
開發(fā)中經(jīng)常會涉及到一些需要進行格式化的數(shù)據(jù)(金額:¥100需要處理為100后专,日期:需要按需求處理為指定格式,如:yyyy-MM-dd格式)输莺,這些數(shù)據(jù)都要經(jīng)過一定的格式化處理才能正常使用戚哎。
雖然Formatter與Converter存在一定的差異(Formatter的源類型必須是String類型裸诽,而Converter的源類型可以是任意數(shù)據(jù)類型),但格式化轉(zhuǎn)換本質(zhì)上還是屬于“類型轉(zhuǎn)換”型凳,因此在SpringMVC中Formatter格式化轉(zhuǎn)換器實際上是委托給Converter機制實現(xiàn)的丈冬。
Spring提供了一個Formatter<T>接口(格式化轉(zhuǎn)換器,將String類型的字符串轉(zhuǎn)換為T任意類型)甘畅,該接口繼承了Printer<T>和Parser<T>接口(分別包含一個print方法和一個parse方法埂蕊,所有的格式化轉(zhuǎn)換器實現(xiàn)類都必須重寫這兩個方法)。
public interface Formatter<T> extends Printer<T>, Parser<T> {}
1. Printer<T>接口中定義了print方法
String print(T object, Locale locale);
將對象轉(zhuǎn)為String類型的(按照一定的格式)字符串并返回疏唾。
該方法中包含一個Locale類型的形參蓄氧,可根據(jù)國家/語言進行定制。
2. Parser<T>接口中定義了parse方法
T parse(String text, Locale locale) throws ParseException;
將滿足一定格式的字符串轉(zhuǎn)為對象槐脏。
該方法中包含一個Locale類型的形參喉童,可根據(jù)國家/語言進行定制。
內(nèi)置格式化轉(zhuǎn)換器(用來格式化 日期Date類型顿天、數(shù)值Number類型的數(shù)據(jù))【存疑】
1. NumberFormatter
實現(xiàn)Number與String之間的解析與格式化堂氯。
2. CurrencyFormatter
實現(xiàn)Number與String之間的解析與格式化(帶貨幣符號)。
3. PercentFormatter
實現(xiàn)Number與String之間的解析與格式化(帶百分數(shù)符號)牌废。
4. DateFormatter
實現(xiàn)Date與String之間的解析與格式化咽白。
1. SpringMVC提供了一個FormattingConversionService類(ConversionService類型轉(zhuǎn)換器接口的實現(xiàn)類),它與其他的類型轉(zhuǎn)換器實現(xiàn)類不同鸟缕,它不僅具有類型轉(zhuǎn)換功能局扶,還具有格式化轉(zhuǎn)換功能。SpringMVC為FormattingConversionService類提供了一個FormattingConversionServiceFactroyBean工廠類(用于在Spring上下文中構造FormattingConversionService的實例叁扫,并且為@DateTimeFormat注解三妈、@NumberFormat注解提供了支持)。
2. 在SpringMVC.xml配置文件中添加了<mvc:annotation-driven/>標簽(項目啟動時會自動通過FormattingConversionServiceFactoryBean類創(chuàng)建并初始化一個FormattingConversionService類型的實例:ConversionService)莫绣,才能使用@DateTimeFormat注解畴蒲、@NumberFormat注解 對 Date類型、Number類型的數(shù)據(jù)進行格式化轉(zhuǎn)換对室。
1. @DateTimeFormat注解(日期格式化)
用于標注java.util.Date模燥、java.util.Calendar、java.long.Long等時間類型的數(shù)據(jù)掩宜。
常用屬性
1. pattern屬性(String類型)
用于指定解析或格式化日期時間的模式(常用取值: yyyy-MM-dd蔫骂、yyyy-MM-dd hh:mm:ss)。
2. iso屬性(DateTimeFormat.ISO類型)
用于指定解析或格式化日期時間的ISO模式牺汤,其取值有4種:
DATE:yyyy-MM-dd
TIME:hh:mm:ss:SSSZ
DATE_TIME:yyyy-MM-dd hh:mm:ss:SSSZ
NONE:無
3. style屬性(String類型)
用于指定日期時間的格式(默認值為SS)辽旋。
該屬性由兩個字符組成,分別表示日期和時間的格式。
S:短日期/時間格式
M:中日期/時間格式
L:長日期/時間格式
F:完整日期/時間格式
例:
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date date;
2. @NumberFormat注解(數(shù)值格式化)
用于標注數(shù)字基本類型(如:int补胚、long等)码耐、java.lang.Number類型的實例(如:BigDecimal、Integer等)溶其。
擁有兩個互斥的屬性:
1. style屬性(NumberFormat.Style類型)
用于指定數(shù)值的樣式類型骚腥,其取值有以下4種:
DEFAULT:默認類型
NUMBER:正常數(shù)值類型
PERCENT:百分數(shù)類型
CURRENCY:貨幣數(shù)值類型
2. pattern屬性(String類型)
用于自定義數(shù)值的樣式,如: #,###
例:
@NumberFormat(style = NumberFormat.Style.CURRENCY)
private BigDecimal money;
8. json數(shù)據(jù)轉(zhuǎn)換(Java對象<--轉(zhuǎn)換-->json數(shù)據(jù))
1. json(全稱:JavaScript Object Notation)簡介
與xml相似瓶逃,也是用來存儲數(shù)據(jù)的(基于純文本的數(shù)據(jù)格式)束铭。但相比于xml,數(shù)據(jù)占用空間更小厢绝、解析速度更快契沫。因此,使用json數(shù)據(jù)來進行前后臺的數(shù)據(jù)交互代芜。
不僅能夠傳遞String埠褪、Number浓利、Boolean等簡單類型的數(shù)據(jù)挤庇,還可以傳遞數(shù)組、Object對象等復雜類型的數(shù)據(jù)贷掖。
支持2種數(shù)據(jù)結構(可相互嵌套):
1. 對象結構
以“{”開始嫡秕,以“}”結束,中間則由0個或多個以英文的逗號,分隔的key:value對構成苹威。
key必須為String類型昆咽,value可以是String、Number牙甫、Object掷酗、Array等數(shù)據(jù)類型。
語法格式:
{
key1:value1,
key2:value2,
...
}
例:
一個person對象包含姓名窟哺、密碼泻轰、年齡等信息,使用json的表示形式如下:
{
"pname":"張三",
"password":"123456",
"page":12
}
2. 數(shù)組結構且轨。
以“[”開始浮声、以“]”結束,中間部分由0個或多個以英文的逗號,分隔的值列表組成旋奢。
語法格式:
[
value1,
value2,
...
]
例:
一個數(shù)組中包含了String泳挥、Number、Boolean至朗、null多種類型的數(shù)據(jù)屉符,使用json的表示形式如下:
[
"張三",
123456789,
true,
null
]
2. json數(shù)據(jù)轉(zhuǎn)換(Java對象和json數(shù)據(jù)的相互轉(zhuǎn)換)
SpringMVC提供了一個默認的MappingJackson2HttpMessageConverter類,來處理瀏覽器與控制器類之間的json數(shù)據(jù)轉(zhuǎn)換。
使用步驟:
1. 導入Jackson依賴包
1. jackson-annotations-x.x.x.jar(json轉(zhuǎn)換的注解包)
2. jackson-core-x.x.x.jar(json轉(zhuǎn)換的核心包)
3. jackson-databind-x.x.x.jar(json轉(zhuǎn)換的數(shù)據(jù)綁定包)
2. springMVC.xml配置文件中筑煮,需要添加<mvc:annotation-driven/>開啟注解驅(qū)動(會自動注冊RequestMappingHandlerMapping辛蚊、RequestMappingHandlerAdapter兩個組件,為讀寫json數(shù)據(jù)提供支持)真仲。
3. json轉(zhuǎn)換注解
1. @RequestBody注解
標注控制器方法的形參袋马。
用于將請求體中的數(shù)據(jù)綁定到控制器方法的形參上(json轉(zhuǎn)對象)。
2. @ResponseBody注解
標注控制器方法秸应。
用于將控制器方法的返回值虑凛,直接作為響應報文的響應體響應到瀏覽器上(對象轉(zhuǎn)json)。
例:
@ResponseBody
@RequestMapping(value = "/product", method = RequestMethod.POST)
public Product addProduct(@RequestBody Product product) {
productDao.addProduct(product);
return product;
}
@ResponseBody
@RequestMapping("/getProductList1")
public List<Product> getProductList1() {
List productList = productDao.getProductList();
return productList;
}
下載Jackson依賴包软啼,搜索包->選擇版本->點擊bundle按鈕
示例
===》控制器
@Controller
public class ProductController {
@Resource
private ProductDao productDao;
// 查看或回顯商品信息桑谍,get:查看商品信息 update:為修改頁回顯的商品信息
@ResponseBody
@RequestMapping("/product/{productId}")
public Product getProduct(@PathVariable("productId") String productId) {
Product product = productDao.getProductById(productId);
return product;
}
// 新增商品
@ResponseBody
@RequestMapping(value = "/product", method = RequestMethod.POST)
public Product addProduct(@RequestBody Product product) {
productDao.addProduct(product);
return product;
}
// 刪除指定的商品
@RequestMapping(value = "/product", method = RequestMethod.DELETE)
public String deleteProduct(String productId) {
productDao.deleteProduct(productId);
return "redirect:/products";
}
// 獲取商品列表
@ResponseBody
@RequestMapping("/getProductList1")
public List<Product> getProductList1() {
List productList = productDao.getProductList();
return productList;
}
// 修改商品信息
@RequestMapping(value = "/edit-product")
public String updateProduct(Product product) {
productDao.updateProduct(product);
return "redirect:/products";
}
}
===》product_list.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!--引入 jquery-->
<script type="text/javaScript"
src="../../js/jquery-3.6.0.min.js " th:src="@{/js/jquery-3.6.0.min.js}"></script>
</head>
<body>
<table th:border="1" th:cellspacing="0" th:cellpadding="0" style="text-align: center;">
<thead>
<th>商品id</th>
<th>商品名</th>
<th>商品價格</th>
<th>商品庫存</th>
<th>操作</th>
</thead>
<tbody th:id="tb">
</tbody>
</table>
<br>
<!-- 作用:通過超鏈接控制表單的提交,將post請求轉(zhuǎn)換為delete請求 -->
<form id="delete_form" method="post" th:action="@{/product}">
<!-- HiddenHttpMethodFilter要求:必須傳輸_method請求參數(shù)祸挪,并且值為最終的請求方式 -->
<input type="hidden" name="_method" value="delete"/>
<input type="hidden" name="productId" th:id="form-id"/>
</form>
<button id="btn">新增商品</button>
<div id="addWindow">
<label id="x" style="position: absolute;top:2px;left: 95%;font-size: 25px;">x</label>
<h4>新增商品</h4>
<form th:action="@{/product}" method="post">
<table id="table-box" style="margin: auto">
<tr>
<td>商品 ID:</td>
<td><input type="text" id="productId" name="productId" required></td>
</tr>
<tr>
<td>商品名稱:</td>
<td><input type="text" id="productName" name="productName" required></td>
</tr>
<tr>
<td>商品價格:</td>
<td><input type="text" id="price" name="price" required></td>
</tr>
<tr>
<td>商品庫存:</td>
<td><input type="text" id="stock" name="stock" required></td>
</tr>
<tr>
<td>商品簡介:</td>
<td><textarea id="introduction" name="introduction" rows="10" cols="30"></textarea><br></td>
</tr>
<tr>
<td colspan="2" align="center"><input id="addPro" type="submit" value="新增商品">
<input type="reset" id="reset" value="重置">
</td>
</tr>
</table>
</form>
</div>
<div id="backGround"></div>
<div id="editWindow">
<label id="ex" style="position: absolute;top:2px;left: 95%;font-size: 25px;">x</label>
<h4>修改商品</h4>
<form th:action="@{/edit-product}" id="edit-form" method="post">
<table id="edit-form-table" style="margin: auto">
<tr>
<td>商品 ID:</td>
<td><input id="e-productId" type="text" name="productId" readonly></td>
</tr>
<tr>
<td>商品名稱:</td>
<td><input id="e-productName" type="text" name="productName" required></td>
</tr>
<tr>
<td>商品價格:</td>
<td><input id="e-price" type="text" name="price" required></td>
</tr>
<tr>
<td>商品庫存:</td>
<td><input id="e-stock" type="text" name="stock" required></td>
</tr>
<tr>
<td>商品簡介:</td>
<td><textarea id="e-introduction" name="introduction" rows="10" cols="30"></textarea>
</td>
</tr>
<tr>
<td colspan="2" align="center"><input type="submit" value="修改商品信息"></td>
</tr>
</table>
</form>
</div>
<div id="edit-backGround"></div>
<div id="lookWindow">
<label id="lx" style="position: absolute;top:2px;left: 95%;font-size: 25px;">x</label>
<h4>商品詳情</h4>
<table style="margin: auto">
<tr>
<td>商品 ID:</td>
<td id="l-productId"></td>
</tr>
<tr>
<td>商品名稱:</td>
<td id="l-productName"></td>
</tr>
<tr>
<td>商品價格:</td>
<td id="l-price"></td>
</tr>
<tr>
<td>商品庫存:</td>
<td id="l-stock"></td>
</tr>
<tr>
<td>商品簡介:</td>
<td id="l-introduction"></td>
</tr>
</table>
</div>
<div id="look-backGround"></div>
<script type="text/javaScript">
$(document).ready(function () {
// 點擊新增商品锣披,彈出新增商品彈窗
$("#btn").click(function () {
$("#addWindow").slideDown(300);
$("#backGround").show();
});
$("#x").click(function () {
$("#addWindow").slideUp(300);
$("#backGround").hide();
$("#reset").trigger("click")
});
$("#ex").click(function () {
$("#editWindow").slideUp(300);
$("#edit-backGround").hide();
});
$("#lx").click(function () {
$("#lookWindow").slideUp(300);
$("#look-backGround").hide();
});
$("#look-close").click(function () {
$("#lookWindow").slideUp(300);
$("#look-backGround").hide();
});
/**
* 填完數(shù)據(jù)后,點擊新增商品彈窗中的 新增商品 按鈕贿条,執(zhí)行新增商品操作
*/
$("#addPro").click(function () {
add();
$("#addWindow").slideUp(300);
$("#backGround").hide();
$("#reset").trigger("click")
});
$("#editPro").click(function () {
$("#edit-form").submit();
$("#editWindow").slideUp(300);
$("#edit-backGround").hide();
$("#e-reset").trigger("click")
});
});
$(function () {
$.ajax({
// 請求路徑
url: "http://localhost:8080/helloSpringMVC/getProductList1",
// 請求類型
type: "get",
// 定義發(fā)送請求的數(shù)據(jù)格式為JSON字符串
contentType: "application/json;charset=utf-8",
// 定義回調(diào)響應的數(shù)據(jù)格式為JSON字符串雹仿,該屬性可以省略
dataType: "json",
// 成功響應的結果
success: function (data) {
if (data != null) {
var html = "";
for (var i = 0; i < data.length; i++) {
var product = data[i];
html += "<tr>" +
"<td>" + product.productId + "</td>" +
"<td>" + product.productName + "</td>" +
"<td>" + product.price + "</td>" +
"<td>" + product.stock + "</td>" +
"<td><button onclick='lookPage(" + product.productId + ")'>查看商品</button>" +
"<button onclick='editPage(" + product.productId + ")';>修改商品</button>" +
"<button onclick='deletePro(" + product.productId + ")';>刪除商品</button></td>" +
"</tr>"
}
$("#tb").html(html);
}
}
});
})
function deletePro(productId) {
var b = confirm("確認刪除id 為" + productId + " 的商品?");
if (b) {
var delete_form = $("#delete_form");
$("#form-id").val(productId);
delete_form.submit();
}
}
// 執(zhí)行新增商品操作
function add() {
var productId = $("#productId").val();
var productName = $("#productName").val();
var price = $("#price").val();
var introduction = $("#introduction").val();
var stock = $("#stock").val();
$.ajax({
// 請求路徑
url: "http://localhost:8080/springmvc-json-demo/product",
// 請求類型
type: "post",
data: JSON.stringify({
productId: productId,
productName: productName,
price: price,
stock: stock,
introduction: introduction
}), // 定義發(fā)送請求的數(shù)據(jù)格式為JSON字符串
contentType: "application/json;charset=utf-8",
// 定義回調(diào)響應的數(shù)據(jù)格式為JSON字符串整以,該屬性可以省略
dataType: "json",
// 成功響應的結果
success: function (data) {
if (data != null) {
var product = data;
var html = "<tr>" +
"<td>" + product.productId + "</td>" +
"<td>" + product.productName + "</td>" +
"<td>" + product.price + "</td>" +
"<td>" + product.stock + "</td>" +
"<td><button onclick='lookPage(" + product.productId + ")'>查看商品</a>" +
"<button onclick='editPage(" + product.productId + ")';>修改商品</a>" +
"<button onclick='deletePro(" + product.productId + ")';>刪除商品</a></td>" +
"</tr>";
$("#tb").append(html);
}
}
});
}
function lookPage(productId) {
$("#lookWindow").slideDown(300);
$("#look-backGround").show();
$.ajax({
// 請求路徑
url: "http://localhost:8080/springmvc-json-demo/product/" + productId,
// 請求類型
type: "get",
// 定義發(fā)送請求的數(shù)據(jù)格式為JSON字符串
contentType: "application/json;charset=utf-8",
// 定義回調(diào)響應的數(shù)據(jù)格式為JSON字符串胧辽,該屬性可以省略
dataType: "json",
// 成功響應的結果
success: function (data) {
if (data != null) {
$("#l-productId").html(data.productId);
$("#l-productName").html(data.productName);
$("#l-price").html(data.price);
$("#l-stock").html(data.stock);
$("#l-introduction").html(data.introduction);
}
}
});
}
// 打開商品修改頁,并將商品信息回顯到彈窗中
function editPage(productId) {
$("#editWindow").slideDown(300);
$("#edit-backGround").show();
$.ajax({
// 請求路徑
url: "http://localhost:8080/springmvc-json-demo/product/" + productId,
// 請求類型
type: "get",
// 定義發(fā)送請求的數(shù)據(jù)格式為JSON字符串
contentType: "application/json;charset=utf-8",
// 定義回調(diào)響應的數(shù)據(jù)格式為JSON字符串公黑,該屬性可以省略
dataType: "json",
// 成功響應的結果
success: function (data) {
if (data != null) {
var product = data;
$("#e-productId").val(data.productId);
$("#e-productName").val(data.productName);
$("#e-price").val(data.price);
$("#e-stock").val(data.stock);
$("#e-introduction").val(data.introduction);
}
}
});
}
</script>
<style type="text/css">
#addWindow, #editWindow {
display: none;
position: absolute;
top: 25%;
left: 25%;
width: 30%;
height: 40%;
padding: 20px;
border: 3px solid #ccc;
background-color: white;
z-index: 2;
overflow: auto;
}
#lookWindow {
display: none;
position: absolute;
top: 25%;
left: 25%;
width: 30%;
height: 30%;
padding: 20px;
border: 3px solid #ccc;
background-color: white;
z-index: 2;
overflow: auto;
}
#backGround, #edit-backGround, #look-backGround {
display: none;
position: absolute;
top: 0%;
left: 0%;
width: 100%;
height: 1100px;
background-color: black;
z-index: 1;
-moz-opacity: 0.8;
opacity: .80;
filter: alpha(opacity=88);
}
#x:hover, #lx:hover, #ex:hover {
cursor: pointer;
color: rgb(55, 198, 192);
}
</style>
</body>
</html>