Spring MVC系列之模型綁定(SpringBoot)(七)

前言

上一節咱們在SpringBoot中啓用了Spring MVC最終輸出了HelloWorld,本節咱們來說講Spring MVC中的模型綁定,這個名稱來源於.NET或.NET Core,不知是否恰當,咱們暫且這樣理解吧。javascript

@RequestParam VS  @PathVariable

 一看註解名稱應該很是好理解,註解@RequestParam主要用來獲取查詢字符串參數,而註解@PathVaruable用於獲取路由參數,下面咱們來看以下一個例子:css

    @ResponseBody
    @RequestMapping(value = "/demo1", method = RequestMethod.GET)
    public String demo1(@RequestParam(value = "param1", required = true, defaultValue = "jeffcky") String param1,
                        @RequestParam(value = "param2", required = false) String param2) {
        return param1 + "," + param2;
    }

 

如上咱們獲取查詢字符串參數param1和param2,同時呢,咱們要求參數param1必須提供,若爲空,咱們給定默認值爲jeffcky,而參數param2可不提供,則爲其默認值,好比以下:html

咱們知道不管是註解@RequestParam仍是註解@PathVariable,都有屬性required,若爲false,則此參數無需提供,難道事實真的如此嗎,咱們看看以下示例:java

    @ResponseBody
    @RequestMapping(value = "/demo3/{id}", method = RequestMethod.GET)
    public String demo3(@RequestParam(value = "id") String param1, @PathVariable(value = "id", required = false) String param2) {
        return param1 + "," + param2;
    }

咱們設置了路由上的變量id爲可選,當咱們請求時咱們也並未提供該參數,可是結果倒是404,這也證實:註解@RequestParam獲取查詢字符串,而註解@PathVariable獲取路由參數,雖然兩者註解提供參數(required)可選,可是針對註解@PathVariable該參數無效,並且參數必須提供,不然返回404。jquery

深刻探討註解@RequestParam

上述是咱們針對路由和查詢字符串註解的對比,接下來咱們來看看對於查詢字符串註解各類姿式,看看Spring是如何進行處理的呢,好比咱們有兩個根據註解@RequestParam的請求參數和方法同樣,此時將會發生什麼呢?,以下:spring

    @RequestMapping(value = "/user", method = RequestMethod.GET)
    @ResponseBody
    public String say() {
        return "hello world";
    }

    @RequestMapping(value = "/user", method = RequestMethod.GET)
    public ModelAndView user() {
        User user = new User();
        user.setGender("M");
        ModelAndView modelAndView = new ModelAndView("user", "command", user);
        return modelAndView;
    }

很顯然會啓動程序後會拋出上述異常,意爲不明確有兩個相同的映射,那麼要是咱們將say方法的請求方法給去掉,此時將代表可此方法的請求不受限制,如此這樣會報錯嗎?如若不報錯,那麼會首先匹配到哪一個呢?數據庫

    @RequestMapping(value = "/user")
    @ResponseBody
    public String say() {
        return "hello world";
    }

    @RequestMapping(value = "/user", method = RequestMethod.GET)
    public ModelAndView user() {
        User user = new User();
        user.setGender("M");
        ModelAndView modelAndView = new ModelAndView("user", "command", user);
        return modelAndView;
    }

由此咱們能夠知道:註解@RequestParam用於查詢字符串,若請求參數一致, 但一個請求方法未指定(接收全部方法),一個指定對應請求方法,結果將匹配到指定對應的請求方法。那麼要是咱們想讓其匹配到say方法,咱們應該腫麼辦呢?註解@RequestParam的參數爲數組,接下來咱們將say方法進行以下修改便可(參數順序可顛倒):bootstrap

    @RequestMapping(value = {"/user", "*"})
    @ResponseBody
    public String say() {
        return "hello world";
    }

@ModelAttribute VS ModelMap VS ModelAndView

既然涉及到參數綁定,那麼咱們就得學習Spring MVC表單提交,順着這個思路咱們來學習Spring MVC表單相關內容,如上圖其實我已經給你們展現了對應方法和視圖,接下來咱們來經過表單提交來說講三者的區別,爲了有些童鞋可能須要親自動手實踐,這裏咱們先給出整個結構,以下:數組

咱們建立以下進行表單提交的用戶實體類瀏覽器

package com.demo.springboot.model;

public class User {
    private String firstName;
    private String lastName;
    private String gender;
    private String email;
    private String userName;
    private String password;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

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

接下來咱們須要在控制器文件目錄下建立UserController控制器,而後呢,咱們返回用戶視圖,以下方法:

    @RequestMapping(value = "/user", method = RequestMethod.GET)
    public ModelAndView user() {
        User user = new User();
        user.setGender("M");
        ModelAndView modelAndView = new ModelAndView("user", "command", user);
        return modelAndView;
    }

在前面內容咱們經過字符串的形式返回的視圖,而後加上經過配置文件中視圖存放位置和加上後綴查找視圖,可是利用ModelAndView纔是最友好的方式,經過其名稱添加模型和返回視圖應該就很清楚了,就像.NET MVC中的View方法同樣,咱們能夠返回模型數據,同時指定視圖名稱是一個道理。上述咱們設置了性別的默認值爲字符串M,咱們暫且先說到這裏,待會還要回過頭再次進行講解的,接下來咱們去建立user.jsp,以下:

<%@ page language="java" contentType="text/html;" pageEncoding="utf-8" %>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
<html>

<head>
    <title>Spring MVC Form</title>
    <link href="/static/css/bootstrap.min.css" rel="stylesheet">
    <link href="/static/css/bootstrap-theme.min.css" rel="stylesheet">
</head>

<body>
<div class="container">
    <div class="col-md-offset-2 col-md-7">
        <h2 class="text-center">Spring MVC 5 Form</h2>
        <div class="panel panel-info">
            <div class="panel-heading">
                <div class="panel-title">Sign Up</div>
            </div>
            <div class="panel-body">
                <form:form action="addUser" class="form-horizontal"
                           method="post" modelAttribute="user">

                    <div class="form-group">
                        <label for="firstName" class="col-md-3 control-label">First
                            Name</label>
                        <div class="col-md-9">
                            <form:input path="firstName" class="form-control"/>
                        </div>
                    </div>
                    <div class="form-group">
                        <label for="lastName" class="col-md-3 control-label">Last
                            Name</label>
                        <div class="col-md-9">
                            <form:input path="lastName" class="form-control"/>
                        </div>
                    </div>
                    <div class="form-group">
                        <form:label path="gender" class="col-md-3 control-label">gender</form:label>
                        <div class="col-md-9">
                            <form:radiobutton path="gender" value="M" label="Male"/>
                            <form:radiobutton path="gender" value="F" label="Female"/>
                        </div>
                    </div>
                    <div class="form-group">
                        <label for="userName" class="col-md-3 control-label">User
                            Name </label>
                        <div class="col-md-9">
                            <form:input path="userName" class="form-control"/>
                        </div>
                    </div>

                    <div class="form-group">
                        <label for="password" class="col-md-3 control-label">Password</label>
                        <div class="col-md-9">
                            <form:password path="password" class="form-control"/>
                        </div>
                    </div>
                    <div class="form-group">
                        <label for="email" class="col-md-3 control-label">Email</label>
                        <div class="col-md-9">
                            <form:input path="email" class="form-control"/>
                        </div>
                    </div>
                    <div class="form-group">
                        <div class="col-md-offset-3 col-md-9">
                            <form:button class="btn btn-primary">Submit</form:button>
                        </div>
                    </div>

                </form:form>
            </div>
        </div>
    </div>
</div>
<script type="text/javascript" src="/static/js/jquery.min.js"></script>
<script type="text/javascript" src="/static/js/bootstrap.min.js"></script>
</body>
</html>

首先咱們必須在其頂部添加以下這一行代表咱們要使用spring framework框架中的表單,且前綴爲form:

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

在spring framework框架中對於表單中各個標籤的使用經過冒號隔開,以下:

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

最終在瀏覽器中將渲染成HTML標籤,仍是很是簡單,這裏咱們只是稍微過一下,沒有太多複雜的東西,咱們只要知道規則便可

<input name="firstName" type="text" value=""/>
<input name="lastName" type="text" value=""/>

咱們在後臺方法中返回模型即user,最終利用spring框架將模型上的屬性綁定到表單標籤上,那麼接下來咱們提交表單後,咱們在後臺怎麼獲取到模型數據呢,其實咱們在spring框架的表單標籤上定義了屬性modelAttribute,以下:

接下來咱們在後臺接收表單中的數據,以下:

    @RequestMapping(value = "/addUser", method = RequestMethod.POST)
    public String addUser(@ModelAttribute("user") User user,
                          ModelMap model) {
        model.addAttribute("firstName", user.getFirstName());
        model.addAttribute("lastName", user.getLastName());
        model.addAttribute("email", user.getEmail());
        model.addAttribute("userName", user.getUserName());
        model.addAttribute("password", user.getPassword());
        return "users";
    }

在後臺咱們一樣經過註解@ModelAttribute("user")與表單上定義的屬性modelAttribute="user"匹配,從而獲取數據,最終將獲取到的數據添加到ModelMap中,並返回視圖users.jsp中,以下:

<%@ page language="java" contentType="text/html;" pageEncoding="utf-8" %>
<%@taglib uri = "http://www.springframework.org/tags/form" prefix = "form"%>
<html>

<head>
    <title>Spring MVC Form Handling</title>
</head>

<body>
<h2>Submitted User Information</h2>
<table>
    <tr>
        <td>firstName</td>
        <td>${firstName}</td>
    </tr>
    <tr>
        <td>lastName</td>
        <td>${lastName}</td>
    </tr>
    <tr>
        <td>Email</td>
        <td>${email}</td>
    </tr>
    <tr>
        <td>userName</td>
        <td>${userName}</td>
    </tr>
    <tr>
        <td>Password</td>
        <td>${password}</td>
    </tr>
    <tr>
    </tr>
</table>
</body>
</html>

當咱們啓動程序時發現報錯了,根據咱們一路的解釋,彷佛沒有任何毛病,這是何緣故, 上述異常大概是代表特性user出了問題,其實緣由出在後臺獲取用戶視圖上,以下這一行上:

ModelAndView modelAndView = new ModelAndView("user", "command", user);

如上構造函數中的第一個參數表明要渲染的視圖名稱,而第三個參數是在視圖中要綁定的模型,那麼第二個參數是個什麼鬼呢?通常狀況下這個值都會默認設置成command,據查資料這個字符串在spring框架是一個常量,那麼它的做用是什麼呢?我我的猜想若是默認設置成該值,那麼就表明註解@ModelAttribute的參數值就是實體類名稱,好比咱們實體類爲User,那麼默認ModelAttribute參數名就是user,咱們無需在上述表單上指定modelAttribute的值,即便指定爲user也會拋出上述異常。若是在表單上顯式定義了modelAttribute的值,那麼在實例化模型視圖類時,第二個參數必須與其值相等,咱們將上述command修改成user便可解決問題,不信的話,你能夠試試。同時在接收表單提交的參數時,通過測試,後臺的註解@ModelAttribute可去除參數名稱也可綁定。最終根據咱們填寫表單的內容和渲染結果,以下:

 

上述咱們是經過ModelAndView返回模型和視圖,這只是實現方式之一,其餘實現將又會引來問題,接下來咱們直接返回該模型,此時將以該模型名稱做爲做爲表單上屬性modelAttribute的名稱,此時必須顯式設置modelAttribute="user",以下:

    @RequestMapping(value = "/user", method = RequestMethod.GET)
    public User user() {
        User user = new User();
        user.setGender("M");
        user.setFavorites(new String[]{"乒乓球", "羽毛球", "檯球"});
        return user;
    }

如若在上述表單上沒有顯式設置modelAttribute="user"且值不能爲其餘值,不然將拋出以下異常:

 

若咱們想將上述表單上的屬性modelAttribute的值user,設置爲其餘值,好比modelAttribute="User",此時必須在該視圖對應方法上經過註解@ModelAttribute顯式設置名稱爲User,不然一樣將拋出上述異常,以下:

    @ModelAttribute("User")
    @RequestMapping(value = "/user", method = RequestMethod.GET)
    public User user() {
        User user = new User();
        user.setGender("M");
        user.setFavorites(new String[]{"乒乓球", "羽毛球", "檯球"});
        return user;
    }

到此爲止咱們學習到了ModelAndView用來設置模型和視圖名稱,而註解@ModelAttribute則是控制器和視圖綁定數據的橋樑,該註解既可做爲方法參數接收視圖模型數據,也可修改控制器方法將模型數據綁定到視圖。ModelMap則是映射模型數據,固然也支持集合和合並特性等等。咱們再來演示經過對方法進行註解@ModelAttribute,而後綁定到視圖中,在User類中咱們再定義一個屬性country,以下:

    private String country;
 
    public String getCountry() {
        return country;
    }

    public void setCountry(String country) {
        this.country = country;
    }

在控制器中經過註解@ModelAttribute定義國家元數據,並綁定到視圖上,以下:

    @ModelAttribute("countryList")
    public Map<String, String> getCountryList() {
        Map<String, String> countryList = new HashMap<>();
        countryList.put("CHI", "中國");
        countryList.put("CH", "英國");
        countryList.put("SG", "新加坡");
        return countryList;
    }
<div class="form-group">
   <label for="country" class="col-md-3 control-label">Country</label>
        <div class="col-md-9">
            <form:select path = "country">
                 <form:option value = "無" label = "請選擇"/>
                 <form:options items = "${countryList}" />
            </form:select>
         </div>
 </div>

在視圖中咱們經過$符號渲染數據,固然我麼也能夠在視圖中寫Java代碼,這和.NET或.NET Core中的Razor視圖同樣,只不過在JSP中經過<%  代碼 %>來寫代碼,咱們一樣也來演示下,在User中再定義一個數組,以下:

    private String[] favorites;

    public  String[] getFavorites(){
        return favorites;
    }

    public void setFavorites(String[] favorites) {
        this.favorites = favorites;
    }

在獲取user.jsp視圖對應後臺方法中,咱們設置上述定義的愛好列表默認值,以下:

user.setFavorites(new String[]{"乒乓球", "羽毛球", "檯球"});

同時在控制器中咱們定義愛好列表,而後綁定到視圖中,對默認設置的愛好列表經過checkbox進行選中

    @ModelAttribute("favorites")
    public Object[] getfavoriteList() {
        List<String> favorites = new ArrayList<>();
        favorites.add("足球");
        favorites.add("乒乓球");
        favorites.add("羽毛球");
        favorites.add("檯球");
        return favorites.toArray();
    }

再在user.jsp視圖中,添加對愛好列表數據的綁定和選中,以下:

<div class="form-group">
    <label for="favorites" class="col-md-3 control-label">Favorites</label>
       <div class="col-md-9">
          <form:checkboxes class="f" items = "${favorites}" path = "favorites" />
       </div>
 </div>

而後在獲取提交表單的方法中,獲取上述咱們添加的城市和選中的愛好列表,以下:

最後在渲染提交表單的視圖users.jsp中,當獲取愛好列表時,此時經過代碼的形式進行渲染,以下:

 <tr>
        <td>Country</td>
        <td>${country}</td>
    </tr>
    <tr>
        <td>
            <% String[] favorites = (String[])request.getAttribute("favorites");
                for(String favorite: favorites) {
                    out.println(favorite);
                }
            %>
        </td>
    </tr>

 

總結 

本節咱們詳細分析了在Spring MVC中對於參數的綁定,最須要注意的是在模型綁定到視圖中的問題,這裏咱們下個結論: 若經過ModelAndView設置模型和視圖時,若此時第二個參數爲command,則表單上的屬性modelAttribute默認值爲模型名稱,此時若顯式設置值爲模型名稱將拋出異常,若顯式設置該屬性的值不爲模型名稱,此時須要將command設置與其同樣,不然將拋出異常。若直接返回模型時,此時表單上的屬性modelAttribute的值必須顯式設置爲模型名稱,不然將拋出異常,若表單上的屬性modelAttribute值不爲模型名稱時,此時必須在該視圖對應方法上顯式設置註解@ModelAttribute其名稱與其一致,不然將拋出異常。其餘的地方咱們只須要了解對應使用規則沒有什麼難點,好了,本節的內容咱們到此爲止,下一節咱們陸續開始於數據庫打交道,感謝您的閱讀,咱們下節見。

相關文章
相關標籤/搜索