SpringMVC是基於MVC設計理念的一款優秀的Web框架,是目前最流行的MVC框架之一,SpringMVC通過一套註解,讓POPJ成爲處理請求的控制器,而無需實現任何接口,然後使用實現接口的控制器也完全沒問題;支持REST風格的URL請求;採用鬆散耦合架構,比其他MVC框架更具有靈活性和擴展性。關於SpringMVC工程如何搭建請點擊:minglisoft.cn/technology
0 XML配置文件
web.xml配置DispatcherServlet,DispatcherServlet默認會加載/WEB-INF/xxx-servlet.xml的Spring配置信息,啓動Web層的Spring容器。當然,也可以通過配置contextConfigLocation來自定義配置文件名稱和位置,如下所示:
<servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/springmvc-servlet.xml</param-value> </init-param> <load-on-startup>2</load-on-startup></servlet><servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern></servlet-mapping>
0.1 xxx-servlet.xml配置
xxx-servlet.xml中一般配置自動掃描的包信息、HandlerMapping、ViewResolver,如果沒有配置HandlerMapping,則默認使用的是BeanNameURLHandlerMapping;如果沒有配置ViewResolver,則默認使用的InternalResourceViewResolver。
<!-- 配置自定義掃描的包--><context:component-scan base-package="com.luoxn28"></context:component-scan><!-- ViewResolver 視圖解析器 如果沒有配置的話,則InternalResourceViewResolver就是默認的 --><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="order" value="1"/> <!-- ViewResolver優先級 --> <!-- 前綴 和 後綴 --> <property name="prefix" value="/"/> <property name="suffix" value=".jsp"/></bean>
1 使用@RequestMapping映射請求
SpringMVC中使用@RequestMapping註解爲handler指定可以處理那些URL請求,該註解可以在方法及類定義處使用,DispatcherServlet處理請求時,通過handler的@RequestMapping提供的映射信息確定請求的處理類(或方法)。@RequestMapping在類和方法定義不同之處在於:
類定義處:提供初步的請求映射信息,相對於 WEB 應用的根目錄。
方法處:提供進一步的細分映射信息。,相對於類定義處的 URL,若類定義處未標註@RequestMapping,則方法處標記的 URL 相對於WEB 應用的根目錄。
使用@RequestMapping示例如下所示:
@Controller @RequestMapping("/test")public class Test { @RequestMapping("/testperson") public String testUser(Person person) { System.out.println(person); return "index";
1.1 @RequestMapping映射請求頭、請求方法、參數
@RequestMapping 除了可以使用請求URL映射請求外,還可以使用請求方法、請求參數及請求頭映射請求,這些從@RequestMapping註解源碼也可以看得出來。@RequestMapping 的 value、method、params 及 heads分別表示請求 URL、請求方法、請求參數及請求頭的映射條件,他們之間是與的關係,聯合使用多個條件可讓請求映射更加精確化。
@RequestMapping(value = "/delete", method = RequestMethod.POST, params = "userId")public String delete() { // ... return "xxx"; } @RequestMapping(value = "/show", headers = "contentType=text/*")public String show() { // ... return "xxx";
1.2 @RequestMapping映射URL綁定的佔位符
帶佔位符的URL是Spring3.0之後新增的功能,該功能是SpringMVC向REST風格挺進發展一個重要標誌。通過@PathVariable可以將URL中佔位符參數綁定到控制器處理方法的入參中:URL 中的 {xxx} 佔位符可以通過@PathVariable("xxx") 綁定到操作方法的入參中。
@RequestMapping("/delete/{id}")public String delete(@PathVariable("id") Integer id) { UserDao.delete(id); return "index"; }
ps: 關於什麼是REST請翻到最後:finally REST部分。
2 映射請求參數
Spring MVC 通過分析處理方法的簽名,將 HTTP 請求信息綁定到處理方法的相應人蔘中。
Spring MVC 對控制器處理方法簽名的限制是很寬鬆的,幾乎可以按喜歡的任何方式對方法進行簽名。
必要時可以對方法及方法入參標註相應的註解(@PathVariable、@RequestParam、@RequestHeader 等)、SpringMVC 框架會將 HTTP 請求的信息綁定到相應的方法入參中,並根據方法的返回值類型做出相應的後續處理。
2.1 使用@RequestParam綁定請求參數
在方法入參出使用@RequestParam把請求參數傳遞到方法中。-value是參數名,-requered是否必須,默認爲true,表示如果該請求參數中不存在對應的參數,則拋出異常。
@RequestMapping("/userinfo")public String getUserInfo(@RequestParam(value = "usrename", required = false) String username, @RequestParam("age") int age) { // ... return "xxx";
2.2 使用@RequestHeader綁定請求頭參數
請求頭包含了若干個屬性,服務器可據此獲知客戶端的信息,通過@RequestHeader即可將請求頭中的屬性值綁定到處理方法的入參中。
@RequestMapping("/hreaderinfo")public String getUserInfo(@RequestHeader("Accept-Encoding") String encoding) { // ... return "xxx";
2.3 使用@CookieValue綁定Cookie值
@RequestMapping("/sessioninfo")public String getUserInfo(@CookieValue("sessionId") String sessionId) { // ... return "xxx"; }
2.4 使用POPJ對象綁定請求參數值
Spring MVC 會按請求參數名和 POJO 屬性名進行自動匹配,自動爲該對象填充屬性值。支持級聯屬性。
@RequestMapping("/personinfo")public String getUserInfo(Person person) { // ... return "xxx"; }
當請求URL爲 /personinfo?name=luoxn28&age=23 時,就會進行POPJ對象的填充,Person類如下所示:
public class Person { String name; int age; // xxx}
2.5 使用ServletAPI類型的參數
@RequestMapping("/servlet")public String getUserInfo(HttpServletRequest request, HttpServletResponse response) { // ... return "xxx"; }
SpringMVC的handler方法可以接受HttpServletRequest/HttpServletResponse/HttpSession或者是java.security.Principal/Locale/InputStream/OutputStream/Reader/Writer類型的參數。
3 處理數據模型
SpringMVC有幾種方式用於數據模型輸出,有ModelAndView、Map及Model、@SessionAttributes、@ModelAttribute等。
ModelAndView: 處理方法返回值類型爲ModelAndView時,方法體即可通過該對象添加模型數據。
Map及Model: 入參爲org.springframework.ui.Model、org.springframework.ui.ModelMap 或 java.uti.Map 時,處理方法返回時,Map中的數據會自動添加到模型中。
@SessionAttributes: 將模型中的某個屬性暫存到HttpSession 中,以便多個請求之間可以共享這個屬性。
@ModelAttribute: 方法入參標註該註解後, 入參的對象就會放到數據模型中。
@RequestMapping("/model")public String getUserInfo(Map<String, Object> map) { map.put("time", new Date()); return "xxx"; }
@Controller @RequestMapping("/test") @SessionAttributes("time")public class Test { @RequestMapping("/session") public String getUserInfo(Map<String, Object> map) { map.put("time", new Date()); return "index"; } }
當訪問url爲 /test/session 時,會往服務器session中加入time屬性,其值爲當前時間(new Date())。
@ModelAttribute
在方法定義上使用 @ModelAttribute 註解:Spring MVC在調用目標處理方法前,會先逐個調用在方法級上標註了@ModelAttribute 的方法。
在方法的入參前使用 @ModelAttribute 註解:
可以從隱含對象中獲取隱含的模型數據中獲取對象,再將請求參數綁定到對象中,再傳入入參
將方法入參對象添加到模型中
@Controller @RequestMapping("/test") @SessionAttributes("time")public class Test { /** * 該方法會往隱含模型中添加一個名爲time的模型數據 * 注意:同一個瀏覽器同一段時間內,該函數只會被調用一次 */ @ModelAttribute("time") public Date getDate() { Date time = new Date(); System.out.println("getDate -- " + time); return time; } @RequestMapping("/model") public String getUserInfo(@ModelAttribute("time") Date time, Map<String, Object> map) { System.out.println(time); map.put("date", new Date()); return "index"; } }
3.1 由@SessionAttribute引發的異常
如果在處理類定義處標註了@SessionAttributes(“xxx”),則嘗試從會話中獲取該屬性,並將其賦給該入參,然後再用請求消息填充該入參對象。如果在會話中找不到對應的屬性,則拋出 HttpSessionRequiredException 異常。
所以,爲了避免發生異常,一般都會在請求到達handler方法前往數據模型(session域)中添加屬性,比如上面代碼中的public Date getDate()方法一樣。
4 視圖和視圖解析器
SpringMVC是如何解析視圖的呢?在handler方法返回String、ModelAndView或者View後,都會被SpringMVC內部轉換爲ModelAndView類型(如果handler方法爲void類型的,則該方法自己負責數據渲染和返回結果)。
Spring MVC 藉助視圖解析器(ViewResolver)得到最終的視圖對象(View),最終的視圖可以是 JSP ,也可能是Excel、JFreeChart 等各種表現形式的視圖。對於最終究竟採取何種視圖對象對模型數據進行渲染,handler並不關心,handler工作重點聚焦在生產模型數據的工作上,從而實現 MVC 的充分解耦。
4.1 視圖
視圖的作用是渲染模型數據,將模型裏的數據以某種形式呈現給客戶。SpringMVC中定義了一個View接口,源碼如下,視圖對象有視圖解析器負責實例化,是無狀態的,所以不會有線程安全問題。
public interface View { String RESPONSE_STATUS_ATTRIBUTE = View.class.getName() + ".responseStatus"; String PATH_VARIABLES = View.class.getName() + ".pathVariables"; String SELECTED_CONTENT_TYPE = View.class.getName() + ".selectedContentType"; String getContentType(); void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception; }
常用的視圖實現類:
4.2 視圖解析器
SpringMVC 爲邏輯視圖名的解析提供了不同的策略,可以在 Spring WEB 上下文中配置一種或多種解析策略,並指定他們之間的先後順序。每一種映射策略對應一個具體的視圖解析器實現類。視圖解析器的作用比較單一:將邏輯視圖解析爲一個具體的視圖對象。所有的視圖解析器都必須實現 ViewResolver 接口。常用的視圖解析器實現類如下所示:
可以選擇一種視圖解析器或混用多種視圖解析器,每個視圖解析器都實現了 Ordered 接口並開放出一個 order 屬性,可以通過 order 屬性指定解析器的優先順序,order 越小優先級越高。SpringMVC 會按視圖解析器順序的優先順序對邏輯視圖名進行解析,直到解析成功並返回視圖對象,否則將拋出 ServletException 異常。
InternalResourceViewResolver是最常用的視圖解析器,也是默認的視圖解析器,負責解析JSP視圖,使用方式如下所示:
<!-- ViewResolver 視圖解析器 如果沒有配置的話,則InternalResourceViewResolver就是默認的 --><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="order" value="1"/> <!-- ViewResolver優先級 --> <!-- 前綴 和 後綴 --> <property name="prefix" value="/"/> <property name="suffix" value=".jsp"/></bean>
4.3 關於重定向
一般情況下,控制器方法返回字符串類型的值會被當成邏輯視圖名處理。如果返回的字符串中帶 forward: 或 redirect: 前綴時,SpringMVC 會對他們進行特殊處理:將 forward: 和redirect: 當成指示符,其後的字符串作爲 URL 來處理。
redirect:success.jsp:會完成一個到 success.jsp 的重定向的操作
forward:success.jsp:會完成一個到 success.jsp 的轉發操作
@RequestMapping("/forward")public String getUserInfo(Map<String, Object> map) { //... return "redirect:index"; }
5 REST編程
實現rest風格的增刪改查,增刪改查的對象是Person類,源碼如下:
package com.luoxn28.attribute;import org.springframework.stereotype.Repository;import java.util.HashSet;import java.util.Set; @Repositorypublic class Person { String name; int age; public static Set<Person> persons = null; static { persons = new HashSet<Person>(); persons.add(new Person("aaa", 12)); persons.add(new Person("bbb", 16)); persons.add(new Person("ccc", 18)); persons.add(new Person("ddd", 24)); } public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
顯示Person、刪除Person、更新Person頁面list.jsp源碼如下:
<%@ page contentType="text/html;charset=UTF-8" language="java" %><%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %><html><head> <title>Title</title> <script type="text/javascript" src="/js/jquery-1.9.1.min.js"></script> <script type="text/javascript"> $(function(){ $(".delete").click(function(){ var href = $(this).attr("href"); $("form").attr("action", href).submit(); return false; }); }) </script></head><body> <form action="" method="POST"> <input type="hidden" name="_method" value="DELETE"/> </form> <c:forEach var="person" items="${persons}"> 姓名:${person.name} <br/> 年齡:${person.age} <br/> <a class="delete" href="/rest/person/${person.name}">刪除</a> <a href="/rest/person/${person.name}">更新</a> <hr/> </c:forEach></body></html>
list.jsp
添加Person頁面post.jsp源碼如下:
<%@ page contentType="text/html;charset=UTF-8" language="java" %><%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %><html><head> <title>Title</title></head><body> <form action="/rest/person" method="post"> 名字<input type="text" name="name" placeholder="名字"/> <br/> 年齡<input type="text" name="age" placeholder="年齡"/> <br/> <input type="submit" value="提交"/> </form></body></html>
post.jsp
Rest處理類源碼如下所示:
package com.luoxn28.rest;import com.luoxn28.attribute.Person;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import java.util.Iterator;import java.util.Map;import java.util.Set; @Controller @RequestMapping("/rest")public class Rest { // 顯示所用Person @RequestMapping("/persons") public String persons(Map<String, Set<Person>> map) { map.put("persons", Person.persons); return "list"; } // 添加Person @RequestMapping(value = "/person", method = RequestMethod.POST) public String personPost(Person person, Map<String, Set<Person>> map) { Person.persons.add(person); map.put("persons", Person.persons); return "list"; } // 刪除Person @RequestMapping(value = "/person/{name}", method = RequestMethod.DELETE) public String personDelete(@PathVariable("name") String name, Map<String, Set<Person>> map) { Iterator<Person> iter = Person.persons.iterator(); while (iter.hasNext()) { if (iter.next().getName().equals(name)) { iter.remove(); System.out.println("刪除成功 " + name); } } map.put("persons", Person.persons); return "redirect:/rest/persons"; } // 更新Person @RequestMapping(value = "/person/{name}", method = RequestMethod.GET) public String personUpdate(@PathVariable("name") String name, Map<String, Set<Person>> map) { Iterator<Person> iter = Person.persons.iterator(); while (iter.hasNext()) { Person person = iter.next(); if (person.getName().equals(name)) { person.setAge(0); System.out.println("更新成功 " + name); } } map.put("persons", Person.persons); return "redirect:/rest/persons"; } }
finally REST
REST:即 Representational State Transfer。(資源)表現層狀態轉化。是目前最流行的一種互聯網軟件架構。它結構清晰、符合標準、易於理解、擴展方便,所以正得到越來越多網站的採用。
資源(Resources):網絡上的一個實體,或者說是網絡上的一個具體信息。它可以是一段文本、一張圖片、一首歌曲、一種服務,總之就是一個具體的存在。可以用一個URI(統一資源定位符)指向它,每種資源對應一個特定的 URI 。要獲取這個資源,訪問它的URI就可以,因此 URI 即爲每一個資源的獨一無二的識別符。
表現層(Representation):把資源具體呈現出來的形式,叫做它的表現層(Representation)。比如,文本可以用 txt 格式表現,也可以用 HTML 格式、XML 格式、JSON 格式表現,甚至可以採用二進制格式。
狀態轉化(State Transfer):每發出一個請求,就代表了客戶端和服務器的一次交互過程。HTTP協議,是一個無狀態協議,即所有的狀態都保存在服務器端。因此,如果客戶端想要操作服務器,必須通過某種手段,讓服務器端發生“狀態轉化”(State Transfer)。而這種轉化是建立在表現層之上的,所以就是 “表現層狀態轉化”。具體說,就是 HTTP 協議裏面,四個表示操作方式的動詞:GET、POST、PUT、DELETE。它們分別對應四種基本操作:GET 用來獲取資源,POST 用來新建資源,PUT 用來更新資源,DELETE 用來刪除資源。
REST示例
– /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
詳細請參考源碼來源:minglisoft.cn/technology