SpringMVC

[b]一、Spring概述[/b]
[b]1. 簡介[/b]
Spring MVC屬於SpringFrameWork的後續產品,已經融合在Spring Web Flow裏面。Spring 框架提供了構建 Web 應用程序的全功能 MVC 模塊。Spring框架在框架設計、擴展性、靈活性等方面全面超越了Struts、WebWork等MVC框架,從原來的追趕者一躍爲MVC的領跑者。Spring框架圍繞DispacherServlet這個核心展開,它負責截獲請求並將其分派給相應的處理器處理。

[b]2. Spring MVC和Struts2的比較[/b]
(1)Spring MVC的入口是servlet,而Struts2是filter,這裏要指出filter和servlet是不同的。
(2)Spring MVC會稍微比Struts2快。Spring MVC是基於方法的設計,方法級別的攔截。而struts2設計是基於類,採用類級別的攔截。
(3)參數傳遞方面,Struts2用屬性的get方法接受參數,這就說明參數是讓多個方法共享的。
(4)配置文件方面很明顯Struts2要複雜的多。

[b]二、Spring MVC示例[/b]
[b]1. 在web.xml中配置DispatcherServlet[/b]
DispatcherServlet是Spring MVC的靈魂和心臟,它負責接收HTTP請求並協調Spring MVC的各個組件完成請求處理的工作。和任何Servlet一樣,用戶必須在web.xml中配置好DispatcherServlet。如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<!-- 指定業務層Spring容器的配置文件,多個用逗號隔開 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:config/spring-config.xml</param-value>
</context-param>

<!-- 通過contextConfigLocation指定的配置文件啓動業務層的Spring容器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 配置名爲spring的DispatcherServlet,默認自動加載/WEB-INF/spring-servlet.xml的配置文件,啓動Web層的Spring容器。
一個web.xml可以配置多個DispatcherServlet -->
<!-- 這裏,WEB層Spring容器將作爲業務層Spring容器的子容器,即WEB層容器可以引用業務層容器的Bean,而業務層卻不能訪問WEB層容器的Bean -->
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 指定使用類路徑下的WEB層spring容器配置文件 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:config/spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 指定DispatcherServlet處理所有URL以.html爲後綴的HTTP請求 -->
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
</web-app>


[b]2. 編寫處理請求的控制器[/b]
Spring MVC通過一個@Controller註解即可將一個POJO轉化爲處理器請求的控制器,通過@RequestMapping爲控制器指定處理哪些URL的請求。如下:

UserController.java:
package com.jiang.web.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import com.jiang.web.entity.User;
//標註UserController成爲處理請求的控制器
@Controller
//處理來自/user URI的請求,限定請求路徑爲/user下,此路徑可以不加
@RequestMapping("/user")
public class UserController {
//直接處理“/user.html”的請求,請求的方法必須爲POST
@RequestMapping(method = RequestMethod.POST)
//將表單值映射到User對象中
public ModelAndView cteateUser(User user) {
//注意此處的ModelAndView是在org.springframework.web.servlet包下,不要import錯了
ModelAndView mav = new ModelAndView();
mav.setViewName("menu");
mav.addObject("user", user);
return mav;
}
//將處理http://127.0.0.1:8080/WalletManager/user/login.html的請求
@RequestMapping("/login")
public String index() {
//返回一個邏輯視圖名,根據配置文件映射爲/WEB/INF/customer/login.jsp
return "login";
}
}


User.java:
package com.jiang.web.entity;
public class User {
private String userName;
private String password;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}


[b]3. 編寫視圖對象[/b]
WEB-INF/customer/login.jsp:
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>登陸</title>
</head>
<body>
<!-- 將表單提交到/user.html控制器中, -->
<form method="post" action="/user.html">
<table>
<tr>
<td>用戶名:</td>
<td><input type="text" name="userName" /></td>
</tr>
<tr>
<td>密碼:</td>
<td><input type="password" name="password" /></td>
</tr>
<tr>
<td><input type="submit" value="登陸" /></td>
</tr>
</table>
</form>
</body>
</html>


WEB-INF/customer/menu.jsp:
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>新會員中心</title>
</head>
<body>
Wellcome! ${user.userName}
</body>
</html>


[b]4. 配置Spring MVC的配置文件[/b]
spring-config.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:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.0.xsd">
</beans>


spring-mvc.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:p="http://www.springframework.org/schema/p"
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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">

<!-- 讓Spring掃描包下標註註解的類生效 -->
<context:component-scan base-package="com.jiang.web.controller"/>

<!-- 定義一個視圖名稱解析器,將名稱解析爲/WEB-INF/customer/名稱.jsp -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/customer/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>


[b]5. SpringMVC處理user.html過程描述[/b]
(1)DispatcherServlet接收到客戶端的user.html的請求
(2)DispatcherServlet使用DefaultAnnotationHandlerMapping查找負責處理該請求的處理器爲user.html
(3)DispatcherServlet將請求分發給名爲user.html的UserController處理器
(4)處理器完成業務處理後,返回ModelAndView對象,其中View的邏輯名爲menu,模型包含一個鍵爲user的User對象
(5)DispatcherServlet調用InternalResourceViewResolver組件對ModelAndView中的邏輯視圖名進行解析,得到真實的/WEB-INF/customer/menu.jsp視圖對象
(6)DispatcherServlet使用/WEB-INF/customer/menu.jsp對模型中的user模型對象進行渲染
(7)返回響應頁面給客戶端

[b]三、註解驅動的控制器[/b]
[b]1. 使用@RequestMapping映射請求[/b]
在POJO類定義處標註@Controller,再通過<context:component-scan base-package="com.jiang.web.controller"/>掃描相應的類包,即可使POJO成爲一個能處理HTTP請求的控制器。然後在控制器的類定義及方法定義處都可標註@RequestMapping,類定義處提供初步請求映射信息,方法處提供進一步的細分映射信息。
將請求映射到控制器處理方法的工作包含一系列的映射規則,這些規則是根據請求中的各種信息制定的,具體包括請求URL、請求參數、請求方法、請求頭這4個方面的信息項。

[b](1)通過請求URL進行映射[/b]
@RequestMapping("/user"),它不但支持標準的URL,還支持Ant風格(即?、*和**的字符)的和帶{xxx}佔位符的URL。如下:
/user/*/createUser:匹配/user/aaa/createUser、/user/bbb/createUser等URL
/user/**/createUser:匹配/user/createUser、/user/aaa/bbb/createUser等URL
/user/createUser??:匹配/user/createUseraaa、/user/createUserbbb等URL
/user/{userId}:匹配/user/123、/user/234等URL
/user/**/{userId}:匹配/user/aaa/bbb/123等URL
/company/{companyId}/user/{userId}/detail:匹配/company/123/user/456/detail等URL

URL中的{xxx}可以通過@PathVariable將URL中的佔位符參數綁定到控制器處理方法的入參中,類和方法處的佔位符都可以綁定到處理方法的入參中,如下:
    @RequestMapping("owners/{ownerId}")
public class UserController{
@RequestMapping("users/{userId}")
public ModelAndView showDetail(@PathVariable("ownerId")String ownerId,@PathVariable("userId")String userId){
..
}
}

注意,要使用上面的入參成功綁定URL中的佔位符,必須在編譯時開啓debug開關。

[b](2)通過請求參數、請求方法或請求頭進行映射讓請求映射更加精確化[/b]

[img]http://dl2.iteye.com/upload/attachment/0102/2241/aaa0edf9-b54a-3bf0-913b-785011a03eaf.png[/img]

@RequestMapping的value、method、params及headers分別表示請求URL、請求方法、請求參數及報文頭的映射條件,它們之間是與的關係,聯合使用多個條件項可讓請求映射更加精確化。
    @Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping(value="/delete",method = RequestMethod.POST,params="userId")
public String test1(@PathVariable("userId")String userId) {
return "user/test1";
}
@RequestMapping(value="/show",headers="content-type=text/*")
public String test2(@PathVariable("userId")String userId) {
return "user/test2";
}
}


[b]2. 請求處理方法的簽名[/b]
處理方法的簽名包括對方法及方法入參標註相應的註解、入參、返回值等。對於這些簽名,SpringMVC會優雅的將Http請求的信息綁定到相應的方法入參中,並根據方法返回值類型作出相應的後續處理。

[b](1)使用@RequestParam綁定請求參數值[/b]
@RequestMapping(value="/handle")
public String handle(
@RequestParam(value="userName",required=false)String userName,
@RequestParam(value="age")int age) {
...
}
上面的實例將“UserName”和“age”請求參數分別綁定到handle()方法的userName和age中,並自動完成類型轉換。required參數表示請求中是否必須包含對應的參數,默認爲true,上面的實例如果不存在age請求參數,將拋出異常。還有一個defaultValue參數設置默認參數名,不推薦使用該參數。

[b](2)使用@CookieValue綁定請求中的Cookie值[/b]
@RequestMapping(value="/handle")
public String handle(
@CookieValue(value="sessionId",required=false)String sessionId,
@RequestParam(value="age")int age) {
...
}
value指定Cookie的名稱,required爲false表示請求中沒有相應的Cookie時也不會報錯。

[b](3)使用@RequestHeader綁定請求報文頭的屬性值[/b]
@RequestMapping(value="/handle")
public String handle(
@RequestHeader("Accept-Encoding")String encoding,
@RequestHeader("Keep-Alive")long keepAlive) {
...
}
參數和@RequestParam相同。

[b](4)使用命令/表單對象綁定請求參數值[/b]
@RequestMapping(value="/handle")
public String handle(User user) {
...
}
所謂命令/表單對象並不需要實現任何接口,僅是一個擁有若干屬性的POJO,Spring MVC會按請求參數名和命令/表單對象屬性名匹配的方式,自動對該對象填充屬性值。支持級聯的屬性名,如address.addressName。

[b](5)使用Servlet API對象作爲入參[/b]
在Spring MVC中,控制器類可以不依賴任何Servlet API對象,但是Spring MVC並不阻止我們使用Servlet API的類作爲處理方法的入參。

@RequestMapping(value="/handle")
public void handle(HttpServletRequest request,HttpServletResponse response) {
...//同時使用HttpServletRequest和HttpServletResponse作爲入參
//使用HttpServletResponse返回響應時,將處理方法返回值設成void即可
String userName = WebUtils.findParameterValue(request,"userName");
response.addCookie(new Cookie("userName", userName));
}

@RequestMapping(value="/handle")
public String handle(HttpServletRequest request) {
...//只使用HttpServletRequest作爲入參,可返回String或ModelAndView
}

@RequestMapping(value="/handle")
public String handle(HttpSession session) {
...//使用HttpSession作爲入參,可返回String或ModelAndView
}

@RequestMapping(value="/handle")
public String handle(HttpServletRequest request, @RequestParam(value="userName",required=false)String userName) {
...//同時使用HttpServletRequest和基本類型作爲入參,它們之間的位置不固定
}

Spring MVC在org.springframework.web.context.request包中定義了若干個可代理Servlet原生API類的接口,它們也可以作爲處理類的入參,通過這些類可訪問請求對象的任何信息。
@RequestMapping(value="/handle")
public String handle(WebRequest request) {
...//使用Spring MVC的WebRequest作爲入參
}

[b](6)使用IO對象作爲入參[/b]
Servlet的ServletRequest擁有getInputStream()和getReader()方法,可以通過它們讀取請求的信息。相應Servlet的ServletResponse擁有getOutputStream()和getWriter()方法,可以通過它們輸出響應信息。
Spring MVC允許控制器的處理方法使用java.io.InputStream/java.io.Reader及java.io.OutputStream/java.io.Writer作爲方法的入參,Spring MVC將獲取ServletRequest的InputStream/Reader或ServletResponse的OutputStream/Writer,然後傳遞給控制器的處理方法。如下:

@RequestMapping(value="/handle")
public String handle(OutputStream os) throws IOException {
Resource res = new ClassPathResource("/image.jpg");
FileCopyUtils.copy(res.getInputStream(), os);//將圖片寫到輸出流
}

[b](7)其它類型的參數[/b]
控制器處理方法的入參除支持以上類型的參數以外,還支持java.util.Locale、java.security.Principal,可以通過Servlet的HttpServletRequest的getLocal()及getUserPrincipal()得到相應的值。如果處理方法的入參類型爲Locale或Principal,Spring MVC自動從請求對象中獲取相應的對象並傳遞給處理方法的入參。

[b]3. 使用HttpMessageConverter<T>[/b]
HttpMessageConverter<T>是Spring3.0新添加的一個重要接口,它負責將請求信息轉換爲一個對象(類型爲T),將對象(類型爲T)輸出爲響應信息。
DispatcherServlet默認已經安裝了AnnotationMethodHandlerAdapter作爲HandlerAdapter的組件實現類,HttpMessageConConverter即由AnnotationMethodHandlerAdapter使用,將請求信息轉換爲對象,或將對象轉換爲響應信息。

Spring爲HttpMessageConverter<T>提供了衆多的實現類,它們組成了一個功能強大、用途廣泛的HttpMessageConverter<T>家族,如下:
StringHttpMessageConverter 將請求信息轉換爲字符串,默認裝配;
FormHttpMessageConverter 將表單數據讀取到MultiValueMap中;
XmlAwareFormHttpMessageConverter 擴展於FormHttpMessageConverter,如果部分表單屬性是XML數據,可用該轉換器進行讀取,默認裝配;
ResourceHttpMessageConverter 讀寫org.springframework.core.io.Resource類型;
BufferedImageHttpMessageConverter 讀寫BufferedImage對象;
ByteArrayHttpMessageconverter 讀寫二進制數據,默認裝配;
SourceHttpMessageConverter 讀寫javax.xml.transform.Source類型的數據,默認裝配;
MarshallingHttpMessageConverter 通過Spring的org.springframework.oxm.Marshaller(將Java對象轉換成XML)和Unmarshaller(將XML解析爲Java對象)讀寫XML消息;
Jaxb2RootElementHttpMessageConverter 通過JAXB2讀寫XML消息,將請求消息轉換到標註XmlRootElement和XmlType註解類型中;
MappingJacksonHttpMessageConverter 通過利用Jackson開源類包的ObjectMapper讀寫JSON數據;
RssChannerHttpMessageConverter 能夠讀寫RSS種子消息;
AtomFeedHttpMessageConverter 和RssChannerHttpMessageConverter能夠讀寫RSS種子消息;

上面標明默認裝配的實現類已裝配到AnnotationMethodHandlerAdapter,如果需要裝配其他類型,可以Spring的的Web容器上下文中自行定義一個AnnotationMethodHandlerAdapter,如下:
<!-- 定義一個AnnotationMethodHandlerAdapter,顯式定義後將覆蓋默認的AnnotationMethodHandlerAdapter -->
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" p:messageConverters-ref="messageConverters"/>
<util:list id="messageConverters">
<bean class="org.springframework.http.converter.BufferedImageHttpMessageConverter"/>
.....
</util:list>

使用HtpMessageConverter<T>,將請求信息轉化並綁定到處理方法的入參中。
(1)使用@RequestBody / @ResponseBody對處理方法進行標註(p540)
(2)使用HttpEntity<T> / ResponseEntity<T>作爲處理方法的入參或返回值(p542)

[b]示例:處理JSON[/b]
(1)首先爲AnnotationMethodHandlerAdapter裝配上可處理JSON的HttpMessageConverter:
    <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list >
<ref bean="mappingJacksonHttpMessageConverter" />
</list>
</property>
</bean>

<bean id="mappingJacksonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>application/json; charset=UTF-8</value>
<value>text/html; charset=UTF-8</value>
</list>
</property>
</bean>


(2)在控制器編寫相應的方法:
@RequestMapping("/product")
@ResponseBody
public List<Product> findProductList() {
List<Product> liProduct= new ArrayList<Product>();
for (ProductCodeEnum t : ProductCodeEnum.values())
{
Product product = new Product();
product.setId(t.getCode());
product.setText(t.getCnName());
product.setLeaf(true);

liProduct.add(product);
}
return liProduct;
}


注意,處理JSON必須引入下面四個包:
<dependency> 
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-lgpl</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-lgpl</artifactId>
<version>1.9.13</version>
</dependency>


[b]4. 處理模型數據[/b]
對於MVC框架來說模型數據是最重要的,因爲控制層(C)是爲了產生模型數據(M),而視圖(V)則是爲了渲染模型數據。
[b](1)ModelAndView[/b]
控制器處理方法的返回值如果爲ModelAndView,則其既包含視圖信息,也包含模型數據信息。可以簡單地將模型數據看成一個Map<Stirng, Object>對象。ModelAndView操作如下:
ModelAndView addObject(String attributeName, Object attribute Value); //添加模型數據
ModelAndView addAllObjects(Map<String, ?> modelMap); //添加模型數據
void setView(View view); //指定一個具體的視圖對象
void setViewName(String viewName); //指定一個邏輯視圖名

[b](2)ModelAttribute[/b]
如果希望將方法入參對象添加到模型中,只需在相應入參前使用@ModelAttribute註解或在方法定義處使用@ModelAttribute註解即可。
@RequestMapping(value = "/handle61")
// SpringMVC將請求消息綁定到User對象中,然後再以“user”爲鍵將User對象放到模型中。
public String handle61(@ModelAttribute("user") User user) {
user.setUserId("1000");
return "/user/createSuccess";
}

@ModelAttribute("user")
public User getUser() {
User user = new User();
user.setUserId("1001");
user.setUserName("<>");
return user;
}
當訪問此控制器類中的任何一個請求處理方法前,都會事先執行標註了@ModelAttribute的getUser()方法,並將其返回值以user爲鍵添加到模型中。

[b](3)Map及Model[/b]
入參爲org.springframework.ui.Model、org.springframework.ui.ModelMap或java.util.Map時處理方法返回時,Map中的數據會自動添加到模型中。
@RequestMapping(value = "/handle63")
public String handle63(ModelMap modelMap) {
User user = (User) modelMap.get("user");
user.setUserName("tom");
modelMap.addAttribute("testAttr", "value1");
return "/user/showUser";
}

[b](4)SessionAttributes[/b]
將模型中的某個屬性暫存到HttpSession中,以便多個請求之間可以共享這個屬性
// 將handle71處的模型屬性自動保存到HttpSession中
@SessionAttributes("user")

@RequestMapping(value = "/handle71")
public String handle71(@ModelAttribute("user") User user) {
user.setUserName("John");
return "redirect:handle72.html"; //向handle72發起一個新的請求
}
@RequestMapping(value = "/handle72")
public String handle72(ModelMap modelMap, SessionStatus sessionStatus) {
User user = (User) modelMap.get("user"); //讀取模型中的數據
if (user != null) {
user.setUserName("Jetty");
sessionStatus.setComplete(); //讓SpringMVC清除本處理器對應的會話屬性
}
return "/user/showUser";
}

[b]四、處理方法的數據綁定[/b]
在請求消息到達真正調用處理方法的這一段時間內,Spring MVC還完成了很多工作,包括數據轉換、數據格式化及數據校驗等。
[b]1. 數據綁定流程[/b]
Spring MVC通過反射機制對目標處理方法的簽名進行分析,將請求消息綁定到處理方法的入參中。
(1)Spring MVC主框架將ServletRequest對象及處理方法的入參對象實例傳遞給DataBinder
(2)DataBinder調用裝配在Spring Web上下文中的ConversionService組件進行數據類型轉換、數據格式化的工作,將ServletRequest中的消息填充到入參對象中。
(3)DataBinder對象繼續調用Validator組件對已經綁定了請求消息數據的入參對象進行數據合法性校驗。最終生成BindingResult對象,將它們賦給處理方法的入參。

[b]2. 數據轉換[/b]
Spring3.0在覈心模型中添加了一個通用的類型轉換模塊,位於org.springframework.core.convert包中,同時支持Java標準的PropertyEditer。
(1)ConversionService是Spring類型轉換體系的核心接口。
(2)Spring支持的轉換器
Converter<S,T>
GenericConverter
ConverterFactory
(3)在Spring MVC中使用ConversionService自定義轉換器

[b]3. 數據格式化[/b]
Spring3.0引入了一個新的格式化框架,位於org.springframework.format包中,
(1)Formatter<T>是格式化框架最重要的接口
(2)註解驅動格式化的重要接口
(3)啓用註解驅動格式化功能
(4)註解驅動格式化實例

[b]4. 數據校驗[/b]
(1)JSR303是Java爲Bean數據合法性校驗所提供的標準框架,它已經包含在Java EE6.0中。通過在Bean屬性上標註類似於@NotNull等標準的註解指定校驗規則
(2)Spring校驗框架,它在org.springframework.validation包下,通過註解驅動的方式進行數據校驗。
(3)Spring數據校驗(獲取校驗結果、如何在頁面中顯示錯誤、通過國際化資源顯示錯誤信息)
(4)自定義校驗規則(爲請求處理類裝配一個自定義的Validator)

[b]五、視圖和視圖解析器[/b]
請求處理方法執行完成後,最終返回一個ModelAndView對象,對於那些String、View或ModelMap等類型的處理方法,SpringMVC也會在內部將它們裝配成一個ModelAndView對象,它包含了實圖邏輯名和模型對象的信息。
1. 視圖,是渲染模型數據,將模型裏的數據以某種形式呈現給客戶(JSP、Excel、PDF等)。
2. 視圖解析器,將邏輯視圖名解析爲一個具體的視圖對象。
3. JSP和JSTL
(1)使用InternalResourceViewResolver作爲視圖解析器(JSP)
(2)使用Spring表單標籤,它可以很容易地將模型數據中的表單/命令對象綁定到HTML表單元素中
4. 模板視圖
FreeMarker和Velocity是除了JSP之外被使用最多的頁面模板技術。頁面模板編寫好頁面結構,模板頁面中使用一些特殊的變量標識符綁定Java對象的動態數據。
5. Excel
如果希望使用Excel展示用戶列表,僅需要擴展Spring的AbstractExcelView或AbstractJExcelView即可。
6. PDF
PDF視圖和Excel類似,也是使用一個Bean作爲視圖對象
7. XML
將模型中的數據以XML的形式輸出,對應視圖對象爲MarshallingView
8. JSON
SpringMVC的MappingJacksonJsonView藉助Jackson框架的ObjectMapper將模型數據轉換爲JSON格式輸出。
默認情況下,MappingJacksonJsonView會將模型中的所有數據都輸出爲JSON,這顯然是不適合的,可以通過renderedAttravutes指定模型中哪些屬性需要輸出。
9. XmlViewResolver
同BeanNameViewResolver,但它可以將視圖Bean定義在一個獨立的XML文件中
10. ResourceBundleViewResolver
它通過一個國際化資源文件定義視圖對象,爲不同地區的用戶提供不同類型的視圖
11. 混合使用多種視圖技術
(1)ContentNegotiatingViewResolver
(2)使用同一URL獲取不同形式的返回內容

[b]六、本地化解析[/b]
1. 本地化概述
瀏覽器中設置的本地化類型會包含在HTML請求報文頭中發送給Web服務器,確切地說是通過報文頭的Accept-Language參數將“語言首選項”對話框中選擇的語言發送到服務器,成爲服務器判別客戶端本地化類型的依據。可以通過IE菜單:工具--Internet選項--語言--選擇本地化類型設置。

2. 使用CookieLocaleResolver,DispactcherServlet會自動識別本地化解析器並裝配它。
3. 使用SessionLocaleResolver,查找Session中屬性名爲SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME的屬性,並將其轉換爲Locale對象。
4. 使用LocaleChangeInterceptor,通過一個請求參數控制網站的本地化。

[b]七、文件上傳[/b]
SpringMVC爲文件上傳提供了即插即用的MultipartResolver,採用了Jakarta Commons FileUpload技術實現了一個MultipartResolver實現類:CommonsMultipartResolver。
[b]1. 配置MultipartResolver[/b]
 <!-- 文件上傳 -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver"
p:defaultEncoding="UTF-8"
p:maxUploadSize="5000000"
p:uploadTempDir="upload/temp"/>


[b]2. 編寫控制器[/b]
 @RequestMapping(value = "/upload") 
public String updateThumb(@RequestParam("name") String name,
@RequestParam("file") MultipartFile file) throws Exception{
if (!file.isEmpty()) {
file.transferTo(new File("d:/temp/"+file.getOriginalFilename()));
return "redirect:success.html";
}else{
return "redirect:fail.html";
}
}


SpringMVC會將上傳文件綁定到MultipartFile對象中。使用如下:
byte[] getBytes():獲取文件數據
String getContentType():獲取文件MIME類型
InputStream getInputStream():獲取文件流
String getName():獲取表單中文件組件的名字
String getOriginalFilename():獲取上傳文件的原名
long getSize():獲取文件的字節大小,單位byte
boolean isEmpty():是否有上傳的文件
void transferTo(File dest):將上傳文件保存到一個目標文件中

[b]3. 表單[/b]
負責上傳文件的表單編碼必須是“multipart/form-data”類型。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>請上傳用戶頭像</title>
</head>
<body>
<h1>
請選擇上傳的頭像文件
</h1>
<form method="post" action="<c:url value="/user/upload.html"/>" enctype="multipart/form-data">
<input type="text" name="name" />
<input type="file" name="file" />
<input type="submit" />
</form>
</body>
</html>


[b]八、其它[/b]
[b]1. 靜態資源處理[/b]
(1)將DispatcherServlet請求映射配置爲"/",使其捕獲Web容器所有的請求。
    <filter>
<filter-name>Set Character Encoding</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>
</filter>
<filter-mapping>
<filter-name>Set Character Encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>


(2)Spring MVC配置
方法一:
<!-- 如果是靜態資源的請求,就將該請求轉由Web應用服務器默認的Servlet處理,-->
<mvc:default-servlet-handler/>

方法二:
<!-- 由Spring MVC框架自己處理靜態資源,將Web根路徑“/”及類路徑下/META-INF/publicResources/的目錄映射爲/resources路徑。假設Web根路徑下擁有js/ext.js,則在jsp則可通過 "/resources/js/ext.js訪問" -->
<mvc:resources mapping="/resources/**" location="/, classpath:/META-INF/publicResources/"/>
<mvc:resources/>允許靜態資源放在任何地方,如WEB-INF目錄下、類路徑下等。在接收到靜態資源的獲取請求時,會檢查請求頭的Last-Modified值,如果靜態資源沒有發生變化,則直接返回303響應狀態碼,指示客戶端使用瀏覽器緩存的數據,而非將靜態資源的內容輸出到客戶端。如果在映射的物理路徑下存在多個同名靜態資源,找到第1個就返回,查找的順序和物理路徑在location中的配置順序一致。
<mvc:resources mapping="/resources/**"
location="/" cache-period="31536000"/>
通過cache-period屬性設置靜態資源在客戶端瀏覽器中的緩存有效時間,一般設置爲一年。

有時由於瀏覽器本身緩存管理機制的問題,當我們發佈新的版本時客戶端並不會從服務器端下載新的靜態資源。一個好的解決辦法是:網頁中引用靜態資源的路徑添加應用的發佈版本號,這樣由於版本號的變更造成網頁中靜態資源路徑發生更改,從而使這些靜態資源成爲“新的資源”。下面將版本號包含到<mvc:resources/>中。

創建版本號獲取類:
import javax.servlet.ServletContext;
import org.springframework.web.context.ServletContextAware;

public class ResourcePathExposer implements ServletContextAware {
private ServletContext servletContext;
private String resourceRoot;

public void init() {
String version = "1.2.1";
resourceRoot = "/resources-" + version;
getServletContext().setAttribute("resourceRoot",
getServletContext().getContextPath()+resourceRoot);
}

public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}

public String getResourceRoot() {
return resourceRoot;
}

public ServletContext getServletContext() {
return servletContext;
}
}


在<mvc:resources/>中配置:
    <bean id="rpe" class="com.baobaotao.web.ResourcePathExposer"
init-method="init"/>
<mvc:resources mapping="#{rpe.resourceRoot}/**"
location="/" cache-period="31536000"/>


在jsp中引用路徑:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>靜態資源測試</title>
<script src="${resourceRoot}/js/test.js" type="text/javascript"> </script>
</head>
<body>
hello!!
<script>
test();
</script>
</body>
</html>


[b]2. 異常處理[/b]
SpringMVC通過HandlerExceptionResolver處理程序的異常,包括處理器映射、數據綁定以及處理器執行時發生異常。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章