前言
SpringMVC是一个非常流行的Java Web框架,它提供了很多方便的功能和工具来帮助我们构建高效、灵活的Web应用程序。其中,数据绑定就是SpringMVC中非常重要的一部分,它可以帮助我们方便地将请求参数绑定到Java对象上,从而简化了我们的开发流程。
在本篇博客中,我们将深入探讨SpringMVC中的数据绑定机制,包括如何定义绑定规则、如何处理绑定错误等内容。希望本文能够对您理解SpringMVC的数据绑定机制有所帮助。
一、什么是数据绑定
数据绑定是一种编程技术,用于将数据模型的值自动地与用户界面元素(如文本框、标签、下拉菜单等)进行同步。当数据模型中的值发生变化时,这些变化会自动地反映在用户界面元素中,反之亦然。数据绑定可以减少开发人员的工作量,提高应用程序的可维护性和可重用性。
数据绑定可以分为单向绑定和双向绑定两种类型。单向绑定是指只有在数据模型中的值发生变化时,才会更新用户界面元素的值。而双向绑定则是指在数据模型中的值发生变化时,不仅会更新用户界面元素的值,同时也会将用户界面元素中的值更新到数据模型中。
二、前期准备
1、新建项目,结构如下
2、添加依赖
<dependencies>
<!-- springmvc 依赖,会将spring的核心包一并添加进来 -->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.23</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.5</version>
</dependency>
</dependencies>
3、配置 web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
用于配置 Servlet 的映射和加载。在 Spring MVC 中,它用于配置 DispatcherServlet 的初始化和请求映射。
具体来说,这段配置的作用如下:
- 定义了一个名为 "dispatcher" 的 Servlet,并指定了 org.springframework.web.servlet.DispatcherServlet 作为其处理类。
- 设置了 load-on-startup 属性为 1,表示在应用启动时就加载该 Servlet。
- 使用 <servlet-mapping> 元素将 "dispatcher" Servlet 映射到所有的请求路径上(即 <url-pattern>/</url-pattern>),意味着所有的请求都会经过该 Servlet 进行处理。
这段配置的作用是将所有的请求交给 DispatcherServlet 处理,并让它成为应用的核心控制器。DispatcherServlet 将根据请求的 URL 和其他配置信息,将请求分发给相应的处理器方法进行处理,然后返回响应结果。
4、新建并配置 dispacther-servlet.xml 文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:mcv="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 启用扫描 -->
<context:component-scan base-package="edu.nf.ch04"/>
<!-- mvc 注解驱动 -->
<mvc:annotation-driven/>
<!-- 默认 servlet 处理静态资源 -->
<mcv:default-servlet-handler/>
<!-- 内部资源视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
这段 XML 配置的作用如下:
- <context:component-scan>:用于扫描指定包下的组件,并将其注册为 Spring 容器中的 Bean。这样可以自动发现和管理这些组件,使它们可以在应用程序中被使用。
- <mvc:annotation-driven>:启用 MVC 注解驱动,使得 Spring MVC 可以处理注解相关的请求映射、参数绑定、数据转换等操作。通过这个配置,你可以使用注解来定义控制器、请求映射、请求参数等,简化了开发过程。
- <mvc:default-servlet-handler>:启用默认的静态资源处理器。当有静态资源请求时(如 CSS、JS 文件),Spring MVC 将不会处理这些请求,而是将其交给容器的默认 Servlet 处理。这样可以提高性能并减少不必要的开销。
- <mvc:resources>:将指定路径下的静态资源映射到指定的 URL 路径上。在这个例子中,mapping="page/**" 表示将以 /page/ 开头的 URL 路径映射到静态资源,location="/static/" 表示静态资源的存放路径为 /static/。这样配置后,当访问以 /page/ 开头的 URL 路径时,Spring MVC 会将对应的静态资源返回给客户端。
- <bean>:定义了一个内部资源视图解析器 InternalResourceViewResolver,用于将逻辑视图名称解析为具体的 JSP 视图路径。prefix 属性表示 JSP 文件的前缀路径,suffix 属性表示 JSP 文件的后缀名。通过这个配置,可以简化控制器中返回视图的处理过程。
这段配置主要是为了实现 Spring MVC 的基本功能,包括组件扫描、注解驱动、静态资源处理和视图解析等。它们共同协作,使得开发者可以更方便地开发和管理 Spring MVC 应用程序。
5、在 entity 包下新建三个实体类 User、Card、address
User
/**
* @Date 2023-10-20
* @Author qiu
* 映射到实体的时候,字段名与请求中的 name 保持一致
*
*/
@Data
public class User {
private String userName;
private Integer age;
private Date birth;
private List<String> tel;
// 一对一关联
private Card card;
// 一对多关联
private List<Address> addresses;
}
Card
@Data
public class Card {
private String cardNum;
}
Address
@Data
public class Address {
private String addr;
}
二、把数据保存到作用域中
1、使用原生 servlet
@Controller
@Slf4j
public class UserController {
@PostMapping("/add")
public ModelAndView add(HttpServletRequest request){
String userName = request.getParameter("username");
String age = request.getParameter("age");
log.info(userName + " "+ age);
// 将参数当如请求作用域中
request.setAttribute("username",userName);
request.setAttribute("age",age);
return new ModelAndView("index");
}
}
是一个基于Spring MVC框架的UserController类,其中包含了一个add()方法,用于处理HTTP POST请求。在该方法中,使用HttpServletRequest对象获取了请求参数(即用户名和年龄),并将它们记录在日志中。
接着,使用HttpServletRequest对象将这些参数放入请求作用域中,以便在返回视图时能够在页面上显示这些参数。最后,使用ModelAndView对象将逻辑视图名("index")返回给前端控制器DispatcherServlet,以便DispatcherServlet能够找到对应的视图并进行渲染。
2、通过 modelAndView 将参数放入请求作用域
@Controller
@Slf4j
public class UserController {
@PostMapping("/add")
public ModelAndView add(HttpServletRequest request){
String userName = request.getParameter("username");
String age = request.getParameter("age");
log.info(userName + " "+ age);
// 通过 modelAndView 将参数放入请求作用域
ModelAndView index = new ModelAndView("index");
index.addObject("username",userName);
index.addObject("age",age);
return index;
}
}
创建了一个ModelAndView对象,将逻辑视图名("index")作为构造函数的参数传入。然后,使用ModelAndView的addObject()方法将用户名和年龄作为键值对添加到ModelAndView对象中,以便在返回视图时能够在页面上显示这些参数。
3、运行效果
1)、在 index.html 页面中完成一个提交表单
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>添加用户</h1>
<form action="../add2" method="post">
Name:<input type="text" name="username" value="user"><br>
Age:<input type="text" name="age" value="2000"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
注意:这个 name 定义了以后,在控制器中就是获取这个 name 来获取它的 value 的。
2)在 index.jsp 中获取 index.html 提交的数据
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>首页</h1>
用户名:${requestScope.username}<br>
年龄:${requestScope.age}<br>
</body>
</html>
3) 运行效果
4)使用原生 servlet 保存数据到作用域和 modelAndView 将参数放入请求作用域有什么区别
在使用原生 Servlet 时,可以通过不同的方式将数据保存到作用域中,包括请求作用域、会话作用域和应用程序作用域。对于保存数据到作用域的不同方式,以及将参数放入请求作用域和使用 modelAndView 的区别,可以进行如下解释:
-
请求作用域:
- 使用
request.setAttribute(String name, Object value)
方法将数据保存到请求作用域中。 - 请求作用域的生命周期仅限于一次请求,当请求完成后,作用域中的数据会被销毁。
- 请求作用域适合在同一个请求中的多个 Servlet 或 JSP 页面之间共享数据。
- 使用
-
modelAndView:
- modelAndView 是一种模式,通常与 MVC(Model-View-Controller)架构一起使用。
- 在 modelAndView 中,数据被封装在一个特定的对象中,该对象包含模型数据和视图信息。
- 模型数据是指需要传递给视图显示的数据,而视图信息则指定了要显示的视图的名称或路径。
- modelAndView 可以通过方法参数或返回值来传递,并且可以在控制器中进行处理和操作。
- modelAndView 适合在 MVC 架构中,将处理逻辑和数据展示分离,提供更好的代码组织和可维护性。
区别:
- 生命周期:请求作用域的生命周期仅限于一次请求,而 modelAndView 可以在多个请求之间传递数据。
- 使用方式:请求作用域需要手动将数据保存到作用域中,而 modelAndView 是通过方法参数或返回值来传递数据。
- MVC 架构:modelAndView 通常与 MVC 架构一起使用,更适合于大型应用程序的开发和维护。
- 数据封装:modelAndView 将数据封装在一个对象中,提供了更好的数据组织和管理。
总结: 如果只是在同一个请求中传递数据,可以使用请求作用域;如果是在 MVC 架构中进行数据传递和视图展示,可以选择使用 modelAndView。具体选择哪种方式取决于项目需求和架构设计。
三、将请求数据直接绑定到参数上
1、使用默认参数名
@PostMapping("/add2")
public ModelAndView add2(String userName , Integer userAge,
String[] tel, Date birth
){
// 通过 modelAndView 将参数放入请求作用域
ModelAndView index = new ModelAndView("index");
// 将参数保存到请求作用域
index.addObject("username",userName);
index.addObject("age",userAge);
index.addObject("tel1",tel[0]);
index.addObject("tel2",tel[1]);
index.addObject("birth",birth);
return index;
}
该方法将接收到的参数数据保存到请求作用域中,并将请求重定向到名为 "index" 的视图。
2、使用 @RequestParam 注解
@PostMapping("/add2")
public ModelAndView add2(@RequestParam(value = "username" ,required = true,defaultValue = "aaaa") String userName , @RequestParam("age") Integer userAge,
String[] tel, Date birth
){
// 通过 modelAndView 将参数放入请求作用域
ModelAndView index = new ModelAndView("index");
// 将参数保存到请求作用域
index.addObject("username",userName);
index.addObject("age",userAge);
index.addObject("tel1",tel[0]);
index.addObject("tel2",tel[1]);
index.addObject("birth",birth);
return index;
}
将请求数据直接绑定到参数上,默认参数名与请求中的 name 保持一致即可映射。否则使用 @RequestParam 注解
- required = true:必须提交
- defaultValue = "aaaa":设置默认值
1) 这里需要对日期时间进行处理
/**
* 注册自定义转换器,@InitBinder 注解标注的方法会在执行
* 任何 controller 的方法之前先执行,spring 会传入
* 一个 webBinder 的参数,使用这个参数可以注册任意的 Formatter
* @param binder 数据绑定器,用于注册各种格式化类
*/
@InitBinder
public void regFormatter(WebDataBinder binder){
binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd"));
}
该
regFormatter
方法用于注册一个自定义的日期格式化器,将日期字符串按照格式 "yyyy-MM-dd" 进行解析和格式化。这样,在处理请求时,如果遇到需要将日期字符串转换为Date
类型的情况,就会使用该自定义的格式化器进行处理。
3 运行效果
1)在 index.html 中完成提交
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>添加用户</h1>
<form action="../add" method="post">
Name:<input type="text" name="username" value="user"><br>
Age:<input type="text" name="age" value="2000"><br>
Tel1:<input type="text" name="tel" value="123123213213"><br>
Tel2:<input type="text" name="tel" value="1231231233"><br>
brith:<input type="text" name="birth" value="2000-05-03"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
2) 在 index.jsp 获取 index.html 提交的数据
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>首页</h1>
用户名:${requestScope.username}<br>
年龄:${requestScope.age}<br>
电话1:${requestScope.tel1}<br>
电话2:${requestScope.tel2}<br>
生日:${requestScope.birth}<br>
</body>
</html>
3)运行
四、获取 list 集合中的数据
1、完成控制器
@PostMapping("/add3")
public ModelAndView add3(User user){
// 通过 modelAndView 将参数放入请求作用域
ModelAndView index = new ModelAndView("index");
// 将参数保存到请求作用域
index.addObject("username",user.getUserName());
index.addObject("age",user.getAge());
index.addObject("tel1",user.getTel().get(0));
index.addObject("tel2",user.getTel().get(1));
index.addObject("birth",user.getBirth());
index.addObject("cardNum",user.getCard().getCardNum());
log.info(user.getCard().getCardNum() + "========");
index.addObject("addr1",user.getAddresses().get(0).getAddr());
index.addObject("addr2",user.getAddresses().get(1).getAddr());
return index;
}
// 一对多关联
private List<Address> addresses; 实体类封装的是一个集合类型的实体,所这里获取就通过 addresses.get(0).getAddr();取获取第一个参数,以此类推。
2、在 index.html 中完成提交
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>添加用户</h1>
<form action="../add2" method="post">
Name:<input type="text" name="username" value="user"><br>
Age:<input type="text" name="age" value="2000"><br>
Tel1:<input type="text" name="tel" value="123123213213"><br>
Tel2:<input type="text" name="tel" value="1231231233"><br>
brith:<input type="text" name="birth" value="2000-05-03"><br>
idCard:<input type="text" name="card.cardNum" value="440990191929293939"><br>
addr1:<input type="text" name="addresses[0].addr" value="珠海"><br>
addr2:<input type="text" name="addresses[1].addr" value="霞山"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
需要使用对象嵌套和集合嵌套的方式来处理。在表单元素中,可以通过
[0]
、[1]
等索引来访问集合中的元素,如果有多个号码,也可以通过.tel1
、.tel2
等属性名来访问对象中的属性。
3、在 index.jsp 中获取 index.html 提交的数据
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>首页</h1>
用户名:${requestScope.username}<br>
年龄:${requestScope.age}<br>
电话1:${requestScope.tel1}<br>
电话2:${requestScope.tel2}<br>
生日:${requestScope.birth}<br>
身份证:${requestScope.cardNum}<br>
住址1:${requestScope.addr1}<br>
住址2:${requestScope.addr2}<br>
</body>
</html>
4、运行效果
五、路径参数绑定
1、完成控制器
/**
* 路径参数绑定
* 请求格式:/url地址/(变量)
* 并且使用 @PathVariable 注解
* @param uid
* @return
*/
@GetMapping("/user/{id}")
public ModelAndView getUser(@PathVariable("id") String uid){
String aa = "";
ModelAndView mav = new ModelAndView("index");
mav.addObject("uid",uid);
return mav;
}
创建了一个ModelAndView对象,并将逻辑视图名("index")作为构造函数的参数传入。然后,使用ModelAndView的addObject()方法将路径变量"uid"添加到ModelAndView对象中,以便在返回视图时能够在页面上显示该参数。
@PathVariable注解的value属性用于指定路径变量名,如果方法参数名和路径变量名一致,则可以省略value属性。另外,@PathVariable注解还有其他可选属性,例如required、defaultValue等,用于指定路径变量是否必需,以及默认值等。
2、在 index.jsp 中获取路径传递的参数
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>首页</h1>
路径参数:${requestScope.uid}<br>
</body>
</html>
3、运行效果
六、总结
参数绑定是指将HTTP请求中的参数值绑定到方法的参数上,以便在方法中使用这些参数进行业务逻辑处理。在Spring MVC框架中,参数绑定可以通过多种方式实现,包括使用@RequestParam注解、使用@PathVariable注解、使用HttpServletRequest对象等。
总结参数绑定的几个关键点如下:
-
@RequestParam注解:用于将请求参数绑定到方法的参数上。可以指定参数名、是否必需、默认值等属性。适用于GET和POST请求。
-
@PathVariable注解:用于将路径变量绑定到方法的参数上。可以指定路径变量名,并通过@PathVariable注解的value属性指定参数名。适用于RESTful风格的URL。
-
HttpServletRequest对象:可以通过HttpServletRequest对象获取请求参数。适用于需要对请求参数进行更加复杂的处理或访问其他请求相关信息的情况。
参数绑定的优势在于简化了开发过程,避免了手动解析请求参数的繁琐操作。同时,参数绑定也提供了一定的灵活性,可以根据具体业务需求选择不同的参数绑定方式。
需要注意的是,在进行参数绑定时,应该考虑安全性和可靠性。对于用户输入的参数,应该进行合法性验证和防御性编程,以避免潜在的安全漏洞和错误。此外,还应该注意参数类型的匹配和转换,确保参数能够正确地绑定到方法的参数上。
总而言之,参数绑定是Spring MVC框架中非常重要的功能之一,可以方便地获取和处理HTTP请求中的参数值,提高开发效率和代码可读性。在使用参数绑定时,需要注意安全性和可靠性,并根据具体业务需求选择合适的参数绑定方式。