天天看點

SpringMVC筆記整理

筆記為個人學習過程中整理,如有錯誤,歡迎指正

第一講 HelloWorld 2

SpringMVC環境配置 2

web.xml檔案内容 2

springmvc.xml内容 3

HelloWorld.java 4

第二講 部分常用注解 5

使用@RequestMapping映射請求 5

@PathVariable映射URL綁定的占位符 7

REST技術 7

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

@RequestParam注解 9

@RequestHeader注解 10

@CookieValue注解 10

使用POJO作為參數 10

使用Servlet API作為入參 11

第三講 SpringMVC處理模型資料 12

ModelAndView 12

Map 13

SessionAttributes注解 13

第四講 ModelAttribute注解 14

源代碼分析的流程 14

第五講 視圖 15

JstlView 15

自定義視圖 16

重定向 17

第六講 RESTful SpringMVC CRUD 17

SpringMVC處理靜态資源 18

資料綁定流程 19

自定義類型轉換器 19

mvc:annotation-driven 21

@InitBinder注解 21

資料格式化 22

第七講 JSR303資料校驗 23

提示消息的國際化 25

處理JSON 25

HttpMessageConverter的使用 26

檔案下載下傳 26

第八講 國際化與檔案上傳 27

如何在Bean裡擷取locale對應的消息 27

如何通過超連結切換Locale 27

檔案上傳 28

第九講 攔截器 29

自定義攔截器 29

攔截器的執行順序 30

第十講 異常處理 32

ExceptionHandler注解 33

ResponseStatusExceptionResolver 34

DefaultHandlerExceptionResolver 35

SimpleMappingExceptionResolver 35

第一講 HelloWorld

  1. 步驟:
  1. 加入jar包

第一個jar包需要單獨去找

commons-logging-1.2.jar

下面的jar都在spring的libs檔案夾下

spring-aop-5.0.0.RELEASE.jar

spring-beans-5.0.0.RELEASE.jar

spring-context-5.0.0.RELEASE.jar

spring-core-5.0.0.RELEASE.jar

spring-expression-5.0.0.RELEASE.jar

spring-web-5.0.0.RELEASE.jar

spring-webmvc-5.0.0.RELEASE.jar

  1. 在web.xml檔案中配置DispatcherServlet
  2. 加入SpringMVC的适配檔案
  3. 編寫處理請求的處理器,并辨別為處理器
  4. 編寫視圖

SpringMVC環境配置

web.xml檔案内容

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns="http://java.sun.com/xml/ns/javaee"

xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"

id="WebApp_ID" version="2.5">

<!-- 配置 DispatcherServlet -->

<!-- The front controller of this Spring Web application, responsible for

handling all application requests -->

<servlet>

<servlet-name>springDispatcherServlet</servlet-name>

<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<!-- 配置 DispatcherServlet 的一個初始化參數:配置 SpringMVC 配置檔案的名稱和位置 -->

<!-- 實際上也可以不通過 contextConfigLocation 來配置SpringMVC的配置檔案,而使用預設的 預設的配置檔案為

/WEB-INF/<servlet-name>-servlet.xml -->

<init-param>

<param-name>contextConfigLocation</param-name>

<param-value>classpath:springmvc.xml</param-value>

</init-param>

<load-on-startup>1</load-on-startup>

</servlet>

<!-- Map all requests to the DispatcherServlet for handling -->

<servlet-mapping>

<servlet-name>springDispatcherServlet</servlet-name>

<!-- 設定應答的請求,此處設定為應答所有請求 -->

<url-pattern>/</url-pattern>

</servlet-mapping>

</web-app>

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/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.2.xsd

http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd">

<!-- 配置自定掃描的包 -->

<context:component-scan base-package="springmvc"></context:component-scan>

<!-- 配置視圖解析器:如何把 handler 方法傳回值解析為實際的實體視圖 -->

<bean

class="org.springframework.web.servlet.view.InternalResourceViewResolver">

<property name="prefix" value="/WEB-INF/views/"></property>

<property name="suffix" value=".jsp"></property>

</bean>

</beans>

配置完成截圖

HelloWorld.java

package springmvc.handlers;

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.RequestMapping;

@Controller

public class HelloWorld {

@RequestMapping("/hello")

public String hello() {

System.out.println("hello world");

return "success";

}

}

第二講 部分常用注解

使用@RequestMapping映射請求

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

在控制器的類定義及方法定義處都可以标注

@RequestMapping

- 類定義處:提供初步的請求映射資訊。相對于WEB應用的根目錄

- 方法處:提供進一步的細分映射資訊。相對于類定義處的URL。若類定義處未标注@RequestMapping,則方法處标記的URL相對于WEB應用的根目錄

DispatcherServlet截獲請求後,就通過控制器上@RequestMapping提供的映射資訊确定請求所對應的處理方法。

例:

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.RequestMapping;

@RequestMapping("/springmvc")

@Controller

public class SpringMVCTest {

private static final String SUCCESS = "success";

@RequestMapping("/testRequestMapping")

public String testRequestMapping() {

System.out.println("testRequestMapping");

return SUCCESS;

}

}

此處就必須通過WEB應用名/springmvc/testRequestMapping才能執行SpringMVCTest方法

@RequestMapping除了可以使用請求URL映射請求外,還可以使用請求方法、請求參數及請求頭映射請求

@RequestMapping的value、method、params及heads分别表示請求URL、請求方法、請求參數及請求頭的映射條件,他們之間是“與”的關系,聯合使用多個條件可以讓請求映射更加精确化

params和headers支援簡單的表達式:

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

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

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

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

例:

@RequestMapping(value="/testMethod", method=RequestMethod.POST)

public String testMethod() {

System.out.println("testMethod");

return SUCCESS;

}

@RequestMapping(value = "testParamsAndHeaders", params = { "username","age!=10" }, headers = { "Accept-Language=zh-CN,zh;q=0.9" })

public String testParamsAndHeaders() {

System.out.println("testParamsAndHeaders");

return SUCCESS;

}

使用@RequestMapping映射時支援通配符,不過通配符需要使用Ant風格資源位址支援3種比對符:

- ?:比對檔案名中的一個字元

- *:比對檔案名中的任意字元

- **:**比對多層路徑

@RequestMapping還支援Ant風格的URL

- /usercreateUser:比對/user/createUser、/user/aaa/bbb/createUser等URL

- /user/createUser??:比對/user/createUseraa、/user/createUserbb等URL

@PathVariable映射URL綁定的占位符

通過@PathVariable可以将URL中占位符參數綁定到控制器處理方法的傳入參數中:URL中的{xxx}占位符可以通過@PathVariable("xxx")綁定到操作方法的傳入參數中

jsp中連結:

<a href="springmvc/testPathVariable/143">Test PathVariable</a>

響應類中:

@RequestMapping("/testPathVariable/{id}")

public String testPathVariable(@PathVariable("id") Integer id) {

System.out.println("testPathVariable:" + id);

return SUCCESS;

}

此時會輸出143

REST技術

REST:即Representational State Transfer。(資源) 表現層狀态轉化。是目前最流行的一種網際網路軟體架構。它結構清晰、符合标準、易于了解、擴充友善。是以正得到越來越多網站的采用

  1. 資源( Resources):網絡上的一個實體,或者說是網絡上的一個具體資訊。它可以是一段文本、一張圖檔、一首歌曲、一種服務,總之就是一個具體的存在。可以用一個URI(統一資源定位符)指向它,每種資源對應一個特定的URI。要擷取這個資源,通路它的URI就可以,是以URI 即為每一個資源的獨一無二的識别符。
  2. 表現層(Representation):把資源具體呈現出來的形式,叫做它的表現層(Representation )。比如,文本可以用txt 格式表現,也可以用HTML 格式XML 格式、JSON 格式表現,甚至可以采用二進制格式。
  3. 狀态轉化(State Transfer):每發出一個請求,就代表了用戶端和伺服器的一次互動過程。HTTP協定,是一個無狀态協定,即所有的狀态都儲存在伺服器端。是以,如果用戶端想要操作伺服器,必須通過某種手段,讓伺服器端發生“狀态轉化”(State Transfer)。而這種轉化是建立在表現層之上的,是以就是“表現層狀态轉化”。具體說,就是HTTP 協定裡面,四個表示操作方式的動詞: GET、POST、PUT、DELETE。它們分别對應四種基本操作:GET 用來擷取資源,POST 用來建立資源,PUT 用來更新資源,DELETE 用來删除資源。

示例:

- /order/1 HTTP GET:得到id = 1的order

- /order/1 HTTP DELETE:删除id = 1的order

- /order/1 HTTP PUT:更新id = 1的order

- /order   HTTP POST:新增order

HiddenHttpMethodFilter:浏覽器form 表單隻支援GET 與POST 請求,而DELETE、PUT等method并不支援,Spring3.0 添加了一個過濾器,可以将這些請求轉換為标準的http方法,使得支援GET、POST、PUT與DELETE 請求。

需要用到REST時的準備工作就是要在web.xml中進行配置:

<!-- 配置org.springframework.web.filter.HiddenHttpMethodFilter:可以把POST請求轉化為DELETE或POST請求 -->

<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>

return SUCCESS;

}

使用Servlet API作為入參

Spring可以使用Servlet原生的API作為目标方法的參數,MVC的Handler可以接受如下Servlet API類型的參數

  1. HttpServletRequest
  2. HttpServletResponse
  3. HttpSession
  4. java.security.Principal
  5. Locale
  6. InputStream
  7. OutputStream
  8. Reader
  9. Writer

@RequestMapping("/testServletAPI")

public void testServletAPI(HttpServletRequest request,

HttpServletResponse response, Writer out) throws IOException{

System.out.println("testServletAPI:" + request + ", " + response);

out.write("hello Springmvc");

}

通路的結果為:

第三講 SpringMVC處理模型資料

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

  1. ModelAndView:處理方法傳回值類型為ModelAndView時,方法體即可通過該對象添加模型資料
  2. Map及Model:入參為org.springframework.ui.Model、org.springframework.ui.ModelMap或java.util.Map時,處理方法傳回時,Map中的資料會自動添加到模型中
  3. @SessionAttributes:将模型中的某個屬性暫存到HttpSession中,以便多個請求之間可以共享這個屬性
  4. @ModelAttribute:方法入參标注該注解後,入參的對象就會放到資料模型中

ModelAndView

控制器處理方法的傳回值如果為ModelAndView,則其既包含視圖資訊,也包含模型資料資訊。

  1. 添加模型資料:

- ModelAndView addObject(String attributeName, Object attributeValue)

- ModelAndView addAllObject(Map<String, ?> modelMap)

  1. 設定視圖:

- void setView(View view)

- void setViewName(String viewName)

SpringMVC會把ModelAndView的model中的資料放入到request域對象中

@RequestMapping("/testModelAndView")

public ModelAndView testModelAndView() {

String viewName=SUCCESS;

ModelAndView modelAndView=new ModelAndView(viewName);

//添加模型資料到ModelAndView中

modelAndView.addObject("time", new Date());

return modelAndView;

}

在将要跳轉到的jsp頁面上這麼寫

time: ${requestScope.time}

則會在網頁上顯示目前時間

Map

SpringMVC在内部使用了一個org.springframework.ui.Model接口存儲模型資料

  1. 具體步驟

- SpringMVC在調用方法前會建立一個隐含的模型對象作為模型資料的存儲容器

- 如果方法的入參為Map或Model類型,SpringMVC會将隐含模型的引用傳遞給這些入參。在方法體内,開發者可以通過這個入參對象通路到模型中的所有資料,也可以向模型中添加新的屬性資料

@RequestMapping("/testMap")

public String testMap(Map<String, Object> map) {

map.put("names", Arrays.asList("Sony", "Jerry", "Angra"));

return SUCCESS;

}

在将要跳轉到的jsp頁面上這麼寫

names:${requestScope.names}

則在網頁上會顯示names:[Sony, Jerry, Angra]

SessionAttributes注解

若希望在多個請求之間共用某個模型屬性資料,則可以在控制器類上标注一個@SessionAttributes,SpringMVC将在模型中對應的屬性暫存到HttpSession中

@SessionAttributes除了可以通過屬性名指定需要放到會話中的屬性外,還可以通過模型屬性的對象類型指定哪些模型屬性需要放到會話中

- @SessionAttributes(types-User.class)會将隐含模型中所有類型為User.class的屬性添加到會話中

- @SessionAttributes(value={"user1", "user2"})

- @SessionAttributes(types={User.class, Dept.class})

- @SessionAttributes(value={"user1", "user2"}, types={Dept.class})

@SessionAttributes(value = { "user" }, types = { String.class })

@RequestMapping("/springmvc")

@Controller

public class SpringMVCTest {

private static final String SUCCESS = "success";

@RequestMapping("/testSessionAttributes")

public String testSessionAttributes(Map<String, Object> map){

User user = new User("Angra", "MyPass123", "[email protected]", 27);

map.put("user", user);

map.put("school", "ownSchool");

return SUCCESS;

}

}

第四講 ModelAttribute注解

有@ModelAttribute标記的方法,會在每個目标方法執行之前被SpringMVC調用

運作流程:

  1. 執行@ModelAttribute注解修飾的方法:從資料庫中取出對象,并把對象放入到Map中,鍵為user
  2. SpringMVC從Map中取出User對象,并且把表單的請求參數賦給該User對象的對應屬性
  3. SpringMVC把上述對象傳入目标方法的參數

注意:在@ModelAttribute修飾的方法中,放入Map時的鍵需要和目标方法入參類型的第一個字母小寫的字元串一緻

源代碼分析的流程

  1. 調用@ModelAttribute注解修飾的方法,實際上把@ModelAttribute方法中Map中的資料放在了implicitModel中
  2. 解析請求處理的目标參數,實際上該目标參數來自于WebDataBinder對象的target屬性
  1. 建立WebDataBinder對象:
  • 确定objectName屬性:若傳入的attrName屬性值為"",則objectName為類名第一個字母小寫
  • 确定target屬性:
  1. 在implicitModel中查找attrName對應的屬性值,若存在,則ok;若不存在,則驗證目前Handler是否使用了@SessionAttributes進行修飾,若使用了,則嘗試從Session中擷取attrName所對應的屬性值。若session中沒有對應的屬性值,則會抛出異常。若Handler沒有使用@SessionAttributes進行修飾,或者@SessionAttributes中沒有使用value值指定的key和attrName相比對,則通過反射建立了POJO對象
  1. SpringMVC把表單的請求參數賦給了WebDataBinder的target 對應的屬性
  2. *SpringMVC會把WebDataBinder的attrName和target給到 implicitModel,進而傳到request域對象中
  3. 把WebDataBinder的target作為參數傳遞給目标方法的入參
  1. SpringMVC确定目标方法POJO類型入參的過程:
  1. 确定一個key
      • 若目标方法的POJO類型的參數沒有使用@ModelAttribute作為修飾,則key為POJO類名第一個字母的小寫
      • 若使用了@ModelAttribute來修飾,則key為@ModelAttribute注解的value屬性值
  2. 在implicitModel中查找key對應的對象,若存在,則作為入參傳入

若在@ModelAttribute标記的方法中Map中儲存過,且key和1确定的key一直,則一定會擷取到

  1. 若implicitModel中不存在key對應的對象,則檢查目前的Handler是否使用@SessionAttributes注解修飾,若使用了該注解,且@SessionAttributes注解的value屬性值中包含了key,則會從HttpSession中來擷取key所對應的value值,若存在則直接傳入到目标方法的入參中,若不存在則将抛出異常
  2. 若Handler沒有辨別@SessionAttributes注解或@SessionAttributes注解的value值中不包含key,則會通過反射來建立POJO類型的參數,傳入為目标方法的參數
  3. SpringMVC會把key和value儲存到implicitModel中,進而會儲存到request中

第五講 視圖

JstlView

若項目中使用了JSTL,則SpringMVC會自動把視圖由Internal

在JSP中使用JSTL标簽需要在檔案頭加入如下語句:

<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

使用國際化語言的标簽:

<fmt:message key="i18n.username"></fmt:message>

配置國際化資源檔案需要在springmvc.xml檔案中寫以下代碼

<bean id="messageSource"

class="org.springframework.context.support.ResourceBundleMessageSource">

<property name="basename" value="i18n"></property>

</bean>

使用mvc:view-controller标簽可以直接響應轉發的頁面,而無需再經過Handler的方法

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

但是如果不進行其他配置其他轉向success的頁面就會404,在開發中還需要配置mvc:annotation-driven,直接寫如下語句就行了

<mvc:annotation-driven></mvc:annotation-driven>

自定義視圖

若希望使用Excel 展示資料清單,僅需要擴充SpringMVC 提供的AbstractExcelView 或AbstractJExcel View 即可。實作buildExcelDocument(

方法,在方法中使用模型資料對象建構Excel 文檔就可以了。

  1. AbstractExcelView 基于POI API,而Abstract ExcelView 是基于JExcelAPI 的。
  2. 視圖對象需要配置IOC 容器中的一個Bean,使用BeanNameViewResolver作為視圖解析器即可
  3. 若希望直接在浏覽器中直接下載下傳Excel 文檔,則可以設定響應頭Content-Disposition 的值為attachment;filename=xxx.xIs
  1. springmvc.xml檔案中的配置:

<!-- 配置視圖BeanNameViewResolver解析器:使用視圖的名字來解析視圖 -->

<bean class="org.springframework.web.servlet.view.BeanNameViewResolver">

<!-- 通過order屬性來定義視圖解析器的優先級,order值越小優先級越高 -->

<property name="order" value="100"></property>

</bean>

  1. handler中内容

@RequestMapping("/testView")

public String testView() {

System.out.println("testView");

return "helloView";

}

  1. view中的内容

@Component

public class HelloView implements View {

@Override

public String getContentType() {

return "text/html";

}

@Override

public void render(Map<String, ?> model, HttpServletRequest request,

HttpServletResponse response) throws Exception {

response.getWriter().print("hello view,time:" + new Date());

}

}

重定向

一般情況下,控制器方法傳回字元串類型的值會被當成邏輯視圖名處理

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

  1. redirect:success.jsp: 會完成一個到success.jsp 的重定向的操作
  2. forward:success.jsp: 會完成一個到success.jsp 的轉發操作

例:

@RequestMapping("/testRedirect")

public String testRedirect() {

System.out.println("testRedirect");

return "redirect:/index.jsp";

}

第六講 RESTful SpringMVC CRUD

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

這是SpringMVC的form标簽,其可以更快速的開發出表單頁面,而且可以更友善的進行表單值的回顯,支援級聯操作

可以通過modelAttribute屬性指定綁定的模型屬性,若沒有指定該屬性,則預設從request域對象中讀取command的表單bean,如果該屬性值也不存在,則會發生錯誤,意思就是SpringMVC認為表單一定會進行回顯,即便你不需要回顯也需要這樣做,可以傳入一個new的對象沒有任何屬性值的,以達到沒有回顯的效果

<form:form action="emp" method="POST" modelAttribute="employee">

<!-- path屬性對應html的form标簽裡的name屬性值 -->

LastName:<form:input path="lastName" />

<br>

Email:<form:input path="email" />

<br>

<%

Map<String, String> genders = new HashMap<String, String>();

genders.put("1", "Male");

genders.put("0", "Female");

request.setAttribute("genders", genders);

%>

Gender:<form:radiobuttons path="gender" items="${genders}" />

<br>

Department:<form:select path="department" items="${departments}" itemLabel="departmentName" itemValue="id"></form:select>

<input type="submit" value="Submit">

</form:form>

  1. form:input、form:password、form:hidden、form:textarea:對應HTML表單的text、password、hidden、textarea 标簽
  2. form:radiobutton:單選框元件标簽,當表單bean 對應的屬性值和value 值相等時,單選框被選中
  3. form:radiobuttons: 單選框組标簽,用于構造多個單選框

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

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

- itemLabel:指定radio 的label 值

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

  1. form:checkbox:複選框元件。用于構造單個複選框
  2. form:checkboxs:用于構造多個複選框。使用方式同form:radiobuttons标簽
  3. form:select:用于構造下拉框元件。使用方式同form:radiobuttons标簽
  4. form:option:下拉框選項元件标簽。使用方式同form:radiobuttons标簽
  5. form:errors:顯示表單元件或資料校驗所對應的錯誤

- <form:errors path= "*" />:顯示表單所有的錯誤

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

- <form:errors path="username" />:顯示特定表單對象屬性的錯誤

SpringMVC處理靜态資源

優雅的REST風格的資源URL不希望帶.html或.do等字尾

若将DispatcherServlet請求映射配置為/,則SpringMVC将捕獲WEB容器的所有請求,包括靜态資源的請求,SpringMVC會将他們當成一個普通請求處理,因找不到對應處理器将導緻錯誤。

可以在SpringMVC的配置檔案中配置<mvc:default-servlet-handler/>的方式解決靜态資源的問題:

  1. <mvc:default-servlet-handler/>将在SpringMVC上下文中定義一個DefaultServletHttpRequestHandler,它會對進入DispatcherServlet的請求進行篩查,如果發現是沒有經過映射的請求,就将該請求交由WEB 應用伺服器預設的Servlet處理,如果不是靜态資源的請求,才由DispatcherServlet繼續處理
  2. 一般WEB 應用伺服器預設的Servlet的名稱都是default。若所使用的WEB伺服器的預設Servlet名稱不是default,則需要通過default-servlet-name屬性顯式指定
  3. 有時還需要寫<mvc:annotation-driven><mvc:annotation-driven/>才能好用

普通的連結是使用GET傳輸的,要使用REST風格必須是POST傳輸,此時我們要将連結改成POST傳輸,這個需要使用JavaScript和jQuery技術實作,具體過程百度

對于_method不能使用form:hidden标簽,因為modelAttribute對應的bean中沒有_method這個屬性。

<form:form action="${pageContext.request.contextPath}/emp" method="POST" modelAttribute="employee"></form:form>

action前的${pageContext.request.contextPath}表示絕對路徑,防止跳轉頁面出錯

資料綁定流程

  1. SpringMVC主架構将ServletRequest對象及目标方法的入參執行個體傳遞給WebDataBinderFactory執行個體,以建立DataBinder執行個體對象
  2. DataBinder調用裝配在SpringMVC上下文中的ConversionService元件進行資料類型轉換、資料格式化工作。将Servlet中的請求資訊填充到入參對象中
  3. 調用Validator元件對已經綁定了請求消息的入參對象進行資料合法性校驗,并最終生成資料綁定結果BindingData對象
  4. SpringMVC抽取BindingResult中的入參對象和校驗錯誤對象,将它們賦給處理方法的響應入參

自定義類型轉換器

Spring定義了3種類型的轉換器接口,實作任意一個轉換器接口都可以作為自定義轉換器注冊到ConversionServiceFactoryBean中:

  1. Converter<S,T>:将S類型對象轉為T類型對象
  2. ConverterFactory:将相同系列多個“同質”Converter 封裝在一起。如果希望将一種類型的對象轉換為另一種類型及其子類的對象(例如将String轉換為Number及Number子類(Integer、Long、Double等)對象)可使用該轉換器工廠類
  3. GenericConverter:會根據源類對象及目标類對象所在的宿主類中的上下文資訊進行類型轉換

在網頁中我們按照lastName-email-gender-department.id的規則輸入一個字元串,以記錄employee的資訊(此處隻是為了試驗便捷,是以用一個文本框輸入全部資訊之後進行操作,如果寫多個選框代表不同資訊操作方式是一樣的)

要儲存employee成為Employee類型

此時是不能直接儲存的,因為網頁和控制器沒有自動轉換,要将String類型轉變為Employee類型,這是轉換器:

轉換器編輯完成後還需要對其進行配置在springmvc.xml中配置

mvc:annotation-driven

<mvc:annotation-driven></mvc:annotation-driven>會自動注冊RequestMappingHandlerMapping、RequestMappingHandlerAdapter與ExceptionHandlerExceptionResolver三個bean。

還将提供以下支援:

  1. 支援使用ConversionService執行個體對表單參數進行類型轉換
  2. 支援使用@NumberFormatannotation、@DateTimeFormat注解完成資料類型的格式化
  3. 支援使用@Valid注解對JavaBean執行個體進行JSR 303驗證
  4. 支援使用@RequestBody和@ResponseBody注解

開發的時候最好加上這個配置

@InitBinder注解

由@InitBinder辨別的方法,可以對WebDataBinder對象進行初始化。WebDataBinder是DataBinder的子類,用于完成由表單字段到JavaBean屬性的綁定

@InitBinder方法不能有傳回值,它必須聲明為void

@InitBinder方法的參數通常是是WebDataBinder

@InitBinder

public void initBinder(WebDataBinder dataBinder){

  dataBinder.setDisallowedFields("roleSet");

}

資料格式化

首先需要配置<mvc:annotation-driven></mvc:annotation-driven>,之後在類型上加對應的注解即可

這樣按照pattern中的格式輸入文本框則會進行正确轉換

FormattingConversionServiceFactoryBean内部已經注冊了:

  1. NumberFormatAnnotationFormatterFactory:支援對數字類型的屬性使用@NumberFormat注解
  2. JodaDateTimeFormatAnnotationFormatterFactory:支援對日期類型的屬性使用@DateTimeFormat 注解

裝配了FormattingConversionServiceFactoryBean後,就可以在SpringMVC入參綁定及模型資料輸出時使用注解驅動了。<mvc:annotation-driven></mvc:annotation-driven>預設建立的ConversionService執行個體即為FormattingConversionServiceFactoryBean

<mvc:default-servlet-handler />

<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>

<!-- 配置ConversionService -->

<bean id="conversionService"

class="org.springframework.format.support.FormattingConversionServiceFactoryBean">

<property name="converters">

<set>

<ref bean="employeeConverter" />

</set>

</property>

</bean>

這樣配置就能既可以添加自定義的類型轉換器,又可以使用SpringMVC給我們提供的格式化工具

如果轉換類型出錯,錯誤資訊會被存進BindingResult裡,可以在函數入參時傳入BindingResult,如果出錯則其getErrorCount()必大于零,用法如下:

@RequestMapping(value = "/emp", method = RequestMethod.POST)

public String save(Employee employee, BindingResult result) {

System.out.println("save:" + employee);

if (result.getErrorCount() > 0) {

System.out.println("出錯了!");

for (FieldError error : result.getFieldErrors()) {

System.out.println(error.getField() + ":"

+ error.getDefaultMessage());

}

}

employeeDao.save(employee);

return "redirect:/emps";

}

第七講 JSR303資料校驗

JSR 303是Java為Bean資料合法性校驗提供的标準架構,它已經包含在JavaEE 6.0中

JSR 303通過在Bean屬性上标注類似于@NotNull、@Max等标準的注解指定校驗規則,并通過标準的驗證接口對Bean進行驗證

注解 功能說明
@Null 被注釋的元素必須為null
@NotNull 被注釋的元素必須不為null
@AssertTrue 被注釋的元素必須為true
@AssertFalse 被注釋的元素必須為false
@Min(value) 被注釋的元素必須是一個數字,其值必須大于等于指定的最小值
@Max(value) 被注釋的元素必須是一個數字,其值必須小于等于指定的最大值
@DecimalMin(value) 被注釋的元素必須是一個數字,其值必須大于等于指定的最小值
@DecimalMax(value) 被注釋的元素必須是一個數字,其值必須小于等于指定的最大值
@size(max, min) 被注釋的元素的大小必須在指定的範圍内
@Digits(integer, fraction) 被注釋的元素必須是一個數字,其值必須在可接受的範圍内
@Past 被注釋的元素必須是一個過去的日期
@Future 被注釋的元素必須是一個将來的日期
@Pattern(value) 被注釋的元素必須符合指定的正規表達式
Hibernate Validator擴充注解
@Email 被注釋的元素必須是電子郵箱位址
@Length 被注釋的字元串的大小必須在指定的範圍内
@NotEmpty 被注釋的字元串必須非空
@Range 被注釋的元素必須在合适的範圍内

Spring4.0擁有自己獨立的資料校驗架構,同時支援JSR303标準的校驗架構。

Spring在進行資料綁定時,可同時調用校驗架構完成資料校驗工作。在SpringMVC中,可直接通過注解驅動的方式進行資料校驗。

Spring的LocalValidatorFactoryBean既實作了Spring的Validator接口,也實作了JSR303的Validator接口。隻要在Spring容器中定義了一個 LocalValidatorFactoryBean,即可将其注入到需要資料校驗的Bean中。

Spring本身并沒有提供JSR303的實作,是以必須将JSR303的實作者的jar包(hibernate-validator-5.0.0.CR2中都有)放到類路徑下。

<mvc:annotation-driven/>會預設裝配好一個LocalValidatorFactoryBean,通過在處理方法的入參上标注@valid注解即可讓SpringMVC在完成資料綁定後執行資料校驗的工作

在已經标注了JSR303注解的表單/指令對象前标注一個@Valid,SpringMVC架構在将請求參數綁定到該入參對象後,就會調用校驗架構根據注解聲明的校驗規則實施校驗

SpringMVC是通過對處理方法簽名的規約來儲存校驗結果的:前一個表單/指令對象的校驗結果儲存到随後的入參中,這個儲存校驗結果的入參必須是BindingResult或Errors類型,這兩個類都位于org.springframework.validation包中

需要校驗的Bean對象和其綁定結果對象或錯誤對象是成對出現的,它們之間不允許聲明其他的入參

Errors接口提供了擷取錯誤資訊的方法,如getErrorCount()或getFieldErrors(String field)

BindingResult擴充了Errors接口

提示消息的國際化

每個屬性在資料綁定和資料校驗發生錯誤時,都會生成一個對應的FieldError對象。當一個屬性校驗失敗後,校驗架構會為該屬性生成4個消息代碼,這些代碼以校驗注解類名為字首,結合modleAttribute、屬性名及屬性類型名生成多個對應的消息代碼:例如User類中的password屬性标準了一個@Pattern注解,當該屬性值不滿足@Pattern所定義的規則時,就會産生以下4個錯誤代碼:

  1. Pattern.user.password
  2. Pattern.password
  3. Pattern.java.lang.String
  4. Pattern

當使用SpringMVC标簽顯示錯誤消息時,SpringMVC會檢視WEB上下文是否裝配了對應的國際化消息,如果沒有,則顯示預設的錯誤消息,否則使用國際化消息。

例:

最開始的NotEmpty、Email和Past都是用于驗證的注解,後面的employee.lastName等都是要驗證的變量

處理JSON

1.首先需要單獨的jar包,有三個jar包,

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

3.在需要用到JSON技術的方法前加@ResponseBody注解即可,如:

HttpMessageConverter<T>的工作原理

HttpMessageConverter的使用

使用HttpMessageConverter<T>将請求資訊轉化并綁定到處理方法的入參中或将響應結果轉為對應類型的響應資訊,Spring提供了兩種途徑:

  1. 使用@RequestBody/@ResponseBody對處理方法進行标注
  2. 使用HttpEntity<T>/ResponseEntity<T>作為處理方法的入參或傳回值

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

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

檔案下載下傳

@RequestMapping("/testResponseEntity")

public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws IOException {

byte[] body=null;

ServletContext servletContext=session.getServletContext();

InputStream in=servletContext.getResourceAsStream("/files/aaa.txt");

body=new byte[in.available()];

in.read(body);

HttpHeaders headers=new HttpHeaders();

headers.add("Content-Disposition", "attachment;filename=aaa.txt");

HttpStatus statusCode=HttpStatus.OK;

ResponseEntity<byte[]> response=new ResponseEntity<byte[]>(body, headers, statusCode);

return response;

}

第八講 國際化與檔案上傳

  1. 在頁面上能夠根據浏覽器語言設定的情況對文本(不是内容),時間,數值進行本地化處理。
  2. 可以在bean中擷取國際化資源檔案Locale對應的消息
  3. 可以通過超連結切換Locale,而不再依賴于浏覽器的語言設定情況

解決:

  1. 使用JSTL的fmt标簽
  2. 在bean中注入ResourceBundleMessageSource的執行個體,使用其對應的getMessage方法即可
  3. 配置LocalResolver和LocaleChangeInterceptor

在JSP中使用JSTL标簽需要在檔案頭加入如下語句:

<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

使用國際化語言的标簽:

<fmt:message key="i18n.username"></fmt:message>

配置國際化資源檔案需要在springmvc.xml檔案中寫以下代碼

<bean id="messageSource"

class="org.springframework.context.support.ResourceBundleMessageSource">

<property name="basename" value="i18n"></property>

</bean>

使用mvc:view-controller标簽可以直接響應轉發的頁面,而無需再經過Handler的方法

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

如何在Bean裡擷取locale對應的消息

@RequestMapping("/i18n")

public String testI18n(Locale locale){

String val=messageSource.getMessage("i18n.user", null, locale);

System.out.println(val);

return "i18n";

}

這樣輸出的val就是國際化後目前locale對應的資訊了

如何通過超連結切換Locale

首先在springmvc.xml檔案中需要配置

<!-- 配置SessionLocalResolver -->

<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver"></bean>

<!-- 配置LocaleChanceInterceptor -->

<mvc:interceptors>

<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"></bean>

</mvc:interceptors>

之後在超連結中帶上地區對應的locale參數即可,如:

<a href="i18n?locale=zh_CH">中文</a>

<a href="i18n?locale=en_US">English</a>

檔案上傳

SpringMVC為檔案上傳提供了直接的支援,這種支援是通過即插即用的MultipartResolver實作的。Spring用Jakarta Commons FileUpload技術實作了一個MultipartResolver實作類:CommonsMultipartResolver

SpringMVC上下文中預設沒有裝配MultipartResolver,是以預設情況下不能處理檔案的上傳工作,如果想使用Spring的檔案上傳功能,需現在上下文中配置MultipartResolver

defaultEncoding:必須和使用者JSP的pageEncoding屬性一緻,以便正确解析表單的内容

為了讓CommonsMultipartResolver正确工作,必須先将Jakarta Commons FileUpload及Jakarta Commons io的類包添加到類路徑下

例:

springmvc.xml中的配置檔案

<!-- 配置MultipartResolver -->

<bean id="multipartResolver"

class="org.springframework.web.multipart.commons.CommonsMultipartResolver">

<property name="defaultEncoding" value="UTF-8"></property>

<property name="maxUploadSize" value="10240000"></property>

</bean>

在Handler中的執行語句

@RequestMapping("/testFileUpload")

public String testFileUpload(@RequestParam("desc") String desc,

@RequestParam("file") MultipartFile file) throws IOException{

System.out.println("desc:" + desc);

System.out.println("OriginalFilename:" + file.getOriginalFilename());

System.out.println("InputStream:" + file.getInputStream());

return "success";

}

這樣就能獲得上傳的file的資訊了,但是此處并未進行存儲!

第九講 攔截器

自定義攔截器

SpringMVC也可以使用攔截器對請求進行攔截處理,使用者可以自定義攔截器來實作特定的功能,自定義的攔截器必須實作HandlerInterceptor接口

  1. preHandle():這個方法在業務處理器處理請求之前被調用,在該方法中對使用者請求request進行處理。如果程式員決定該攔截器對請求進行攔截處理後還要調用其他的攔截器,或者是業務處理器去進行處理,則傳回true;如果程式員決定不需要再調用其他的元件去處理請求,則傳回false。
  2. postHandle():這個方法在業務處理器處理完請求後,但是DispatcherServlet向用戶端傳回響應前被調用,在該方法中對使用者請求request進行處理。
  3. afterCompletion():這個方法在DispatcherServlet完全處理完請求後被調用,可以在該方法中進行一些資源清理的操作。

在springmvc.xml檔案中的配置

<mvc:interceptors>

<!-- 配置自定義的攔截器 -->

<bean class="springmvc.interceptors.FirstInterceptor"></bean>

</mvc:interceptors>

在<mvc:interceptors>裡面這樣指定适用路徑精确配置攔截器,注意這個是沒有s的

<mvc:interceptor>

<mvc:mapping path="/i18n"/>

<!-- 配置自定義的攔截器 -->

<bean class="springmvc.interceptors.FirstInterceptor"></bean>

</mvc:interceptor>

自定義的攔截器類要怎麼寫:

public class FirstInterceptor implements HandlerInterceptor {

@Override

public void afterCompletion(HttpServletRequest arg0,

HttpServletResponse arg1, Object arg2, Exception arg3)

throws Exception {

System.out.println("[FirstInterceptor] afterCompletion"); }

@Override

public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1,

Object arg2, ModelAndView arg3) throws Exception {

System.out.println("[FirstInterceptor] postHandle");

}

@Override

public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1,

Object arg2) throws Exception {

System.out.println("[FirstInterceptor] preHandle");

return true;

}

}

注意:preHandler方法的傳回值必須是true才會執行postHandler和afterCompletion,但是在實作HandlerInterceptor接口時預設傳回false,可以用于設定權限

攔截器的執行順序

當配置了多個攔截器時,preHandle按配置的正序進行,postHandler和afterCompletion方法按配置的反序進行

當SecondInterceptor的preHandler傳回false時,則隻執行兩個的preHandle和First的afterCompletion三個方法,其他方法全部跳過

第十講 異常處理

SpringMVC通過HandlerExceptionResolver處理程式的異常,包括Handler映射、資料綁定以及目标方法執行時發生的異常。

SpringMVC提供的HandlerExceptionResolver的實作類

DispatcherServlet預設裝配的HandlerExceptionResolver:

  1. 沒有使用<mvc:annotation-driven/>配置:
  1. 使用了<mvc:annotation-driven/>配置:

ExceptionHandler注解

  1. 主要處理Handler中用@ExceptionHandler注解定義的方法。
  2. @ExceptionHandler注解定義的方法優先級問題:例如發生的是NullPointerException但是聲明的異常有RuntimeException和Exception,此後會根據異常的最近繼承關系找到繼承深度最淺的那個@ExceptionHandler注解方法,即标記了RuntimeException的方法
  3. ExceptionHandlerMethodResolver内部若找不到@ExceptionHandler注解的話,會找@ControllerAdvice中的@ExceptionHandler注解方法

注意事項:

  1. 在@ExceptionHandler方法的入參中可以加入Exception類型的參數,該參數可以對應發生的異常對象
  2. @ExceptionHandler方法的入參中不能傳入Map,若希望把異常資訊傳到頁面上,需要使用ModelAndView作為傳回值
  3. @ExceptionHandler方法标記的異常有優先級的問題
  4. @ControllerAdvice:如果在目前Handler找不到@ExceptionHandler标記的方法來處理目前方法出現的異常,則會去@ControllerAdvice标記的類中查找@ExceptionHandler标記的方法來處理異常,處理規則與之前的相同

例:

@ControllerAdvice

public class HandlerException {

@ExceptionHandler({ArithmeticException.class})

public ModelAndView handleArithmeticException(Exception ex) {

System.out.println("---出現異常");

ModelAndView mv=new ModelAndView("error");

mv.addObject("exception", ex);

return mv;

}

@ExceptionHandler({RuntimeException.class})

public ModelAndView handleRuntimeException(Exception ex) {

System.out.println("---[出現異常]");

ModelAndView mv=new ModelAndView("error");

mv.addObject("exception", ex);

return mv;

}

}

ResponseStatusExceptionResolver

在異常及異常父類中找到@ResponseStatus注解,然後使用這個注解的屬性進行處理

定義一個@ResponseStatus注解修飾的異常類

@ResponseStatus(HttpStatus.UNAUTHORIZED)

public class UnauthorizedException extends RuntimeException {}

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

@ResponseStatus既可以用于辨別類,也可以用于辨別方法

@ResponseStatus(value=HttpStatus.FORBIDDEN,reason="使用者名和密碼不比對")中的reason會存在于exception的message中

Handler内容

@RequestMapping("/testResponseStatusExceptionResolver")

public String testResponseStatusExceptionResolver(@RequestParam("i") int i) {

if(i==13){

throw new UserNameNotMatchPasswordException();

}

System.out.println("testResponseStatusExceptionResolver.");

return "success";

}

對應的自定義Exception類的内容

@ResponseStatus(value=HttpStatus.FORBIDDEN,reason="使用者名和密碼不比對")

public class UserNameNotMatchPasswordException extends RuntimeException {

private static final long serialVersionUID = 1L;

}

DefaultHandlerExceptionResolver

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

NoSuchRequestHandlingMethodException、

HttpRequestMethodNotSupportedException、

HttpMediaTypeNotSupportedException、

HttpMediaTypeNotAcceptableException等

不需要特殊配置,作為了解即可!

SimpleMappingExceptionResolver

如果希望對所有異常進行統一處理,可以使用SimpleMappingExceptionResolver,它将異常類名映射為視圖名,即發生異常時使用對應的視圖報告異常,需要進行配置:

<!-- 配置使用 SimpleMappingExceptionResolver 來映射異常 -->

<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">

<property name="exceptionMappings">

<props>

<prop key="java.lang.ArrayIndexOutOfBoundsException">error

</prop>

</props>

</property>

</bean>

這樣當Handler中發生數組越位的錯誤時,會把exception存入request,然後會跳轉到error的界面,即相當于那個Handler中的那個方法return "error";

如果想改變其儲存進request的鍵,那麼可以配置

<property name="exceptionAttribute" value="xxx"></property>

這樣xxx就是目前Exception的名字了,${ xxx }就可以在jsp頁面上顯示該異常

第十一講 SpringMVC運作流程分析

第十二講 SpringMVC與Spring

需要進行Spring整合SpringMVC嗎?還是否需要再加入Spring的IOC容器?是否需要在web.xml檔案中配置啟動Spring IOC容器的ContextLoaderListener

  1. 需要:通常情況下,類似于資料源、事務,整合其他架構都是放在Spring的配置檔案中的(而不是SpringMVC的配置檔案中)。實際上放入Spring配置檔案對應的IOC容器中的還有Service 和Dao.
  2. 不需要:都放在SpringMVC的配置檔案中,也可以分多個Spring的配置檔案,然後使用import節點導入其他的配置檔案

并用的問題

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

  1. 解決方案
  1. 使Spring的IOC容器掃描的包和SpringMVC的IOC容器掃描的包沒有重合的部分
  2. 使用

<context:component-scan base-package="springmvc" use-default-filters="false">

<context:exclude-filter type="annotation" expression=""/>

<context:include-filter type="annotation" expression=""/>

</context:component-scan>

配置來規定隻能掃描的注解include-filter的是要掃描的,exclude-filter是不掃描的,在springmvc.xml和beans.xml檔案中都要配置,一個中寫了include-filter,另一個對應的注解就要寫入exclude-filter

SpringMVC的IOC容器中的bean可以來引用Spring IOC容器中的bean,但是反過來則不行,Spring IOC容器中的bean卻不能來引用SpringMVC IOC容器中的bean!

SpringMVC對比Struts2

  • SpringMVC的入口是Servlet,而Struts2是Filter
  • SpringMVC會稍微比Struts2快些。SpringMVC是基于方法設計,而Sturts2是基于類,每次發一次請求都會執行個體一個Action
  • SpringMVC使用更加簡潔,SpringMVC的開發效率也比Struts2高:支援JSR303,處理ajax的請求更友善

Struts2的OGNL表達式使頁面的開發效率相比SpringMVC更高些。

繼續閱讀