1. 程式人生 > >spring @RequestMapping引數詳解

spring @RequestMapping引數詳解

package com.atguigu.springmvc.handlers;


import java.io.IOException;
import java.io.Writer;
import java.util.Arrays;
import java.util.Date;
import java.util.Map;


import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.servlet.ModelAndView;


@RequestMapping("/ads")
@Controller
public class SpringMVCTest {


private static final String SUCCESS = "success";

@RequestMapping("/testRedirect")
public String testRedirect(){
return "redirect:/index.jsp";
}

@RequestMapping("/testView")
public String testView(){
return "helloView";
}

@RequestMapping("/testViewAndViewResolver")
public String testViewAndViewResolver(){
return SUCCESS;
}

/**
* 1. 有 @ModelAttribute 標記的方法, 會在每個目標方法執行之前被 SpringMVC 呼叫! 
* 2. @ModelAttribute 註解也可以來修飾目標方法 POJO 型別的入參, 其 value 屬性值有如下的作用:
* 1). SpringMVC 會使用 value 屬性值在 implicitModel 中查詢對應的物件, 若存在則會直接傳入到目標方法的入參中.
* 2). SpringMVC 會一 value 為 key, POJO 型別的物件為 value, 存入到 request 中. 
*/
@ModelAttribute
public void getUser(@RequestParam(value="id",required=false) Integer id, 
Map<String, Object> map){
System.out.println("modelAttribute method");
if(id != null){
//模擬從資料庫中獲取物件
User user = new User(1, "Tom", "123456", "
[email protected]
", 12);
System.out.println("從資料庫中獲取一個物件: " + user);

map.put("user", user);
}
}

/**
* 執行流程:
* 1. 執行 @ModelAttribute 註解修飾的方法: 從資料庫中取出物件, 把物件放入到了 Map 中. 鍵為: user
* 2. SpringMVC 從 Map 中取出 User 物件, 並把表單的請求引數賦給該 User 物件的對應屬性.
* 3. SpringMVC 把上述物件傳入目標方法的引數. 

* 注意: 在 @ModelAttribute 修飾的方法中, 放入到 Map 時的鍵需要和目標方法入參型別的第一個字母小寫的字串一致!

* SpringMVC 確定目標方法 POJO 型別入參的過程
* 1. 確定一個 key:
* 1). 若目標方法的 POJO 型別的引數木有使用 @ModelAttribute 作為修飾, 則 key 為 POJO 類名第一個字母的小寫
* 2). 若使用了  @ModelAttribute 來修飾, 則 key 為 @ModelAttribute 註解的 value 屬性值. 
* 2. 在 implicitModel 中查詢 key 對應的物件, 若存在, 則作為入參傳入
* 1). 若在 @ModelAttribute 標記的方法中在 Map 中儲存過, 且 key 和 1 確定的 key 一致, 則會獲取到. 
* 3. 若 implicitModel 中不存在 key 對應的物件, 則檢查當前的 Handler 是否使用 @SessionAttributes 註解修飾, 
* 若使用了該註解, 且 @SessionAttributes 註解的 value 屬性值中包含了 key, 則會從 HttpSession 中來獲取 key 所
* 對應的 value 值, 若存在則直接傳入到目標方法的入參中. 若不存在則將丟擲異常. 
* 4. 若 Handler 沒有標識 @SessionAttributes 註解或 @SessionAttributes 註解的 value 值中不包含 key, 則
* 會通過反射來建立 POJO 型別的引數, 傳入為目標方法的引數
* 5. SpringMVC 會把 key 和 POJO 型別的物件儲存到 implicitModel 中, 進而會儲存到 request 中. 

* 原始碼分析的流程
* 1. 呼叫 @ModelAttribute 註解修飾的方法. 實際上把 @ModelAttribute 方法中 Map 中的資料放在了 implicitModel 中.
* 2. 解析請求處理器的目標引數, 實際上該目標引數來自於 WebDataBinder 物件的 target 屬性
* 1). 建立 WebDataBinder 物件:
* ①. 確定 objectName 屬性: 若傳入的 attrName 屬性值為 "", 則 objectName 為類名第一個字母小寫. 
* *注意: attrName. 若目標方法的 POJO 屬性使用了 @ModelAttribute 來修飾, 則 attrName 值即為 @ModelAttribute 
* 的 value 屬性值 

* ②. 確定 target 屬性:
* > 在 implicitModel 中查詢 attrName 對應的屬性值. 若存在, ok
* > *若不存在: 則驗證當前 Handler 是否使用了 @SessionAttributes 進行修飾, 若使用了, 則嘗試從 Session 中
* 獲取 attrName 所對應的屬性值. 若 session 中沒有對應的屬性值, 則丟擲了異常. 
* > 若 Handler 沒有使用 @SessionAttributes 進行修飾, 或 @SessionAttributes 中沒有使用 value 值指定的 key
* 和 attrName 相匹配, 則通過反射建立了 POJO 物件

* 2). SpringMVC 把表單的請求引數賦給了 WebDataBinder 的 target 對應的屬性. 
* 3). *SpringMVC 會把 WebDataBinder 的 attrName 和 target 給到 implicitModel. 
* 近而傳到 request 域物件中. 
* 4). 把 WebDataBinder 的 target 作為引數傳遞給目標方法的入參. 
*/
@RequestMapping("/testModelAttribute")
public String testModelAttribute(User user){
System.out.println("修改: " + user);
return SUCCESS;
}

/**
* @SessionAttributes 除了可以通過屬性名指定需要放到會話中的屬性外(實際上使用的是 value 屬性值),
* 還可以通過模型屬性的物件型別指定哪些模型屬性需要放到會話中(實際上使用的是 types 屬性值)

* 注意: 該註解只能放在類的上面. 而不能修飾放方法. 
*/
@RequestMapping("/testSessionAttributes")
public String testSessionAttributes(Map<String, Object> map){
User user = new User("Tom", "123456", "
[email protected]
", 15);
map.put("user", user);
map.put("school", "atguigu");
return SUCCESS;
}

/**
* 目標方法可以新增 Map 型別(實際上也可以是 Model 型別或 ModelMap 型別)的引數. 
* @param map
* @return
*/
@RequestMapping("/testMap")
public String testMap(Map<String, Object> map){
System.out.println(map.getClass().getName()); 
map.put("names", Arrays.asList("Tom", "Jerry", "Mike"));
return SUCCESS;
}

/**
* 目標方法的返回值可以是 ModelAndView 型別。 
* 其中可以包含檢視和模型資訊
* SpringMVC 會把 ModelAndView 的 model 中資料放入到 request 域物件中. 
* @return
*/
@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView(){
String viewName = SUCCESS;
ModelAndView modelAndView = new ModelAndView(viewName);

//新增模型資料到 ModelAndView 中.
modelAndView.addObject("time", new Date());

return modelAndView;
}

/**
* 可以使用 Serlvet 原生的 API 作為目標方法的引數 具體支援以下型別

* HttpServletRequest 
* HttpServletResponse 
* HttpSession
* java.security.Principal 
* Locale InputStream 
* OutputStream 
* Reader 
* Writer
* @throws IOException 
*/
@RequestMapping("/testServletAPI")
public void testServletAPI(HttpServletRequest request,
HttpServletResponse response, Writer out) throws IOException {
System.out.println("testServletAPI, " + request + ", " + response);
out.write("hello springmvc");
//return SUCCESS;
}


/**
* Spring MVC 會按請求引數名和 POJO 屬性名進行自動匹配, 自動為該物件填充屬性值。支援級聯屬性。
* 如:dept.deptId、dept.address.tel 等
*/
@RequestMapping("/testPojo")
public String testPojo(User user) {
System.out.println("testPojo: " + user);
return SUCCESS;
}


/**
* 瞭解:

* @CookieValue: 對映一個 Cookie 值. 屬性同 @RequestParam
*/
@RequestMapping("/testCookieValue")
public String testCookieValue(@CookieValue("JSESSIONID") String sessionId) {
System.out.println("testCookieValue: sessionId: " + sessionId);
return SUCCESS;
}


/**
* 瞭解: 對映請求頭資訊 用法同 @RequestParam
*/
@RequestMapping("/testRequestHeader")
public String testRequestHeader(
@RequestHeader(value = "Accept-Language") String al) {
System.out.println("testRequestHeader, Accept-Language: " + al);
return SUCCESS;
}


/**
* @RequestParam 來對映請求引數. value 值即請求引數的引數名 required 該引數是否必須. 預設為 true
*               defaultValue 請求引數的預設值
*/
@RequestMapping(value = "/testRequestParam")
public String testRequestParam(
@RequestParam(value = "username") String un,
@RequestParam(value = "age", required = false, defaultValue = "0") int age) {
System.out.println("testRequestParam, username: " + un + ", age: "
+ age);
return SUCCESS;
}


/**
* Rest 風格的 URL. 以 CRUD 為例: 新增: /order POST 修改: /order/1 PUT update?id=1 獲取:
* /order/1 GET get?id=1 刪除: /order/1 DELETE delete?id=1

* 如何傳送 PUT 請求和 DELETE 請求呢 ? 1. 需要配置 HiddenHttpMethodFilter 2. 需要傳送 POST 請求
* 3. 需要在傳送 POST 請求時攜帶一個 name="_method" 的隱藏域, 值為 DELETE 或 PUT

* 在 SpringMVC 的目標方法中如何得到 id 呢? 使用 @PathVariable 註解

*/
@RequestMapping(value = "/testRest/{id}", method = RequestMethod.PUT)
public String testRestPut(@PathVariable Integer id) {
System.out.println("testRest Put: " + id);
return SUCCESS;
}


@RequestMapping(value = "/testRest/{id}", method = RequestMethod.DELETE)
public String testRestDelete(@PathVariable Integer id) {
System.out.println("testRest Delete: " + id);
return SUCCESS;
}


@RequestMapping(value = "/testRest", method = RequestMethod.POST)
public String testRest() {
System.out.println("testRest POST");
return SUCCESS;
}


@RequestMapping(value = "/testRest/{id}", method = RequestMethod.GET)
public String testRest(@PathVariable Integer id) {
System.out.println("testRest GET: " + id);
return SUCCESS;
}


/**
* @PathVariable 可以來對映 URL 中的佔位符到目標方法的引數中.
* @param id
* @return
*/
@RequestMapping("/testPathVariable/{id}")
public String testPathVariable(@PathVariable("id") Integer id) {
System.out.println("testPathVariable: " + id);
return SUCCESS;
}


@RequestMapping("/testAntPath/*/abc")
public String testAntPath() {
System.out.println("testAntPath");
return SUCCESS;
}


/**
* 瞭解: 可以使用 params 和 headers 來更加精確的對映請求. params 和 headers 支援簡單的表示式.

* @return
*/
@RequestMapping(value = "testParamsAndHeaders", params = { "username",
"age!=10" }, headers = { "Accept-Language=en-US,zh;q=0.8" })
public String testParamsAndHeaders() {
System.out.println("testParamsAndHeaders");
return SUCCESS;
}


/**
* 常用: 使用 method 屬性來指定請求方式
*/
@RequestMapping(value = "/testMethod", method = RequestMethod.POST)
public String testMethod() {
System.out.println("testMethod");
return SUCCESS;
}


/**
* 1. @RequestMapping 除了修飾方法, 還可來修飾類 2. 1). 類定義處: 提供初步的請求對映資訊。相對於 WEB 應用的根目錄
* 2). 方法處: 提供進一步的細分對映資訊。 相對於類定義處的 URL。若類定義處未標註 @RequestMapping,則方法處標記的 URL
* 相對於 WEB 應用的根目錄
*/
@RequestMapping("/testRequestMapping")
public String testRequestMapping() {
System.out.println("testRequestMapping");
return SUCCESS;
}


}