文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Spring Boot之Validation自定义实现的方法

2023-07-02 15:00

关注

这篇“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这个验证类。

然后自定义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自定义实现的方法”这篇文章的内容,相信大家都有了一定的了解,希望小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注编程网行业资讯频道。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯