前言

上一节我们在SpringBoot中启用了Spring MVC最终输出了HelloWorld,本节我们来讲讲Spring MVC中的模型绑定,这个名称来源于.NET或.NET Core,不知是否恰当,我们暂且这样理解吧。

@RequestParam VS  @PathVariable

一看注解名称应该非常好理解,注解@RequestParam主要用来获取查询字符串参数,而注解@PathVaruable用于获取路由参数,下面我们来看如下一个例子:

    @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可不提供,则为其默认值,比如如下:

我们知道无论是注解@RequestParam还是注解@PathVariable,都有属性required,若为false,则此参数无需提供,难道事实真的如此吗,我们看看如下示例:

    @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。

深入探讨注解@RequestParam

上述是我们针对路由和查询字符串注解的对比,接下来我们来看看对于查询字符串注解各种姿势,看看Spring是如何进行处理的呢,比如我们有两个根据注解@RequestParam的请求参数和方法一样,此时将会发生什么呢?,如下:

    @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方法进行如下修改即可(参数顺序可颠倒):

    @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其名称与其一致,否则将抛出异常。其他的地方我们只需要了解对应使用规则没有什么难点,好了,本节的内容我们到此为止,下一节我们陆续开始于数据库打交道,感谢您的阅读,我们下节见。

Spring MVC系列之模型绑定(SpringBoot)(七)的更多相关文章

  1. Spring MVC中的模型数据处理

    一.综述 Spring MVC 提供了以下途径来输出模型数据: 1.ModelAndView 当处理方法返回值类型为 ModelAndView时, 方法体即可通过该对象添加模型数据到请求域. 2.Ma ...

  2. ASP.NET MVC学习之模型绑定(1)

    一.前言 下面我们将开始学习模型绑定,通过下面的知识我们将能够理解ASP.NET MVC模型的模型绑定器是如何将http请求中的数据转换成模型的,其中我们重点讲述的是表单数据. 二.正文 1.简单类型 ...

  3. [转]ASP.NET MVC 4 (九) 模型绑定

    本文转自:http://www.cnblogs.com/duanshuiliu/p/3706701.html 模型绑定指的是MVC从浏览器发送的HTTP请求中为我们创建.NET对象,在HTTP请求和C ...

  4. Asp.net Mvc 中的模型绑定

    asp.net mvc中的模型绑定可以在提交http请求的时候,进行数据的映射. 1.没有模型绑定的时候 public ActionResult Example0() { ) { string id ...

  5. 【Spring MVC系列】--(4)返回JSON

    [Spring MVC系列]--(4)返回JSON 摘要:本文主要介绍如何在控制器中将数据生成JSON格式并返回 1.导入包 (1)spring mvc 3.0不需要任何其他配置,添加一个jackso ...

  6. .NET MVC学习之模型绑定

    ASP.NET MVC学习之模型绑定(2)   继ASP.NET MVC学习之模型绑定继续 3.手工调用模型绑定 很多情况下我们都是通过形参的方式接收来自http流中的数据,这看似是完美的,但是缺少了 ...

  7. Spring mvc系列一之 Spring mvc简单配置

    Spring mvc系列一之 Spring mvc简单配置-引用 Spring MVC做为SpringFrameWork的后续产品,Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块 ...

  8. ASP.NET MVC 4 (九) 模型绑定

    模型绑定指的是MVC从浏览器发送的HTTP请求中为我们创建.NET对象,在HTTP请求和C#间起着桥梁的作用.模型绑定的一个最简单的例子是带参数的控制器action方法,比如我们注册这样的路径映射: ...

  9. spring mvc(4)处理模型数据

    处理模型数据 Spring MVC 提供了以下几种途径输出模型数据: – ModelAndView: 处理方法返回值类型为 ModelAndView时, 方法体即可通过该对象添加 模型数据 – Map ...

随机推荐

  1. oracle避免使用耗费资源的操作

    带有DISTINCT,UNION,MINUS,INTERSECT,ORDER BY的SQL语句会启动SQL引擎 执行耗费资源的排序(SORT)功能. DISTINCT需要一次排序操作, 而其他的至少需 ...

  2. Cisco 交换机笔记

    最近使用 Cisco L3(C3560X), L2(2960) 交换机搭建了 VLAN 环境,其中包括了 VLAN 的配置, VLAN 间的路由等,在此写篇笔记记录下. VLAN 结构 L3 Swit ...

  3. poj 1920 Towers of Hanoi

    Towers of Hanoi Time Limit: 3000MS   Memory Limit: 16000K Total Submissions: 2213   Accepted: 986 Ca ...

  4. zoj 2954 Hanoi Tower

    Hanoi Tower Time Limit: 2 Seconds Memory Limit: 65536 KB You all must know the puzzle named "Th ...

  5. [转载] 虚拟机3种网络模式(NAT, Host-only, Bridged)

    实例讲解虚拟机3种网络模式(桥接.nat.Host-only) 转载自:http://www.cnblogs.com/ggjucheng/archive/2012/08/19/2646007.html ...

  6. springboot activiti工作流简单示例

    最近一直研究springboot,根据工作需求,工作流需要作为一个单独的微服务工程来提供给其他服务调用,现在简单的写下工作流(使用的activiti)微服务的搭建与简单使用 jdk:1.8 数据库:m ...

  7. springboot jpa 解决延迟加载问题

    在springboot中,在application.properties的配置文件中新增spring.jpa.open-in-view=true方法失效,经过测试,有两种解决办法: 1.在applic ...

  8. CSS滤镜 :灰色 ,方便站点哀悼

    html {  -webkit-filter: grayscale(100%); -moz-filter: grayscale(100%); -ms-filter: grayscale(100%); ...

  9. urlencode()与urldecode()

    urlencode()函数原理就是首先把中文字符转换为十六进制,然后在每个字符前面加一个标识符%. urldecode()函数与urlencode()函数原理相反,用于解码已编码的 URL 字符串,其 ...

  10. 【js】Vue 2.5.1 源码学习 (八)响应式入口observe

    大体思路(七) 本节内容: deps 依赖收集的数组对象 => Dep 构造函数 /** ==> observe() * var ob * ==> if --isObject * = ...