这篇“Spring Boot之Validation自定义实现的方法”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Spring Boot之Validation自定义实现的方法”文章吧。
Validation自定义实现方式
Spring Boot Validation定制
虽然在Spring Boot中已经提供了非常多的预置注解,用以解决在日常开发工作中的各类内容,但是在特定情况仍然存在某些场景,无法满足需求,需要自行定义相关的validator。
自定义的注解
这里的场景设置为进行IP地址的验证,通过注解的方式,让用户使用验证规则。注解定义如下:
@Target({ElementType.FIELD})@Retention(RUNTIME)@Documented@Constraint(validatedBy = IPAddressValidator.class)public @interface IPAddress { String message() default "{ipaddress.invalid}"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {};}
这个注解是作用在Field字段上,运行时生效,触发的是IPAddressValidator这个验证类。
message
定制化的提示信息,主要是从ValidationMessages.properties里提取,也可以依据实际情况进行定制
groups
这里主要进行将validator进行分类,不同的类group中会执行不同的validator操作
payload
主要是针对bean的,使用不多。
然后自定义Validator,这个是真正进行验证的逻辑代码:
public class IPAddressValidator implements ConstraintValidator<IPAddress, String> { @Override public boolean isValid(String value, ConstraintValidatorContext context) { Pattern pattern = compile("^([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})$"); Matcher matcher = pattern.matcher(value); try { if (!matcher.matches()) { return false; } else { for (int i = 1; i <= 4; i++) { int octet = Integer.valueOf(matcher.group(i)); if (octet > 255) { return false; } } return true; } } catch (Exception e) { return false; } }}
关于IP地址的验证规则是通用的,具体逻辑不用太在意,主要是需要这里Validator这个接口,以及其中的两个泛型参数,第一个为注解名称,第二个为实际字段的数据类型。
使用自定义的注解
定义了实体类CustomFieldBean.java
@Datapublic class CustomFieldBean { @IPAddress private String ipAddr;}
使用方法非常简约,基于注解,无侵入逻辑。
单元测试用例
测试代码:
@RunWith(SpringRunner.class)@SpringBootTestpublic class CustomFieldValidatorTest { @Autowired private ProductService productService; @Test(expected = ConstraintViolationException.class) public void testInvalid() { CustomFieldBean customFieldBean = new CustomFieldBean(); customFieldBean.setIpAddr("1.2.33"); this.productService.doCustomField(customFieldBean); } @Test public void testValid() { CustomFieldBean customFieldBean = new CustomFieldBean(); customFieldBean.setIpAddr("1.2.33.123"); this.productService.doCustomField(customFieldBean); }}
自定义执行Validator
如果不希望由系统自行触发Validator的验证逻辑,则可以由开发者自行进行验证。在Spring Boot已经内置了Validator实例,直接将其加载进来即可。
使用示例如下:
@Autowiredprivate Validator validator;
自定义执行的单元测试
测试代码如下:
@RunWith(SpringRunner.class)@SpringBootTestpublic class CodeValidationTest { @Autowired private Validator validator; @Test(expected = ConstraintViolationException.class) public void testValidator() { CustomFieldBean input = new CustomFieldBean(); input.setIpAddr("123.3.1"); Set<ConstraintViolation<CustomFieldBean>> violations = validator.validate(input); if (!violations.isEmpty()) { throw new ConstraintViolationException(violations); } }}
自定义Validation注解
最近新开了一个项目,虽然hibernate-validator很好用,但是有时不能满足稍微复杂一些的业务校验。为了不在业务代码中写校验逻辑,以及让代码更优雅,故而采用了自定义校验注解的方式。
场景说明
本例注解应用场景: 填写表单时,某一项数据存在时,对应的一类数据都应存在,一同提交。
源码
1.类注解
主注解用于标记要在校验的实体类
@Target( { TYPE })@Retention(RUNTIME)@Constraint(validatedBy = RelateOtherValidator.class)@Documentedpublic @interface RelateOther { String message() default ""; int num() default 2; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {};}
2.辅助注解
辅助注解用于标注于要校验的字段,isMaster区分为主注解和从注解。
主注解是关键字段,存在才进行校验从注解对应字段的有效性;主注解的value()属性可以设置默认值,当字段对应值对应value()时才开启校验。
从注解为等待校验的值,默认为从注解。
@Target( { FIELD })@Retention(RUNTIME)@Documentedpublic @interface RelateOtherItem { boolean isMaster() default false; String value() default "";}
3.校验类
校验类为实际执行校验逻辑的类,在类注解的@Constraint的validatedBy属性上设置。
要设置为校验类,首先要实现ConstraintValidator类的isValid方法。
@Slf4j // @Slf4j是lombok的注解public class RelateOtherValidator implements ConstraintValidator<RelateOther, Object> { // 要校验的个数 private int validateNum; @Override public void initialize(RelateOther constraintAnnotation) { validateNum = constraintAnnotation.num(); } @Override public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) { if (o == null) { return true; } Field[] declaredFields = o.getClass().getDeclaredFields(); boolean mater = false; int emptyNum = 0; try { // 总共需要校验的字段数 int totalValidateNum = validateNum; for (Field field : declaredFields) { // 校验是否进行过标注 if (!field.isAnnotationPresent(RelateOtherItem.class)) { continue; } if (validateNum > 0 && totalValidateNum-- < 0) { return false; } field.setAccessible(true); Object property = field.get(o); RelateOtherItem relateOtherItem = field.getAnnotation(RelateOtherItem.class); // 主字段不存在,则校验通过 if (relateOtherItem.isMaster()) { if (property==null) { return true; } // 与指定值不一致,校验通过 if (!StringUtils.isEmpty(relateOtherItem.value()) && !relateOtherItem.value().equals(property)) { return true; } mater = true; continue; } if (null == property) { emptyNum++; } } // 主字段不存在,则校验通过 if (!mater) { log.info("RelateOther注解主字段不存在"); return true; } return emptyNum==0; } catch (Exception e) { log.info("RelateOther注解,解析异常 {}", e.getMessage()); return false; } }}
4.校验失败
注解校验不同时会抛出一个MethodArgumentNotValidException异常。这里可以采用全局异常处理的方法,进行捕获处理。捕获之后的异常可以获取BindingResult 对象,后面就跟hibernate-validator处理方式一致了。
BindingResult bindingResult = ((MethodArgumentNotValidException) e).getBindingResult();
5.使用demo
注解的使用类似下面,首先在请求实体类上标注类注解,再在对应的字段上标注辅助注解。
@RelateOther(message = "xx必须存在!",num=2)public class MarkReq { @RelateOtherItem (isMaster= true,value="1") private Integer girl; @RelateOtherItem private Integer sunscreen; private String remarks;}
以上就是关于“Spring Boot之Validation自定义实现的方法”这篇文章的内容,相信大家都有了一定的了解,希望小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注编程网行业资讯频道。