上一节咱们在SpringBoot中启用了Spring MVC最终输出了HelloWorld,本节咱们来说讲Spring MVC中的模型绑定,这个名称来源于.NET或.NET Core,不知是否恰当,咱们暂且这样理解吧。javascript
一看注解名称应该很是好理解,注解@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
上述是咱们针对路由和查询字符串注解的对比,接下来咱们来看看对于查询字符串注解各类姿式,看看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"; }
既然涉及到参数绑定,那么咱们就得学习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其名称与其一致,不然将抛出异常。其余的地方咱们只须要了解对应使用规则没有什么难点,好了,本节的内容咱们到此为止,下一节咱们陆续开始于数据库打交道,感谢您的阅读,咱们下节见。