SpringMVC介绍之Validation
对于任何一个应用而言在客户端做的数据有效性验证都不是安全有效的,这时候就要求我们在开发的时候在服务端也对数据的有效性进行验证。SpringMVC自身对数据在服务端的校验有一个比较好的支持,它能将我们提交到服务端的数据按照我们事先的约定进行数据有效性验证,对于不合格的数据信息SpringMVC会把它保存在错误对象中,这些错误信息我们也可以通过SpringMVC提供的标签在前端JSP页面上进行展示。
使用Validator接口进行验证
在SpringMVC中提供了一个Validator接口,我们可以通过该接口来定义我们自己对实体对象的验证。接下来看一个示例。
假设我们现在有一个需要进行验证的实体类User,其代码如下所示:
- public class User {
- private String username;
- private String password;
- 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;
- }
- public String toString() {
- return username + ", " + password;
- }
- }
那么当我们需要使用SpringMVC提供的Validator接口来对该实体类进行校验的时候该如何做呢?这个时候我们应该提供一个Validator的实现类,并实现Validator接口的supports方法和validate方法。Supports方法用于判断当前的Validator实现类是否支持校验当前需要校验的实体类,只有当supports方法的返回结果为true的时候,该Validator接口实现类的validate方法才会被调用来对当前需要校验的实体类进行校验。这里假设我们需要验证User类的username和password都不能为空,先给出其代码,稍后再进行解释。这里我们定义一个UserValidator,其代码如下:
- import org.springframework.validation.Errors;
- import org.springframework.validation.ValidationUtils;
- import org.springframework.validation.Validator;
- public class UserValidator implements Validator {
- public boolean supports(Class<?> clazz) {
- // TODO Auto-generated method stub
- return User.class.equals(clazz);
- }
- public void validate(Object obj, Errors errors) {
- // TODO Auto-generated method stub
- ValidationUtils.rejectIfEmpty(errors, "username", null, "Username is empty.");
- User user = (User) obj;
- if (null == user.getPassword() || "".equals(user.getPassword()))
- errors.rejectValue("password", null, "Password is empty.");
- }
- }
在上述代码中我们在supports方法中定义了该UserValidator只支持对User对象进行校验。在validate方法中我们校验了User对象的username和password不为empty的情况,这里的empty包括null和空字符串两种情况。ValidationUtils类是Spring中提供的一个工具类。Errors就是Spring用来存放错误信息的对象。
我们已经定义了一个对User类进行校验的UserValidator了,但是这个时候UserValidator还不能对User对象进行校验,因为我们还没有告诉Spring应该使用UserValidator来校验User对象。在SpringMVC中我们可以使用DataBinder来设定当前Controller需要使用的Validator。先来看下面一段代码:
- import javax.validation.Valid;
- import org.springframework.stereotype.Controller;
- import org.springframework.validation.BindingResult;
- import org.springframework.validation.DataBinder;
- import org.springframework.web.bind.annotation.InitBinder;
- import org.springframework.web.bind.annotation.RequestMapping;
- @Controller
- public class UserController {
- @InitBinder
- public void initBinder(DataBinder binder) {
- binder.setValidator(new UserValidator());
- }
- @RequestMapping("login")
- public String login(@Valid User user, BindingResult result) {
- if (result.hasErrors())
- return "redirect:user/login";
- return "redirect:/";
- }
- }
在上面这段代码中我们可以看到我们定义了一个UserController,该Controller有一个处理login操作的处理器方法login,它需要接收客户端发送的一个User对象,我们就是要利用前面的UserValidator对该User对象进行校验。首先我们可以看到我们login方法接收的参数user是用@Valid进行标注的,这里的@Valid是定义在JSR-303标准中的,我这里使用的是Hibernate Validation对它的实现。这里我们必须使用@Valid标注我们需要校验的参数user,否则Spring不会对它进行校验。另外我们的处理器方法必须给定包含Errors的参数,这可以是Errors本身,也可以是它的子类BindingResult,使用了Errors参数就是告诉Spring关于表单对象数据校验的错误将由我们自己来处理,否则Spring会直接抛出异常,而且这个参数是必须紧挨着@Valid参数的,即必须紧挨着需要校验的参数,这就意味着我们有多少个@Valid参数就需要有多少个对应的Errors参数,它们是一一对应的。前面有提到我们可以通过DataBinder来指定需要使用的Validator,我们可以看到在上面代码中我们通过@InitBinder标记的方法initBinder设置了当前Controller需要使用的Validator是UserValidator。这样当我们请求处理器方法login时就会使用DataBinder设定的UserValidator来校验当前的表单对象User,首先会通过UserValidator的supports方法判断其是否支持User对象的校验,若支持则调用UserValidator的validate方法,并把相关的校验信息存放到当前的Errors对象中。接着我们就可以在我们的处理器方法中根据是否有校验异常信息来做不同的操作。在上面代码中我们定义了在有异常信息的时候就跳转到登陆页面。这样我们就可以在登陆页面上通过errors标签来展示这些错误信息了。
我们知道在Controller类中通过@InitBinder标记的方法只有在请求当前Controller的时候才会被执行,所以其中定义的Validator也只能在当前Controller中使用,如果我们希望一个Validator对所有的Controller都起作用的话,我们可以通过WebBindingInitializer的initBinder方法来设定了。另外,在SpringMVC的配置文件中通过mvc:annotation-driven的validator属性也可以指定全局的Validator。代码如下所示:
- <?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"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
- http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context-3.0.xsd
- http://www.springframework.org/schema/mvc
- http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
- <mvc:annotation-driven validator="userValidator"/>
- <bean id="userValidator" class="com.xxx.xxx.UserValidator"/>
- ...
- </beans>
使用JSR-303 Validation进行验证
JSR-303是一个数据验证的规范,这里我不会讲这个规范是怎么回事,只会讲一下JSR-303在SpringMVC中的应用。JSR-303只是一个规范,而Spring也没有对这一规范进行实现,那么当我们在SpringMVC中需要使用到JSR-303的时候就需要我们提供一个对JSR-303规范的实现,Hibernate Validator是实现了这一规范的,这里我将以它作为JSR-303的实现来讲解SpringMVC对JSR-303的支持。
JSR-303的校验是基于注解的,它内部已经定义好了一系列的限制注解,我们只需要把这些注解标记在需要验证的实体类的属性上或是其对应的get方法上。来看以下一个需要验证的实体类User的代码:
- import javax.validation.constraints.Min;
- import javax.validation.constraints.NotNull;
- import org.hibernate.validator.constraints.NotBlank;
- public class User {
- private String username;
- private String password;
- private int age;
- @NotBlank(message="用户名不能为空")
- public String getUsername() {
- return username;
- }
- public void setUsername(String username) {
- this.username = username;
- }
- @NotNull(message="密码不能为null")
- public String getPassword() {
- return password;
- }
- public void setPassword(String password) {
- this.password = password;
- }
- @Min(value=10, message="年龄的最小值为10")
- public int getAge() {
- return age;
- }
- public void setAge(int age) {
- this.age = age;
- }
- }
我们可以看到我们在username、password和age对应的get方法上都加上了一个注解,这些注解就是JSR-303里面定义的限制,其中@NotBlank是Hibernate Validator的扩展。不难发现,使用JSR-303来进行校验比使用Spring提供的Validator接口要简单的多。我们知道注解只是起到一个标记性的作用,它是不会直接影响到代码的运行的,它需要被某些类识别到才能起到限制作用。使用SpringMVC的时候我们只需要把JSR-303的实现者对应的jar包放到classpath中,然后在SpringMVC的配置文件中引入MVC Namespace,并加上<mvn:annotation-driven/>就可以非常方便的使用JSR-303来进行实体对象的验证。加上了<mvn:annotation-driven/>之后Spring会自动检测classpath下的JSR-303提供者并自动启用对JSR-303的支持,把对应的校验错误信息放到Spring的Errors对象中。这时候SpringMVC的配置文件如下所示:
- <?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"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
- http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context-3.0.xsd
- http://www.springframework.org/schema/mvc
- http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
- <mvc:annotation-driven/>
- </beans>
接着我们来定义一个使用User对象作为参数接收者的Controller,其代码如下所示:
- import javax.validation.Valid;
- import org.springframework.stereotype.Controller;
- import org.springframework.validation.BindingResult;
- import org.springframework.web.bind.annotation.RequestMapping;
- @Controller
- public class UserController {
- @RequestMapping("login")
- public String login(@Valid User user, BindingResult result) {
- if (result.hasErrors())
- return "user/login";
- return "redirect:/";
- }
- }
这样当我们不带任何参数请求login.do的时候就不能通过实体对象User的属性数据有效性限制,然后会把对应的错误信息放置在当前的Errors对象中。
JSR-303原生支持的限制有如下几种:
限制 |
说明 |
@Null |
限制只能为null |
@NotNull |
限制必须不为null |
@AssertFalse |
限制必须为false |
@AssertTrue |
限制必须为true |
@DecimalMax(value) |
限制必须为一个不大于指定值的数字 |
@DecimalMin(value) |
限制必须为一个不小于指定值的数字 |
@Digits(integer,fraction) |
限制必须为一个小数,且整数部分的位数不能超过integer,小数部分的位数不能超过fraction |
@Future |
限制必须是一个将来的日期 |
@Max(value) |
限制必须为一个不大于指定值的数字 |
@Min(value) |
限制必须为一个不小于指定值的数字 |
@Past |
限制必须是一个过去的日期 |
@Pattern(value) |
限制必须符合指定的正则表达式 |
@Size(max,min) |
限制字符长度必须在min到max之间 |
除了JSR-303原生支持的限制类型之外我们还可以定义自己的限制类型。定义自己的限制类型首先我们得定义一个该种限制类型的注解,而且该注解需要使用@Constraint标注。现在假设我们需要定义一个表示金额的限制类型,那么我们可以这样定义:
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- import java.lang.annotation.Target;
- import javax.validation.Constraint;
- import javax.validation.Payload;
- import com.xxx.xxx.constraint.impl.MoneyValidator;
- @Target({ElementType.FIELD, ElementType.METHOD})
- @Retention(RetentionPolicy.RUNTIME)
- @Constraint(validatedBy=MoneyValidator.class)
- public @interface Money {
- String message() default"不是金额形式";
- Class<?>[] groups() default {};
- Class<? extends Payload>[] payload() default {};
- }
我们可以看到在上面代码中我们定义了一个Money注解,而且该注解上标注了@Constraint注解,使用@Constraint注解标注表明我们定义了一个用于限制的注解。@Constraint注解的validatedBy属性用于指定我们定义的当前限制类型需要被哪个ConstraintValidator进行校验。在上面代码中我们指定了Money限制类型的校验类是MoneyValidator。另外需要注意的是我们在定义自己的限制类型的注解时有三个属性是必须定义的,如上面代码所示的message、groups和payload属性。
在定义了限制类型Money之后,接下来就是定义我们的限制类型校验类MoneyValidator了。限制类型校验类必须实现接口javax.validation.ConstraintValidator,并实现它的initialize和isValid方法。我们先来看一下MoneyValidator的代码示例:
- import java.util.regex.Pattern;
- import javax.validation.ConstraintValidator;
- import javax.validation.ConstraintValidatorContext;
- import com.xxx.xxx.constraint.Money;
- public class MoneyValidator implements ConstraintValidator<Money, Double> {
- private String moneyReg = "^\\d+(\\.\\d{1,2})?$";//表示金额的正则表达式
- private Pattern moneyPattern = Pattern.compile(moneyReg);
- public void initialize(Money money) {
- // TODO Auto-generated method stub
- }
- public boolean isValid(Double value, ConstraintValidatorContext arg1) {
- // TODO Auto-generated method stub
- if (value == null)
- return true;
- return moneyPattern.matcher(value.toString()).matches();
- }
- }
从上面代码中我们可以看到ConstraintValidator是使用了泛型的。它一共需要指定两种类型,第一个类型是对应的initialize方法的参数类型,第二个类型是对应的isValid方法的第一个参数类型。从上面的两个方法我们可以看出isValid方法是用于进行校验的,有时候我们在校验的过程中是需要取当前的限制类型的属性来进行校验的,比如我们在对@Min限制类型进行校验的时候我们是需要通过其value属性获取到当前校验类型定义的最小值的,我们可以看到isValid方法无法获取到当前的限制类型Money。这个时候initialize方法的作用就出来了。我们知道initialize方法是可以获取到当前的限制类型的,所以当我们在校验某种限制类型时需要获取当前限制类型的某种属性的时候,我们可以给当前的ConstraintValidator定义对应的属性,然后在initialize方法中给该属性赋值,接下来我们就可以在isValid方法中使用其对应的属性了。针对于这种情况我们来看一个代码示例,现在假设我要定义自己的@Min限制类型和对应的MinValidator校验器,那么我可以如下定义:
Min限制类型
- @Target({ElementType.FIELD, ElementType.METHOD})
- @Retention(RetentionPolicy.RUNTIME)
- @Constraint(validatedBy=MinValidator.class)
- public @interface Min {
- int value() default 0;
- String message();
- Class<?>[] groups() default {};
- Class<? extends Payload>[] payload() default {};
- }
MinValidator校验器
- public class MinValidator implements ConstraintValidator<Min, Integer> {
- private int minValue;
- public void initialize(Min min) {
- // TODO Auto-generated method stub
- //把Min限制类型的属性value赋值给当前ConstraintValidator的成员变量minValue
- minValue = min.value();
- }
- public boolean isValid(Integer value, ConstraintValidatorContext arg1) {
- // TODO Auto-generated method stub
- //在这里我们就可以通过当前ConstraintValidator的成员变量minValue访问到当前限制类型Min的value属性了
- return value >= minValue;
- }
- }
继续来说一下ConstraintValidator泛型的第二个类型,我们已经知道它的第二个类型是对应的isValid的方法的第一个参数,从我给的参数名称value来看也可以知道isValid方法的第一个参数正是对应的当前需要校验的数据的值,而它的类型也正是对应的我们需要校验的数据的数据类型。这两者的数据类型必须保持一致,否则Spring会提示找不到对应数据类型的ConstraintValidator。建立了自己的限制类型及其对应的ConstraintValidator后,其用法跟标准的JSR-303限制类型是一样的。以下就是使用了上述自己定义的JSR-303限制类型——Money限制和Min限制的一个实体类:
- public class User {
- private int age;
- private Double salary;
- @Min(value=8, message="年龄不能小于8岁")
- public int getAge() {
- return age;
- }
- public void setAge(int age) {
- this.age = age;
- }
- @Money(message="标准的金额形式为xxx.xx")
- public Double getSalary() {
- return salary;
- }
- public void setSalary(Double salary) {
- this.salary = salary;
- }
- }
另外再讲一点Spring对自定义JSR-303限制类型支持的新特性,那就是Spring支持往ConstraintValidator里面注入bean对象。现在假设我们在MoneyValidator里面需要用到Spring ApplicationContext容器中的一个UserController bean对象,那么我们可以给ConstraintValidator定义一个UserController属性,并给定其set方法,在set方法上加注解@Resource或@Autowired通过set方式来注入当前的ApplicationContext中拥有的UserController bean对象。关于@Resource和@AutoWired的区别可以参考这篇博客。所以我们可以这样来定义我们的MoneyValidator:
- public class MoneyValidator implements ConstraintValidator<Money, Double> {
- private String moneyReg = "^\\d+(\\.\\d{1,2})?$";//表示金额的正则表达式
- private Pattern moneyPattern = Pattern.compile(moneyReg);
- private UserController controller;
- public void initialize(Money money) {
- // TODO Auto-generated method stub
- }
- public boolean isValid(Double value, ConstraintValidatorContext arg1) {
- // TODO Auto-generated method stub
- System.out.println("UserController: .............." + controller);
- if (value == null)
- returntrue;
- return moneyPattern.matcher(value.toString()).matches();
- }
- public UserController getController() {
- return controller;
- }
- @Resource
- public void setController(UserController controller) {
- this.controller = controller;
- }
- }
相关推荐
这里提到的"springmvc校验器validation的所有配套jar包"是指用于实现Spring MVC中数据验证功能所需的一系列库文件。具体来说,这些jar包通常包括Hibernate Validation、Spring的验证支持以及可能的其他依赖。 ...
而Hibernate Validation则是Java世界中用于数据验证的主流库,它基于JSR 303和JSR 349标准,提供了丰富的验证规则和易于使用的API。在Spring MVC中集成Hibernate Validation,可以实现对输入数据的有效性检查,从而...
springmvc_Validation相关jar包 包括hibernate-validator.jar jboss-logging-3.0.1.ga.jar validation-api-1.0.0.GA.jar
在Spring MVC框架中,表单验证是一个至关重要的环节,它确保了从客户端提交的数据符合预设的业务规则,从而防止因无效数据导致的错误或安全问题。...这使得Spring MVC成为开发复杂Web应用程序的首选框架之一。
在SpringMVC中,Validation框架用于实现数据验证,它基于Java Bean Validation规范,能够帮助开发者确保输入的数据满足预设的规则。这篇文章将重点讲解Validation的两种特殊用法:分组验证和组序列验证。 1. 分组...
在这个场景中,我们关注的两个关键jar包是`hibernate-validator-5.2.2.Final.jar`和`validation-api-1.1.0.Final.jar`,它们都是实现JSR-303和JSR-349(即Bean Validation)规范的重要组件。 **Hibernate Validator...
`Converter`和`Formatter`在Spring 3.0之后引入,它们在`PropertyEditor`之上提供了更现代和灵活的数据转换机制。`Converter`负责将一种类型转换为另一种类型,而`Formatter`则负责将类型转换为字符串(用于显示)...
总结,SpringMVC 以其强大的功能和灵活性,成为了 Java Web 开发的主流框架之一。通过理解并熟练掌握上述知识点,开发者可以构建出高效、可维护的 Web 应用程序。本教程的 PPT 将详细讲解这些内容,帮助你快速上手 ...
在Spring Boot应用中,结合Spring MVC和Thymeleaf,我们可以实现前端页面的数据验证功能,这就是所谓的Validation。这个实例展示了如何在用户提交表单时,通过后台验证确保数据的完整性和准确性。 首先,实例中使用...
SpringMVC 提供了一个强大的 Validation 机制来实现数据验证本文将详细介绍 SpringMVC 实现 Validation 校验过程详解。 一、概述 在 Web 应用程序中,数据验证可以分为前端验证和后端验证。前端验证通常使用 ...
在本文中,我们将详细介绍如何在SpringMVC项目中引入Validation,如何通过自定义validation减少代码量,提高生产力,并提供了实用的示例代码和配置信息。 Validation的概念 Validation是指对用户输入数据的校验,...
本教程将详细介绍如何在Spring MVC中配置并使用Hibernate Validation进行数据校验。 首先,我们需要引入必要的依赖。在Spring MVC项目中,通常我们会使用Maven或Gradle作为构建工具。对于Hibernate Validator,可以...
它是Java开发人员在构建Web应用时的首选工具之一,因其灵活性、高效性和可扩展性而受到广泛赞誉。这个名为"springMVC教案.rar"的压缩包包含了学习SpringMVC所需的基础教程和相关图片,对初学者来说是一份非常有价值...
此外,还可以使用`@Valid`注解和JSR-303/JSR-349 Bean Validation进行表单验证。 7. **异常处理**:SpringMVC允许自定义全局或特定异常处理器,可以捕获和处理程序运行时可能出现的异常,将其转换为适当的HTTP响应...
### Spring Boot + Spring MVC + Thymeleaf 页面提交 Validation 实现 #### 框架介绍与应用场景 在本文档中,我们将深入探讨如何利用 Spring Boot、Spring MVC 和 Thymeleaf 这三个流行的技术栈来实现一个表单验证...
在这个"SpringMVC入门很简单之数据验证"的主题中,我们将深入探讨如何在SpringMVC中实现数据验证。 在Web应用中,数据验证是必不可少的一环,它确保了用户提交的数据符合预设的规则和格式,防止非法或无效的数据...
8. **Validation**:SpringMVC支持JSR-303/JSR-349 Bean Validation标准,用于验证模型数据。 9. **MVC Interceptors**:拦截器可以预处理和后处理请求,例如权限检查、日志记录等。 10. **MultipartResolver**:...
9. **Conversion and Validation**:SpringMVC提供数据转换和验证功能,比如自动类型转换和JSR-303/JSR-349 Bean Validation。 10. **Asynchronous Request Processing**:SpringMVC支持异步请求处理,允许...
Spring MVC 提供了强大的数据绑定和验证功能,通过 `@RequestParam`、`@ModelAttribute` 等注解,以及 `@Valid` 和 `javax.validation` 规范进行表单验证。 ### 7. RESTful API 支持 Spring MVC 支持创建符合 ...
SpringMVC可以通过JSR-303/JSR-349 Bean Validation来实现。创建一个实体类并添加校验注解,然后在控制器中使用`Validator`进行验证。 ```java public class User { @NotBlank(message = "用户名不能为空") ...