1 概述
验证可以避免用户输入不规范的数据,在数据过滤的一道门。这道门有前端验证和后台验证,前端验证使用JavaScript脚本,后台验证使用Java EE验证规范JSR 303。
2 工程结构
为避免反复手动启动程序,我们引入热启动。
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.1.6.RELEASE
com.wangshenghua
spring-thymeleaf-form
0.0.1-SNAPSHOT
spring-thymeleaf-form
Demo project for Spring Boot
<properties>
<java.version>1.8</java.version>
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
spring-boot-devtools模块可以让工程是源码和配置变更后自动重启嵌入式tomcat。
3 模型层
JSR 303验证规则放置于数据模型层,以注释的形式加入。
User.java
package com.wangshenghua.model;
import javax.validation.constraints.Email;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
public class User {
@NotNull
private long id;
@NotNull
@Size(min = 2, max = 30,message="姓名在2~30个字符之间")
private String name;
@NotNull
@Min(message="年龄至少15岁",value=15)
private Integer age;
@NotEmpty
@Email(message="邮箱格式不对")
private String email;
public User() {
}
public User(@NotNull long id, @NotNull @Size(min = 2, max = 30) String name, @NotNull @Min(15) Integer age,
String email) {
this.id = id;
this.name = name;
this.age = age;
this.email = email;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
常见的验证注释有:
验证规则 说明
@Null 被注释的元素必须为 null
@NotNull 被注释的元素必须不为 null
@AssertTrue 被注释的元素必须为 true
@AssertFalse 被注释的元素必须为 false
@Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max, min) 被注释的元素的大小必须在指定的范围内
@Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
@Past 被注释的元素必须是一个过去的日期
@Future 被注释的元素必须是一个将来的日期
@Pattern(value) 被注释的元素必须符合指定的正则表达式
@Email 被注释的元素必须是电子邮箱地址
@Length 被注释的字符串的大小必须在指定的范围内
@NotEmpty 被注释的字符串的必须非空
@Range 被注释的元素必须在合适的范围内
4 数据访问层
数据访问层采用List模拟数据库。
UserDao.java
package com.wangshenghua.dao;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Repository;
import com.wangshenghua.model.User;
@Repository
public class UserDao {
private static List<User> users = new ArrayList<User>();
// 使用List当容器保存数据,来代替数据库
static {
users.add(new User(1, "黄志燕", 17, "ssss@qq.com"));
users.add(new User(2, "侯慧聪", 19, "hhhh@qq.com"));
users.add(new User(3, "王广宏", 15, "gggg@qq.com"));
}
public long getMaxId() {
long id = 0;
for (User user : users) {
if (user.getId() > id)
id = user.getId();
}
return id + 1;
}
public User getUser(long id) {
User user = null;
for (User u : users) {
if (u.getId() == id) {
user = u;
break;
}
}
return user;
}
public void update(User user, long id) {
for (User u : users) {
if (u.getId() == id) {
u.setName(user.getName());
u.setEmail(user.getEmail());
u.setAge(user.getAge());
break;
}
}
}
public void delete(long id) {
for (User u : users) {
if (u.getId() == id) {
users.remove(u);
break;
}
}
}
public List<User> getUsers() {
return users;
}
}
@Repository注释表示此类是一个数据访问对象dao。Spring对各MVC各层注释有着规范的要求:
控制器层 @Controller
服务层 @Service
数据访问层 @Repository
在类前加入这些注释后,Spring工程一启动,扫描这些有注释的类,并实例化到内存里。
5 控制器
UserController.java
package com.wangshenghua.controller;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import com.wangshenghua.dao.UserDao;
import com.wangshenghua.model.User;
@Controller
public class UserController {
@Autowired
private UserDao userDao;
/** 控制器的方法 **/
@GetMapping("/")
public String index() {
return "redirect:/allUser";
}
@GetMapping("/allUser")
public String allUser(Model model) {
model.addAttribute("users", userDao.getUsers());
return "list-user";
}
@GetMapping("/adduser")
public String showAddUserForm(Model model) {
User user = new User();
user.setId(userDao.getMaxId());
model.addAttribute("user", user);
return "add-user";
}
@PostMapping("/adduser")
public String addUser(@Valid User user, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "add-user";
}
userDao.getUsers().add(user);
return "redirect:/allUser";
}
@GetMapping("/edit/{id}") // {id}是占位符
public String showUpdateForm(@PathVariable("id") long id, Model model) { // @PathVariable 路径变量
User user = userDao.getUser(id);
model.addAttribute("user", user);
return "update-user";
}
@PostMapping("/update/{id}")
public String updateUser(@PathVariable("id") long id, @Valid User user, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "update-user";
}
userDao.update(user, id);
return "redirect:/allUser";
}
@GetMapping("/delete/{id}")
public String deleteUser(@PathVariable("id") long id, Model model) {
userDao.delete(id);
return "redirect:/allUser";
}
}
@Autowired注释将内存里已经实例好的对象引入进来,并赋值给变量。
private UserDao userDao; 如变量userDao指向一个引用,引用内存中的UserDao实例。
这就是Spring中著名的控制反转和依赖注入。之前是由驱动类UserController负责实例化UserDao;现在Spring不是这样玩了,由Spring容器在启动时扫描类上的注释,并实例化到内存,控制权不在驱动类手里,在Spring手里;驱动类要依赖某一个类,只需写注释@Autowired声明一个变量就OK。
在方法addUser里有一个参数User user前,有一个注释@Valid,用于启动数据模型User.java类中JSR 303验证规则,它将对视图层传递过来的User对象进行验证。
验证结果又放置到BindingResult对象里,BindingResult的hasErrors()方法发现有错误,则返回到视图层。
6 视图层
add-user.html
<p th:if="${#fields.hasErrors('name')}" th:errors="*{name}"></p>
<label for="age">年龄</label>
<input type="text" th:field="*{age}" id="age" placeholder="age">
<p th:if="${#fields.hasErrors('age')}" th:errors="*{age}"></p>
<label for="email">邮箱</label>
<input type="text" th:field="*{email}" id="email" placeholder="Email">
<p th:if="${#fields.hasErrors('email')}" th:errors="*{email}"></p>
<input type="submit" value="保存">
</form>
视图层采用thymeleaf技术,它先判断th:if="${#fields.hasErrors('name')}" 如果有错误,则显示错误消息th:errors="*{name}"
7 总结
本节课程使用Spring Boot和thymeleaf技术实现表单验证功能 world程序。
演示了如何在数据模型层User.java中添加JSR 303验证规则,及在控制器如何启用验证,及视图层如何展示验证的消息提示。
同时简要介绍了Spring的控制反转和依赖注释的使用。
学员位重点掌握表单验证和控制反转和依赖注释的使用。
本节课程源码已经上传到github,可以前往下载。