出于程序的稳定性以及简洁,会对数据入参时进行校验,常用的两个包 javax.validation.constraints、org.hibernate.validator.constraints,如@NotNull、@Length等。很多时候随着业务的不同,这些注解不一定能满足需求,这时候可以通过自定义注解来校验
核心注解为@Constraint
如下为一个自定义的数字校验注解
@Pattern(regexp = "[0-9]*")
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = {})
@ReportAsSingleViolation
public @interface NumberVaildation {
String message() default "请输入数字";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
一些注解的说明:
@Retention:必须是RUNTIME,否则Bean validation不会进行检验
@Documented:标注了这个标记的target,在javadoc中应显示其存在
@Constraint是必须的,表明这是一个Bean Validation限制,否则会被忽略,validatedBy中表明用来验证的constraintValidator的实现
@Pattern:可以理解为正则表达式
@ConstraintComposition:组合校验,下面会测试说明
具体用法测试用例
写一个DTO类
@Data
public class TestDTO implements Serializable {
@ApiModelProperty(value = "字母")
@ABCVaildation
private String abc;
@ApiModelProperty(value = "数字")
@NumberVaildation
private String number;
@ApiModelProperty(value = "项目id")
@ItemNumber
private String itemId;
}
三个自定义注解
@NumberVaildation:校验数字
@ABCVaildation:校验字母
@ItemNumber:组合校验
@Pattern(regexp = "^[A-Za-z]+$")
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = {})
@ReportAsSingleViolation
public @interface ABCVaildation {
String message() default "请输入字母";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
@Pattern(regexp = "[0-9]*")
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = {})
@ReportAsSingleViolation
public @interface NumberVaildation {
String message() default "请输入数字";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
@ConstraintComposition(CompositionType.OR)
@NumberVaildation
@ABCVaildation
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = {})
@ReportAsSingleViolation
public @interface ItemNumber {
String message() default "请输入数字/字母";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
controller:
@RestController
@Api(tags = "测试校验专用api")
public class TestController {
@PostMapping(value = "vaildation")
@ApiOperation("测试校验")
public Result vaildationApi(@Valid @RequestBody TestDTO testDTO) {
return Result.ok("通过");
}
}
测试结果:
可以看到在@ItemNumber上加了另外两个注解@NumberVaildation、@ABCVaildation, 并且用了@ConstraintComposition(CompositionType.OR),CompositionType.OR是组合类型,有AND、OR、ALL_FALSE。为or说明满足其中一个注解即可,and需要同时满足,all_false则不通过。
还可以通过@Constraint中的validatedBy 自定义类来实现,需要implements ConstraintValidator
@ConstraintComposition(CompositionType.OR)
@NumberVaildation
@ABCVaildation
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = {CondictionVaildation.class})
@ReportAsSingleViolation
public @interface ItemNumber {
String message() default "请输入数字/字母";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class CondictionVaildation implements ConstraintValidator<ItemNumber, Object> {
@Override
public void initialize(ItemNumber constraintAnnotation) {
}
@Override
public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) {
ConstraintValidatorContextImpl ctx = (ConstraintValidatorContextImpl)constraintValidatorContext;
// 逻辑代码这边写,Object为传入的itemId
return true; // true为通过
}
}
另外,可以将注解放在类上面,这样拿到的object是整个对象,可以在CondictionVaildation 里面打个断点看看