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,可以前往下载。