Spring MVC代码实例系列-06:Spring MVC配置Hibernate-Validator以及自定义校验注解

超级通道 :Spring MVC代码实例系列-绪论

本章主要记录,如何在Spring MVC中添加Hibernate-Validator以及自定义校验注解。本章主要涉及的技术点有:

  1. javax.validation : JSR 303: Bean Validation的一种实现
  2. hibernate.validator : JSR 303: Bean Validation的一种实现
  3. groups : 分组校验
  4. @GroupSequence : 排序分组校验
  5. message.properties : 校验提示信息的配置文件
  6. 自定义注解校验

##1.目录结构

src
\---main
	\---java
	|	\---pers
	|		\---hanchao
	|			\---hespringmvc
	|			 	\---validation
	|			 		\---InsertGroup.java
	|			 		\---UpdateGroup.java
	|			 		\---Student.java
	|			 		\---JsonResult.java
	|			 		\---StudentManageController.java
	\---webapp
		\---message.properties
	\---webapp
		\---validation
		|	\---student.jsp
		\---WEB-INF
		|	\---spring-mvc-servlet.xml
		|	\---web.xml
		\---index.jsp

##2.pom.xml引入相关jar包

    <hibernate-validator.version>5.4.1.Finalhibernate-validator.version>

    
    <dependency>
      <groupId>org.hibernategroupId>
      <artifactId>hibernate-validatorartifactId>
      <version>${hibernate-validator.version}version>
    dependency>

##3.spring-mvc-servlet.xml配置校验驱动

    
    <mvc:annotation-driven validator="validator"/>
    
    <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
        <property name="providerClass" value="org.hibernate.validator.HibernateValidator"/>
        <property name="validationMessageSource" ref="messageSource"/>
    bean>
    
    <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
        <property name="basename" value="message"/>
        <property name="defaultEncoding" value="utf-8"/>
        <property name="useCodeAsDefaultMessage" value="false"/>
        <property name="cacheSeconds" value="60"/>
    bean>

##4.Student.java设置校验规则
先看两个分组接口

package pers.hanchao.hespringmvc.validation;

public interface InsertGroup {}
package pers.hanchao.hespringmvc.validation;

public interface UpdateGroup {}

再看被校验的实体类:

package pers.hanchao.hespringmvc.validation;

import org.hibernate.validator.constraints.CreditCardNumber;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.Range;
import org.hibernate.validator.constraints.URL;
import pers.hanchao.hespringmvc.validation.custom.annotation.CustomLength;

import javax.validation.GroupSequence;
import javax.validation.constraints.*;
import java.util.Date;
/**
 * 

javax.validation校验、hibernate.validator校验、分组校验、@GroupSequence分组顺序校验

* @author hanchao 2018/1/20 14:58 **/
@GroupSequence({InsertGroup.class,UpdateGroup.class,Student.class}) public class Student { /////////////////////////////javax.validation///////////////////////////// //添加学生时,id必为空;修改学生时,id必须有值 @Null(groups = InsertGroup.class,message = "{Null.student.id}") @NotNull(groups = UpdateGroup.class,message = "{NotNull.student.id}") private String id; @Size(min = 2,max = 16,message = "{Size.student.name}",groups = {InsertGroup.class,UpdateGroup.class}) private String name;//名字 @AssertTrue(groups = InsertGroup.class, message = "{AssertTrue.student.newRegister}") @AssertFalse(groups = UpdateGroup.class, message = "{AssertTrue.student.newRegister}") private boolean newRegister;//是否新注册 @Max(value = 100, message = "{Max.student.score}",groups = {InsertGroup.class,UpdateGroup.class}) @Min(value = 0, message = "{Min.student.score}",groups = {InsertGroup.class,UpdateGroup.class}) private int score;//分数[0-100] @DecimalMax(value = "30",inclusive = true, message = "{student.age}",groups = {InsertGroup.class,UpdateGroup.class}) @DecimalMin(value = "19",inclusive = false, message = "{student.age}",groups = {InsertGroup.class,UpdateGroup.class}) private String age;//年龄范围[20-30] @Digits(integer = 3,fraction = 2, message = "{Digits.student.weight}",groups = {InsertGroup.class,UpdateGroup.class}) private float weight;//体重格式[xxx.yy] @Past(message = "{Past.student.entrance}",groups = {InsertGroup.class,UpdateGroup.class}) private Date entrance;//入学时间 @Future(message = "{Future.student.graduation}",groups = {InsertGroup.class,UpdateGroup.class}) private Date graduation;//毕业时间 @Pattern(regexp = "^S2018[0-9]{4}$",flags = Pattern.Flag.CASE_INSENSITIVE, message = "{Pattern.student.number}",groups = {InsertGroup.class,UpdateGroup.class}) private String number;//学号形式 S20180000-S20189999 大小写敏感 /////////////////////////////hibernate.validator///////////////////////////// @URL(message = "{URL.student.blog}",groups = {InsertGroup.class,UpdateGroup.class}) private String blog;//个人学生主页 @Length(min = 1000,max = 5000 ,message = "{Length.student.bonus}",groups = {InsertGroup.class,UpdateGroup.class}) private String tuition;//学费 @Range(min = 2000L,max = 4000L,message = "{Rang.student.bonus}",groups = {InsertGroup.class,UpdateGroup.class}) private String bonus;//奖金 @CreditCardNumber(message = "{CreditCardNumber}",groups = {InsertGroup.class,UpdateGroup.class}) private String creditCard;//银行账号 //toString() //setter and getter }

校验规则说明:

  1. @URL@Length、@Range、 @CreditCardNumber属于hibernate.validator,其他的都属于javax.validation

  2. groups = InsertGroup.class表名此字段只在InsertGroup分组被校验。

  3. groups = {InsertGroup.class,UpdateGroup.class}表面此字段在InsertGroupUpdateGroup分组都被校验。

  4. 一个字段可以被多种校验规则注解,如

     @Max(value = 100, message = "{Max.student.score}",groups = {InsertGroup.class,UpdateGroup.class})
     @Min(value = 0, message = "{Min.student.score}",groups = {InsertGroup.class,UpdateGroup.class})
     private int score;//分数[0-100]
    
  5. @GroupSequence({InsertGroup.class,UpdateGroup.class,Student.class})表明了InsertGroupUpdateGroup分组的校验顺序,即:如果InsertGroup分组的校验有错误,则不再进行UpdateGroup分组的校验,直接返回。

  6. @GroupSequence({InsertGroup.class,UpdateGroup.class,Student.class})一定要注意不要忘了把当前实体类Student.class写入其中。

  7. message = "{Length.student.bonus}""是通过message.properties读取校验提示信息的方式。

  8. 多种校验规则可以共用同样的校验提示信息,如:

         @DecimalMax(value = "30",inclusive = true, message = "{student.age}",groups = {InsertGroup.class,UpdateGroup.class})
         @DecimalMin(value = "19",inclusive = false, message = "{student.age}",groups = {InsertGroup.class,UpdateGroup.class})
         private String age;//年龄范围[20-30]
    

##5.message.properties设置校验提示信息

Null.student.id = 学生id必须为空!
NotNull.student.id = 学生id必须不为空!
Size.student.name = 姓名应该在2至16个汉字之间!
CustomLength.student.name = 姓名应该在2至16个字符之间!
AssertTrue.student.newRegister = 必须是新注册的学生!
AssertFalse.student.newRegister = 必须是已经注册的学生!
Max.student.score = 分数必须小于等于100!
Min.student.score = 分数必须大于等于0!
student.age = 年龄必须处于20~30之间!
Digits.student.weight = 体重必须是xxx.yy的格式,如101.52!
Past.student.entrance = 入学时间必须早于今天!
Future.student.graduation = 毕业时间必须晚于今天!
Pattern.student.number = 学号格式必须位于S20180000-S20189999之间!
URL.student.blog = 学生个人主页必须错误!
Length.student.bonus = 学费必须在1000-5000之间!
Rang.student.bonus = 奖金必须在2000-4000之间!
CreditCardNumber = 银行账号不合法!

校验提示信息说明:

  1. 这里的key,例如Null.student.id等,应该与Student.java中的message = "{Null.student.id}"相匹配。

##6.StudentManageController.java控制类对实体进行校验

package pers.hanchao.hespringmvc.validation;

import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.List;

/**
 * 

简单的bean validation和hibernate validator的例子

* @author hanchao 2018/1/20 14:47 **/
@Controller @RequestMapping("validation") public class StudentManageController { /** *

注册学生信息

* @author hanchao 2018/1/20 14:47 **/
@PostMapping("/insert") @ResponseBody public JsonResult insert(@Validated(InsertGroup.class) @RequestBody Student student, BindingResult bindingResult){ JsonResult jsonResult = new JsonResult(); validate(bindingResult, jsonResult); System.out.println(jsonResult.toString()); return jsonResult; } /** *

修改学生信息

* @author hanchao 2018/1/20 14:47 **/
@PostMapping("/update") @ResponseBody public JsonResult update(@Validated(UpdateGroup.class) @RequestBody Student student, BindingResult bindingResult){ JsonResult jsonResult = new JsonResult(); validate(bindingResult, jsonResult); System.out.println(jsonResult.toString()); return jsonResult; } /** *

对bindingResult进行校验

* @author hanchao 2018/1/20 14:46 **/
private void validate(BindingResult bindingResult, JsonResult jsonResult) { if (bindingResult.hasErrors()){ StringBuffer errors = new StringBuffer(); List<ObjectError> allErrors = bindingResult.getAllErrors(); for (ObjectError objectError : allErrors){ errors.append(objectError.getDefaultMessage() + "
"
); } jsonResult.setCodeAndMessage("0",errors.toString()); } } }

说明:

  1. @Validated(InsertGroup.class) @RequestBody Student student表名只对页面信息进行InsertGroup.class分组相关的校验。
  2. 如果不指定校验哪个分组,如@Validated @RequestBody Student student则会校验所有的字段。
  3. 如果不指定校验哪个分组,如@Validated @RequestBody Student student可以用@Valid @RequestBody Student student替代。

##7.student.jsp页面

<%--
  Created by IntelliJ IDEA.
  User: hanchao
  Date: 2018/1/17
  Time: 21:14
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>


    学生信息管理页面
    
    


###8.result

Spring MVC代码实例系列-06:Spring MVC配置Hibernate-Validator以及自定义校验注解_第1张图片

##9.自定义注解校验
场景(假设情况…):

  • @Length校验的是输入字符的个数,例如你好的长度是2
  • 如果name字段的校验规则为(min = 2,max = 4),在mysql中name字段的长度设计为varchar(4)
  • 这种校验应用在mysql 5.0以后是没问题的,因为Mysql 5.0以后varchar存储的就是字符数,name字段最多可以存储4个汉字。
  • 但是这种校验应用在Mysql 4.0数据库中是有问题的,因为Mysql 4.0存储的是字节数,name组多存储1个UTF-8汉字或者2个GBG汉字。
  • 如果前台传过来的name=张三丰,虽然校验不报错,但是插入mysql时,肯定会报错。

这里引入了自定义校验注解的概念,需要设计一个新的注解实现以下功能:

  1. 能够通过minmax设置最小和最大长度。
  2. 校验的是输入内容的字节数。
  3. 能够设置编码格式。

为了完成这个自定义注解,下面分两步进行:
目录结构(续接之前的目录结构):

\---validation
	\---custom
		\---annotation
			\---CustomLength.java
		\---validator
			\---CustomLengthValidator.java

###9.1.CustomLength.java自定义校验注解

package pers.hanchao.hespringmvc.validation.custom.annotation;

import pers.hanchao.hespringmvc.validation.custom.validator.CustomLengthValidator;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
 * 

校验字符串长度,中文按照charset进行计算

* @author hanchao 2018/1/20 15:40 **/
@Target( { METHOD, FIELD, ANNOTATION_TYPE }) @Retention(RUNTIME) @Constraint(validatedBy = CustomLengthValidator.class) @Documented public @interface CustomLength { long min() default 0; long max() default Integer.MAX_VALUE; String charset() default "gbk"; String message() default "length must be between {min} and {max}"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }

###9.2.CustomLengthValidator自定义校验规则类

package pers.hanchao.hespringmvc.validation.custom.validator;

import java.io.UnsupportedEncodingException;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

import org.hibernate.validator.internal.util.logging.Log;
import org.hibernate.validator.internal.util.logging.LoggerFactory;
import pers.hanchao.hespringmvc.validation.custom.annotation.CustomLength;

/**
 * 

自定义校验规则诶

* @author hanchao 2018/1/20 17:04 **/
public class CustomLengthValidator implements ConstraintValidator<CustomLength, String> { private static final Log log = LoggerFactory.make(); private long min; private long max; private String charset; @Override public void initialize(CustomLength parameters) { //do nothing this.min = parameters.min(); this.max = parameters.max(); this.charset = parameters.charset(); validateParameters(); } @Override public boolean isValid(String value, ConstraintValidatorContext context) { if(null == value){ value = ""; } long length = 0; try { length = ((String)value).getBytes(charset).length; } catch (UnsupportedEncodingException e) { e.printStackTrace(); } boolean result = (length >= min) && (length <= max); log.info("CustomLength.validator:[value:" + (String)value + ",min:" + min + ",max:" + max + ",length:" + length + "],result:" + result); return (length >= min) && (length <= max); } private void validateParameters() { if (this.min < 0) { throw log.getMinCannotBeNegativeException(); } if (this.max < 0) { throw log.getMaxCannotBeNegativeException(); } if (this.max < this.min) throw log.getLengthCannotBeNegativeException(); } }

以上,自定义校验注解CustomLenght定义完毕,下面进行实践。
###9.3.修改Student.java
为了方便演示,注释掉了其他校验

//    @Size(min = 2,max = 4,message = "{Size.student.name}",groups = {InsertGroup.class,UpdateGroup.class})
@CustomLength(min = 2,max = 4,charset = "utf-8",message = "{CustomLength.student.name}",groups = {InsertGroup.class,UpdateGroup.class})
private String name;//名字

##9.4.修改message.properties
添加提示信息

CustomLength.student.name = 姓名应该在2至4个字节之间!

###9.5.result

Spring MVC代码实例系列-06:Spring MVC配置Hibernate-Validator以及自定义校验注解_第2张图片

你可能感兴趣的:(Spring-MVC合集,Spring,MVC学习实例)