天天看點

SpringMVC 參數綁定注解解析

本文介紹了用于參數綁定的相關注解。

綁定:将請求中的字段按照名字比對的原則填入模型對象。

SpringMVC就跟Struts2一樣,通過攔截器進行參數比對。

代碼在 https://github.com/morethink/MySpringMVC

URI模闆變量

這裡指uri template中variable(路徑變量),不含queryString部分

@PathVariable

當使用

@RequestMapping

URI template 樣式映射時, 即 someUrl/{paramId}, 這時的paramId可通過

@Pathvariable

注解綁定它傳過來的值到方法的參數上。

示例代碼:

@RestController
@RequestMapping("/users")
public class UserAction {

    @GetMapping("/{id}")
    public Result getUser(@PathVariable int id) {
        return ResultUtil.successResult("123456");
    }

}
           

上面代碼把URI template 中變量 ownerId的值和petId的值,綁定到方法的參數上。若方法參數名稱和需要綁定的uri template中變量名稱不一緻,需要在@PathVariable("name")指定uri template中的名稱。

請求頭

@RequestHeader

@RequestHeader

注解,可以把Request請求header部分的值綁定到方法的參數上。

這是一個Request 的header部分:

Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding:gzip, deflate, br
Accept-Language:zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
Cache-Control:max-age=0
Connection:keep-alive
Host:localhost:8080
Upgrade-Insecure-Requests:1
User-Agent:Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.62 Safari/537.36
           
@GetMapping("/getRequestHeader")
public Result getRequestHeader(@RequestHeader("Accept-Encoding") String encoding) {
    return ResultUtil.successResult(encoding);
}
           

上面的代碼,把request header部分的 Accept-Encoding的值,綁定到參數encoding上。

@CookieValue

可以把Request header中關于cookie的值綁定到方法的參數上。

例如有如下Cookie值:

JSESSIONID=588DC770E582A3189B7E6210102EAE02

參數綁定的代碼:

@RequestMapping("/getCookie")
public Result getCookie(@CookieValue("JSESSIONID") String cookie) {
    return ResultUtil.successResult(cookie);
}
           

即把JSESSIONID的值綁定到參數cookie上。

請求體

@RequestParam

  • 常用來處理簡單類型的綁定,通過Request.getParameter() 擷取的String可直接轉換為簡單類型的情況( String--> 簡單類型的轉換操作由ConversionService配置的轉換器來完成);因為使用request.getParameter()方式擷取參數,是以可以處理get 方式中queryString的值,也可以處理post方式中 body data的值;
  • 用來處理Content-Type: 為

    application/x-www-form-urlencoded

    編碼的内容,送出方式GET、POST;
  • 該注解有兩個屬性: value、required; value用來指定要傳入值的id名稱,required用來訓示參數是否必須綁定;
@GetMapping("/tesRequestParam")
public Result tesRequestParam(@RequestParam("username") String username) {
    return ResultUtil.successResult(username);
}
           

@RequestBody

該注解常用來處理Content-Type: 不是application/x-www-form-urlencoded編碼的内容,例如application/json, application/xml等;

它是通過使用HandlerAdapter 配置的HttpMessageConverters來解析post data body,然後綁定到相應的bean上的。

因為配置有FormHttpMessageConverter,是以也可以用來處理 application/x-www-form-urlencoded的内容,處理完的結果放在一個MultiValueMap<String, String>裡,這種情況在某些特殊需求下使用,詳情檢視FormHttpMessageConverter api;

@PostMapping("/tesRequestBody")
public Result tesRequestBody(@RequestBody User user) {

    return ResultUtil.successResult(user);
}
           

結果截圖:

SpringMVC 參數綁定注解解析

@RequestBody

通過list接收對象數組

在我們傳遞對象的時候,無論

Content-Type

x-www-form-urlencoded

還是

application/json

其實沒有多大的關系,可是當我們需要傳遞對象數組的時候,表單編碼就不行了,這時我們是可以采用json傳遞,然後背景使用

@RequestBody

注解,通過list接收來對象數組。

前端代碼:

index.html

//打開頁面時運作
$(document).ready(function () {
    var users = [];
    var user1 = {"username": "dd", "password": "123"};
    var user2 = {"username": "gg", "password": "123"};
    users.push(user1);
    users.push(user2);
    $.ajax({
        type: "POST",
        url: "users/saveUsers",
        timeout: 30000,
        dataType: "json",
        contentType: "application/json",
        data: JSON.stringify(users),
        success: function (data) {
            //将傳回的資料展示成table
            showTable(data);
        },
        error: function () { //請求出錯的處理
            $("#result").text("請求出錯");
        }
    });
});
           

背景代碼:

@PostMapping("saveUsers")
public Result saveUsers(@RequestBody List<User> users) {
    return ResultUtil.successResult(users);
}
           
SpringMVC 參數綁定注解解析

@SessionAttribute

該注解用來綁定HttpSession中的attribute對象的值,便于在方法中的參數裡使用。該注解有value、types兩個屬性,可以通過名字和類型指定要使用的attribute 對象

@PostMapping("/setSessionAttribute")
public Result setSessionAttribute(HttpSession session, String attribute) {
    session.setAttribute("attribute", attribute);
    return ResultUtil.SUCCESS_RESULT;
}

@GetMapping("/getSessionAttribute")
public Result getSessionAttribute(@SessionAttribute("attribute") String attribute) {
    return ResultUtil.successResult(attribute);
}
           

我們首先給session添加一個attribute,然後再取出這個attribute。

SpringMVC 參數綁定注解解析

@ModelAttribute

@ModelAttribute标注可被應用在方法或方法參數上。

方法使用@ModelAttribute标注

标注在方法上的

@ModelAttribute

說明方法是用于添加一個或多個屬性到model上。這樣的方法能接受與

@RequestMapping

标注相同的參數類型,隻不過不能直接被映射到具體的請求上。

在同一個控制器中,标注了

@ModelAttribute

的方法實際上會在

@RequestMapping

方法之前被調用。

以下是示例:

// Add one attribute
// The return value of the method is added to the model under the name "account"
// You can customize the name via @ModelAttribute("myAccount")

@ModelAttribute
public Account addAccount(@RequestParam String number) {
    return accountManager.findAccount(number);
}

// Add multiple attributes

@ModelAttribute
public void populateModel(@RequestParam String number, Model model) {
    model.addAttribute(accountManager.findAccount(number));
    // add more ...
}
           

@ModelAttribute

方法通常被用來填充一些公共需要的屬性或資料,比如一個下拉清單所預設的幾種狀态,或者寵物的幾種類型,或者去取得一個HTML表單渲染所需要的指令對象,比如Account等。

@ModelAttribute标注方法有兩種風格:

  • 在第一種寫法中,方法通過傳回值的方式預設地将添加一個屬性;
  • 在第二種寫法中,方法接收一個Model對象,然後可以向其中添加任意數量的屬性。

可以在根據需要,在兩種風格中選擇合适的一種。

一個控制器可以擁有多個

@ModelAttribute

方法。同個控制器内的所有這些方法,都會在

@RequestMapping

@ModelAttribute

方法也可以定義在

@ControllerAdvice

标注的類中,并且這些

@ModelAttribute

可以同時對許多控制器生效。

屬性名沒有被顯式指定的時候又當如何呢?在這種情況下,架構将根據屬性的類型給予一個預設名稱。舉個例子,若方法傳回一個Account類型的對象,則預設的屬性名為"account"。可以通過設定@ModelAttribute标注的值來改變預設值。當向Model中直接添加屬性時,請使用合适的重載方法addAttribute(..)-即帶或不帶屬性名的方法。

@ModelAttribute标注也可以被用在

@RequestMapping

方法上。這種情況下,

@RequestMapping

方法的傳回值将會被解釋為model的一個屬性,而非一個視圖名,此時視圖名将以視圖命名約定來方式來确定。

方法參數使用

@ModelAttribute

标注

标注在方法參數上的

@ModelAttribute

說明了該方法參數的值将由model中取得。如果model中找不到,那麼該參數會先被執行個體化,然後被添加到model中。在model中存在以後,請求中所有名稱比對的參數都會填充到該參數中。

這在Spring MVC中被稱為資料綁定,一個非常有用的特性,我們不用每次都手動從表格資料中轉換這些字段資料。

@PostMapping
public Result saveUser(@ModelAttribute User user) {
    return ResultUtil.successResult(user);
}
           

以上面的代碼為例,這個User類型的執行個體可能來自哪裡呢?有幾種可能:

  • 它可能因為

    @SessionAttributes

    标注的使用已經存在于model中
  • 它可能因為在同個控制器中使用了

    @ModelAttribute

    方法已經存在于model中,正如上一小節所叙述的
  • 它可能是由URI模闆變量和類型轉換中取得的
  • 它可能是調用了自身的預設構造器被執行個體化出來的

@ModelAttribute方法常用于從資料庫中取一個屬性值,該值可能通過

@SessionAttributes

标注在請求中間傳遞。在一些情況下,使用URI模闆變量和類型轉換的方式來取得一個屬性是更友善的方式。

在不給定注解的情況下,參數是怎樣綁定的?

通過分析

AnnotationMethodHandlerAdapter

RequestMappingHandlerAdapter

的源代碼發現,方法的參數在不給定參數的情況下:

  • 若要綁定的對象時簡單類型:調用

    @RequestParam

    來處理的。

    這裡的簡單類型指Java的原始類型(boolean, int 等)、原始類型對象(Boolean, Int等)、String、Date等ConversionService裡可以直接String轉換成目标對象的類型。也就是說沒有特别需求,不推薦使用

    @RequestParam

  • 若要綁定的對象時複雜類型:調用

    @ModelAttribute

    來處理的。也就是說如果不需要從model或者session中得到資料,@ModelAttribute可以不使用。

@RequestMapping

支援的方法參數

下面這些參數Spring在調用請求方法的時候會自動給它們指派,是以當在請求方法中需要使用到這些對象的時候,可以直接在方法上給定一個方法參數的申明,然後在方法體裡面直接用就可以了。

  1. HttpServlet 對象,主要包括HttpServletRequest 、HttpServletResponse 和HttpSession 對象。 但是有一點需要注意的是在使用HttpSession 對象的時候,如果此時HttpSession 對象還沒有建立起來的話就會有問題。
  2. Spring 自己的WebRequest 對象。 使用該對象可以通路到存放在HttpServletRequest 和HttpSession 中的屬性值。
  3. InputStream 、OutputStream 、Reader 和Writer 。 InputStream 和Reader 是針對HttpServletRequest 而言的,可以從裡面取資料;OutputStream 和Writer 是針對HttpServletResponse 而言的,可以往裡面寫資料。
  4. 使用

    @PathVariable

    @RequestParam

    @CookieValue

    @RequestHeader

    标記的參數。
  5. @ModelAttribute

  6. java.util.Map 、Spring 封裝的Model 和ModelMap 。 這些都可以用來封裝模型資料,用來給視圖做展示。
  7. 實體類。 可以用來接收上傳的參數。
  8. Spring 封裝的MultipartFile 。 用來接收上傳檔案的。
  9. Spring 封裝的Errors 和BindingResult 對象。 這兩個對象參數必須緊接在需要驗證的實體對象參數之後,它裡面包含了實體對象的驗證結果。

一個參數傳多個值

在浏覽器輸入此URL

http://localhost:8080/admin/login.action?username=geek&password=geek&password=geek

結果得到的對象為 :

Manager{username='geek', password='geek,geek'}

參考文檔:

  1. @RequestParam @RequestBody @PathVariable 等參數綁定注解詳解
  2. SpringMVC之Controller常用注解功能全解析
  3. @ModelAttribute使用詳解