SpringMVC
視圖解析
請求處理方法(controller方法)執行完成後,最終傳回一個 ModelAndView 對象,即使出現異常也會傳回一個 ModelAndView 對象。對于那些傳回 String,View 或 ModeMap 等類型的處理方法,Spring MVC 也會在内部将它們裝配成一個 ModelAndView 對象,它包含了邏輯名和模型對象的視圖,由視圖解析器解析視圖,然後,進行頁面的跳轉。
Spring MVC 借助視圖解析器(ViewResolver)得到最終的視圖對象(View),最終的視圖可以是 JSP ,也可能是 Excel、JFreeChart等各種表現形式的視圖

視圖View
視圖的作用是渲染模型資料,将模型裡的資料以某種形式呈現給客戶
1. 将資料存放到域中 request.setAttribute(name,value)
2. 轉發|重定向 rd.forward(request,response);
為了實作視圖模型和具體實作技術的解耦,Spring 在 org.springframework.web.servlet 包中定義了一個高度抽象的 View 接口
視圖對象由視圖解析器負責執行個體化。由于視圖是無狀态的,是以他們不會有線程安全的問題
常用的視圖實作類
轉發視圖:InternalResourceView
轉發視圖:JstlView 需導入Jstl的jar包,擴充卡會自動調用此解析器,此解析器繼承了InternalResourceView
若使用 JSTL 的 fmt 标簽則需要在 SpringMVC 的配置檔案中裝配标準資源檔案bean
重定向視圖:RedirectView
一般情況下,控制器方法傳回字元串類型的值會被當成邏輯視圖名處理
如果傳回的字元串中帶 forward: 或 redirect: 字首時,SpringMVC 會對他們進行特殊處理:将 forward: 和 redirect: 當成訓示符,其後的字元串作為 URL 來處理
在傳回值中 return "redirect:/success_redirect.jsp";解析器會自動重定向到此頁面,不需在前面加工程路徑名RedirectView會自動幫我們追加上
注意
轉發:通路 WEB-INF 下路徑時隻允許目前請求為GET或POST請求模式的才能轉發成功,(也就是浏覽器隻支援GET、POST請求的轉發)預設攜帶目前的請求方式發起轉發請求。
通路 Controller 内部路徑時必須加上字首 forward: 同樣攜帶目前的請求方式發起請求,必須有對應的請求方式的路徑
重定向:必須先加上字首 redirect: 通路 WEB-INF 下的路徑不被允許,預設用 GET 方法發起新的請求。
通路 Controller 内部路徑時同樣隻能重定向到GET請求方式的路徑
視圖解析器ViewResolver
SpringMVC 為邏輯視圖名的解析提供了不同的政策,可以在 SpringMVC 上下文中配置一種或多種解析政策,并指定他們之間的先後順序。每一種映射政策對應一個具體的視圖解析器實作類。通過反射原理
視圖解析器的作用比較單一:将邏輯視圖解析為一個具體的視圖對象
url= getPrefix() + viewName + getSuffix()
每個視圖解析器都實作了 Ordered 接口并開放出一個 order 屬性,可以通過 order 屬性指定解析器的優先順序,order 越小優先級越高。
JSP 是最常見的視圖技術,可以使用預設的 InternalResourceViewResolve 作為視圖解析器
BeanNameViewResolver:将邏輯視圖名解析為一個 Bean , Bean 的 id 等于邏輯視圖名
Spring表單标簽
SpringMVC 的表單标簽可以實作将模型資料中的屬性和 HTML 表單元素相綁定,以實作表單資料更便捷編輯和表單值的回顯
引入标簽庫
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
form 标簽
<form:form> 标簽 可以通過 modelAttribute 屬性指定綁定的模型屬性,若沒有指定該屬性,則預設從 request 域對象中讀取 command 屬性的表單 bean,如果該屬性值也不存在,則會發生錯誤。
SpringMVC 提供了多個表單元件标簽用以綁定表單字段的屬性值,如 <form:input/>(同html标簽中 input type="text"),它們的共有屬性:
-
- path:表單字段,對應 html 元素的 name 屬性,支援級聯屬性
- htmlEscape:是否對表單值的 HTML 特殊字元進行轉換,預設值為 true
- cssClass:表單元件對應的 CSS 樣式類名
- cssErrorClass:表單元件的資料存在錯誤時,采取的 CSS 樣式
form:input、form:password、form:hidden、form:textarea:對應 HTML 表單的 text、password、hidden、textarea 标簽
form:radiobutton:單選框元件标簽,當表單 bean 對應的屬性值和 value 值相等時,單選框被選中
form:radiobuttons:單選框組标簽,用于構造多個單選框
-
- items:可以是一個 List、String[] 或 Map
- itemValue:指定 radio 的 value 值。可以是集合中 bean 的一個屬性值
- itemLabel:指定 radio 的 label 值
- delimiter:多個單選框可以通過 delimiter 指定分隔符
form:checkbox:複選框元件。用于構造單個複選框
form:checkboxs:用于構造多個複選框。使用方式同 form:radiobuttons 标簽
form:select:用于構造下拉框元件。使用方式同 form:radiobuttons 标簽
form:option:下拉框選項元件标簽。使用方式同 form:radiobuttons 标簽
form:errors:顯示表單元件或資料校驗所對應的錯誤
-
- <form:errors path= “*” /> :顯示表單所有的錯誤
- <form:errors path= “user*” /> :顯示所有以 user 為字首的屬性對應的錯誤
- <form:errors path= “username” /> :顯示特定表單對象屬性的錯誤
//修改功能需要增加絕對路徑,相對路徑會報錯,路徑不對
<form:form action="${pageContext.request.contextPath }/emp" method="POST" modelAttribute="employee(command)">
LastName:<form:input path="lastName"/><br><br>
Email:<form:input path="email"/><br><br>
Gender:<form:radiobuttons path="gender" items="${genders }"/><br><br>
Dept:
<form:select path="department.id" items="${depts }"
itemLabel="departmentName" itemValue="id">
</form:select>
<br><br>
<input type="submit" value="添加員工資訊">
</form:form>
通過jQuery轉換為DELETE請求
REST風格代碼,要求删除功能,必須以DELETE方式送出。解決方案
1. 準備一個以DELETE方式送出的form表單
2. 通過單擊事件為準備好的form表單action屬性指派。
3. 觸發表單的submit事件
$(function(){
$(".delete").click(function(){
var href = $(this).attr("href");
$("form").attr("action",href).submit();
return false ;
});
});
SpringMVC 處理靜态資源
若将 DispatcherServlet 請求映射配置為 /, 則 Spring MVC 将捕獲 WEB 容器的所有請求, 包括靜态資源的請求, SpringMVC 會将他們當成一個普通請求處理, 因找不到對應處理器将導緻錯誤。進而無法加載靜态資源
解決: 需要在 SpringMVC 的配置檔案中配置 <mvc:default-servlet-handler/> 它将在 SpringMVC 上下文中定義一個 DefaultServletHttpRequestHandler,它會對進入 DispatcherServlet 的請求進行篩查,如果發現是靜态資源的請求,就會由此處理器處理
并且還需要配置<mvc:annotation-driven />,因為隻配置上面一個所有請求又全部隻由預設處理器來處理請求了
底層原理:Tomcat解析Servlet優先級問題
程式員定義的 Servlet優先級大于Tomcat自己的Servlet
URL中default-servlet配置 "*.jsp" 大于 "/" 的配置。
處理JSON和下載下傳
HttpMessageConverter<T>
HttpMessageConverter<T> 是 Spring3.0 新添加的一個接口,負責将請求資訊轉換為一個對象(類型為 T),将對象(類型為 T)輸出為響應資訊
當控制器處理方法使用到 @RequestBody/@ResponseBody 或 HttpEntity<T>/ResponseEntity<T> 時, Spring 首先根據請求頭或響應頭的 Accept 屬性選擇比對的 HttpMessageConverter, 進而根據參數類型或泛型類型的過濾得到比對的 HttpMessageConverter, 若找不到可用的 HttpMessageConverter 将報錯 406
HttpMessageConverter<T> 可将請求資訊轉化并綁定到(controller)處理方法的入參中或将響應結果轉為對應類型的響應資訊
SpringMVC處理 json
1) jackson-annotations-2.1.5.jar
jackson-core-2.1.5.jar
jackson-databind-2.1.5.jar
2)需要在配置檔案設定 <mvc:annotation-driven />
如此可将擴充卡AnnotationMethodHandlerAdapter更新為:RequestMappingHandlerAdapter 而 RequestMappingHandlerAdapter 擴充卡中存在MappingJackson2HttpMessageConverter
3)@RequestBody / @ResponseBody 在處理方法上進行标注
@RequestBody:是将Http請求正文插入到處理方法中,修飾目标方法的入參
@ResponseBody:是将内容或對象作為Http響應正文傳回去
将user對象轉為json對象傳回,注意ajax設定dataType:"json"類型
@ResponseBody
@RequestMapping(value = "/json",method = RequestMethod.GET)
public User json(String string){
return user;
}
異步請求
$("#username1").change(function () {
var username = $("#username1").val();
var dat = {
"username":username,
"_method":"PUT",
};
$.ajax({
url:"${pageContext.request.contextPath}/login",
type:"POST",
data:dat,
dataType:"json",
success:function (data) {
alert(data.name);
},
error:function () {
alert("請求失敗");
}
});
});
HttpEntity<T> 作為處理方法的入參擷取檔案大小請求頭等
ResponseEntity<T> 作為處理方法的傳回值,傳回用戶端要下載下傳的檔案資源,實作檔案下載下傳功能
下載下傳
@RequestMapping(value = "/downLoad",method = RequestMethod.GET)
public ResponseEntity<byte[]> testDownLoad(HttpServletRequest request,HttpServletResponse response) throws IOException {
ResponseEntity<byte[]> responseEntity = null;
//1.擷取到download檔案的真實路徑(推薦先放在項目根目錄下測試_downLoad為檔案名)
String path = request.getServletContext().getRealPath("/downLoad");
//在請求體中擷取檔案名
String fileName = request.getParameter("fileName");
//2.通過真實檔案路徑建立File對象(File.separator為目前系統檔案分隔符)
File file = new File(path + File.separator + fileName);
//3.建立檔案輸入流,将要下載下傳的檔案讀取到程式中
FileInputStream fileInputStream = new FileInputStream(file);
byte[] bytes = new byte[fileInputStream.available()];
//将檔案一次性全部讀取到bytes中
fileInputStream.read(bytes);
//解決 檔案名 中文亂碼問題
String header = request.getHeader("User-Agent");
if(header != null && header.contains("Firefox")) {
fileName = "=?utf-8?B?"+new BASE64Encoder().encode(fileName.getBytes("utf-8"))+"?=";
}else {
fileName = URLEncoder.encode(fileName, "UTF-8");
}
//建立響應頭
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add("Content-Disposition", "attachment; filename="+fileName);
//傳回資源(HttpStatus.OK=200)
responseEntity = new ResponseEntity<>(bytes, httpHeaders, HttpStatus.OK);
fileInputStream.close();
return responseEntity;
}
檔案上傳
Spring MVC 為檔案上傳提供了直接的支援,這種支援是通過即插即用的 MultipartResolver 實作的。實作類:CommonsMultipartResolver
Spring MVC 上下文中預設沒有裝配 MultipartResovler,是以預設情況下不能處理檔案的上傳工作,需在上下文中配置 MultipartResolver
配置 MultipartResolver
defaultEncoding: 必須和使用者 JSP 的 pageEncoding 屬性一緻,以便正确解析表單的内容,為了讓 CommonsMultipartResolver 正确工作,必須先将 Commons-fileupload 及 Commons-io 的 jar 包添加到類路徑下。
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="UTF-8"></property>
<property name="maxUploadSize" value="10240000"></property>
</bean>
MultipartResolver 用于處理檔案上傳,當收到請求時 DispatcherServlet 的 checkMultipart() 方法會調用 MultipartResolver 的 isMultipart() 方法判斷請求中是否包含檔案。如果請求資料中包含檔案,則調用 MultipartResolver 的 resolveMultipart() 方法對請求的資料進行解析,然後将檔案資料解析成 MultipartFile 并封裝在 MultipartHttpServletRequest (繼承了 HttpServletRequest) 對象中,最後傳遞給 Controller
上傳檔案
<form action="${pageContext.request.contextPath}/upLoad" method="post" enctype="multipart/form-data">
需上傳檔案:<input type="file" name="file">
<input type="submit">
</form>
@RequestMapping(value = "/upLoad",method = RequestMethod.POST)
public String upLoad(@RequestParam("file")MultipartFile multipartFile,HttpServletRequest request) throws IOException {
//1.擷取upload的真實路徑
String path = request.getServletContext().getRealPath("/upLoad");
//如果此路徑不存在則建立此目錄
File file1 = new File(path);
if(!file1.exists()){
file1.mkdir();
}
//2.擷取要上傳的檔案名
String filename = multipartFile.getOriginalFilename();
//3.建立上傳file
File file = new File(path + File.separator + filename);
//4.實作檔案上傳
multipartFile.transferTo(file);
return "home";
}
多檔案上傳