hibernate_validator_09
创建自己的约束规则
尽管Bean Validation API定义了一大堆标准的约束条件, 但是肯定还是有这些约束不能满足我们
需求的时候, 在这种情况下, 你可以根据你的特定的校验需求来创建自己的约束条件.
一.创建一个简单的约束条件
按照以下三个步骤来创建一个自定义的约束条件
•创建约束标注
•实现一个验证器
•定义默认的验证错误信息
1. 约束标注---让我们来创建一个新的用来判断一个给定字符串是否全是大写或者小写字符的约束标注.
首先,我们需要一种方法来表示这两种模式( 译注: 大写或小写), 我们可以使用 String 常量, 但是
在Java 5中, 枚举类型是个更好的选择:
package test02; public enum CaseMode { UPPER, LOWER; }
2.现在我们可以来定义真正的约束标注了. 如果你以前没有创建过标注(annotation)的话参考
http://www.cnblogs.com/wangyang108/p/5668388.html
package test02; import static java.lang.annotation.ElementType.*; import static java.lang.annotation.RetentionPolicy.*; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; import javax.validation.Constraint; import javax.validation.Payload; @Target({METHOD, FIELD, ANNOTATION_TYPE }) @Retention(RUNTIME) @Constraint(validatedBy = CheckCaseValidator.class) @Documented public @interface CheckCase { String message() default "{com.mycompany.constraints.checkcase}"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; CaseMode value(); }
一个标注(annotation) 是通过 @interface 关键字来定义的. 这个标注中的属性是声明成类似方法
的样式的. 根据Bean Validation API 规范的要求
•message属性, 这个属性被用来定义默认得消息模版, 当这个约束条件被验证失败的时候,通过
此属性来输出错误信息.
•groups 属性, 用于指定这个约束条件属于哪(些)个校验组.这
个的默认值必须是 Class<?> 类型到空到数组.
• payload 属性, Bean Validation API 的使用者可以通过此属性来给约束条件指定严重级别. 这
个属性并不被API自身所使用.
提示:通过payload属性来指定默认错误严重级别的示例
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class Severity { public static class Info extends Payload {}; public static class Error extends Payload {}; } public class ContactDetails { @NotNull(message="Name is mandatory", payload=Severity.Error.class) private String name; @NotNull(message="Phone number not specified, but not mandatory", payload=Severity.Info.class) private String phoneNumber; // ... }
这样, 在校验完一个ContactDetails
的示例之后, 你就可以通过调用ConstraintViolation.getConstraintDescriptor().getPayload()
来得到之前指定到错误级别了,并且可以根据这个信息来决定接下来到行为.
除了这三个强制性要求的属性(message, groups 和 payload) 之外, 我们还添
加了一个属性用来指定所要求到字符串模式. 此属性的名称value在annotation的定义中比较特
殊, 如果只有这个属性被赋值了的话, 那么, 在使用此annotation到时候可以忽略此属性名称,
即 @CheckCase(CaseMode.UPPER) .
另外, 我们还给这个annotation标注了一些(所谓的) 元标注( 译注: 或"元模型信息"?, "meta
annotatioins"):
• @Target({ METHOD, FIELD, ANNOTATION_TYPE }) : 表示@CheckCase 可以被用在方法, 字段或者
annotation声明上.
• @Retention(RUNTIME) : 表示这个标注信息是在运行期通过反射被读取的.
• @Constraint(validatedBy = CheckCaseValidator.class) : 指明使用那个校验器(类) 去校验使用了
此标注的元素.
• @Documented : 表示在对使用了 @CheckCase 的类进行javadoc操作到时候, 这个标注会被添加到
javadoc当中.
提示:Hibernate Validator对方法的参数上使用约束注释也提供的支持.
为了使用自定义的参数认证的注释,ElementType.PARAMETER一定要被指定成@Target annotation
3.创建一个验证器以让我们的注释生效--实现ConstraintValidator接口
package test02; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; public class CheckCaseValidator implements ConstraintValidator<CheckCase, String> { private CaseMode caseMode; public void initialize(CheckCase constraintAnnotation) { this.caseMode = constraintAnnotation.value(); } public boolean isValid(String object, ConstraintValidatorContext constraintContext) { if (object == null) return true; if (caseMode == CaseMode.UPPER) return object.equals(object.toUpperCase()); else return object.equals(object.toLowerCase()); } }
ConstraintValidator 定义了两个泛型参数, 第一个是这个校验器所服务到标注类型(在我们的例子
中即 CheckCase ), 第二个这个校验器所支持到被校验元素到类型 (即 String ).
如果一个约束标注支持多种类型到被校验元素的话, 那么需要为每个所支持的类型定义一
个 ConstraintValidator ,并且注册到约束标注中.
这个验证器的实现就很平常了, initialize() 方法传进来一个所要验证的标注类型的实例, 在本
例中, 我们通过此实例来获取其value属性的值,并将其保存为 CaseMode 类型的成员变量供下一步使
用.
isValid() 是实现真正的校验逻辑的地方, 判断一个给定的 String 对于 @CheckCase 这个约束条件来说
是否是合法的, 同时这还要取决于在 initialize() 中获得的大小写模式. 根据Bean Validation中所
推荐的做法, 我们认为 null 是合法的值. 如果 null 对于这个元素来说是不合法的话,那么它应该使
用 @NotNull 来标注.
4.现在来验证一下
先建一个Po
package test02; public class User { @CheckCase(CaseMode.UPPER) private String name="wangyang"; }
然后进行验证
package test02; import java.util.Set; import javax.validation.ConstraintViolation; import javax.validation.Validation; import javax.validation.Validator; import javax.validation.ValidatorFactory; import org.junit.BeforeClass; import org.junit.Test; public class MyTest { private static Validator validator; /** * 获取一个验证器 */ @BeforeClass public static void setUp() { ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); validator = factory.getValidator(); } @Test public void test01(){ User u=new User(); Set<ConstraintViolation<User>> validate = validator.validate(u); System.out.println(validate.size()); System.out.println(validate.iterator().next());
//ConstraintViolationImpl{interpolatedMessage='{com.mycompany.constraints.checkcase}', propertyPath=name, rootBeanClass=class test02.User, messageTemplate='{com.mycompany.constraints.checkcase}'} } }
4.ConstraintValidatorContext
上面的例子中的 isValid 使用了约束条件中定义的错误消息模板, 然
后返回一个 true 或者 false . 通过使用传入的 ConstraintValidatorContext 对象, 我们还可以给约束
条件中定义的错误信息模板来添加额外的信息或者完全创建一个新的错误信息模板.
下面我们修改上述代码
package test02; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; public class CheckCaseValidator implements ConstraintValidator<CheckCase, String> { private CaseMode caseMode; public void initialize(CheckCase constraintAnnotation) { this.caseMode = constraintAnnotation.value(); } public boolean isValid(String object, ConstraintValidatorContext constraintContext) { if (object == null) return true; boolean isValid; if (caseMode == CaseMode.UPPER) { isValid = object.equals(object.toUpperCase()); } else { isValid = object.equals(object.toLowerCase()); } if (!isValid) { constraintContext.disableDefaultConstraintViolation(); constraintContext.buildConstraintViolationWithTemplate("{com.mycompany.constraints.CheckCase.message}") .addConstraintViolation(); } return isValid; } }
再进行验证:
package test02; import java.util.Set; import javax.validation.ConstraintViolation; import javax.validation.Validation; import javax.validation.Validator; import javax.validation.ValidatorFactory; import org.junit.BeforeClass; import org.junit.Test; public class MyTest { private static Validator validator; /** * 获取一个验证器 */ @BeforeClass public static void setUp() { ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); validator = factory.getValidator(); } @Test public void test01(){ User u=new User(); Set<ConstraintViolation<User>> validate = validator.validate(u); System.out.println(validate.size()); System.out.println(validate.iterator().next()); //ConstraintViolationImpl{interpolatedMessage='{com.mycompany.constraints.CheckCase.message}', propertyPath=name, rootBeanClass=class test02.User, messageTemplate='{com.mycompany.constraints.CheckCase.message}'} } }
5.Adding new ConstraintViolation with custom property path(这一块的东西真没懂,暂不知道有什么用)
6.校验错误信息
最后, 我们还需要指定如果 @CheckCase 这个约束条件验证的时候,没有通过的话的校验错误信息.
我们可以添加下面的内容到我们项目自定义的 ValidationMessages.properties
例:
package test02; import static java.lang.annotation.ElementType.*; import static java.lang.annotation.RetentionPolicy.*; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; import javax.validation.Constraint; import javax.validation.Payload; @Target({METHOD, FIELD, ANNOTATION_TYPE }) @Retention(RUNTIME) @Constraint(validatedBy = CheckCaseValidator.class) @Documented public @interface CheckCase { String message() default "{test02.CheckCase.message}"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; CaseMode value(); }
再定义一个ValidationMessages.properties
该文件中
test02.CheckCase.message = must be false {value}
运行测试结果:
package test02; import java.util.Set; import javax.validation.ConstraintViolation; import javax.validation.Validation; import javax.validation.Validator; import javax.validation.ValidatorFactory; import org.junit.BeforeClass; import org.junit.Test; public class MyTest { private static Validator validator; /** * 获取一个验证器 */ @BeforeClass public static void setUp() { ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); validator = factory.getValidator(); } @Test public void test01(){ User u=new User(); Set<ConstraintViolation<User>> validate = validator.validate(u); System.out.println(validate.size()); //1 System.out.println(validate); //[ConstraintViolationImpl{interpolatedMessage='must be false UPPER', propertyPath=name, rootBeanClass=class test02.User, messageTemplate='{test02.CheckCase.message}'}] } }