JSR303之自定义业务注解实践

一、环境与业务背景

1、开发环境

jdk1.8、maven

maven依赖:

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.5</version>
</dependency>


<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>  
    <version>6.2.0.Final</version>
</dependency>




<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.22</version>
</dependency>

2、业务背景

因作者所在公司,代码规范,规定入参与出参字段类型都为String,针对某些特定入参取值内容的校验就很有必要,本文针对常见的两种取值内容格式进行校验,进行了自定义业务注解。第一种特定的单个取值内容格式,另外一种以拼接内容的格式进行取值,校验这两种类型取值格式的方式有多种实现方案,本文提供的为常见实现方式。

二、入参取值类型

1、单值取值格式

定义:入参字段的取值为0或1、Y或N等这种格式内容。

以下案例以0或1这个格式举例:

1.1、自定义业务参数校验注解

import com.demo.validation.rule.AbstractStatusValueHandlerRule;




import javax.validation.Constraint;

import javax.validation.Payload;

import java.lang.annotation.Documented;

import java.lang.annotation.Repeatable;

import java.lang.annotation.Retention;

import java.lang.annotation.Target;





import static java.lang.annotation.ElementType.ANNOTATION_TYPE;

import static java.lang.annotation.ElementType.CONSTRUCTOR;

import static java.lang.annotation.ElementType.FIELD;

import static java.lang.annotation.ElementType.METHOD;

import static java.lang.annotation.ElementType.PARAMETER;

import static java.lang.annotation.ElementType.TYPE_USE;

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



@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })

@Retention(RUNTIME)

@Repeatable(StatusValue.List.class)
@Documented

@Constraint(validatedBy = {StatusValueValidated.class})
public @interface StatusValue {



    /**

     * 是否需要为空(true:为空,false:不为为空)
     */
    boolean isNull() default false;

    Class<? extends AbstractStatusValueHandlerRule> handlerRule() default AbstractStatusValueHandlerRule.class;


    String message() default "{javax.validation.constraints.StatusValue.message}";

    Class<?>[] groups() default { };

    Class<? extends Payload>[] payload() default { };

    @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
    @Retention(RUNTIME)
    @Documented
    @interface List {
        StatusValue[] value();
    }


}

1.2、自定义实现校验规则类

规则接口:

public interface StatusValueHandlerRule<T> {
    /**

     * 是否有效 
     * @param value T
     * @return true-有效;false-无效
     */
    boolean isValidation(T value);
}




抽象类:

public abstract class AbstractStatusValueHandlerRule<T> implements StatusValueHandlerRule<T> {




}


String入参类型取值校验规则类:

public class StringStatusValueRule extends AbstractStatusValueHandlerRule<String> {




    @Override
    public boolean isValidation(String value) {
        Optional<CommentYesOrNoEnum> first = Stream.of(CommentYesOrNoEnum.values())
                .filter(enums -> Objects.equals(enums.getCode(), value))
                .findFirst();
        return first.isPresent();
    }
}


枚举类:

@Getter
public enum CommentYesOrNoEnum {
    YES("1","是"),


    NO("0","不"),
    ;
    private String code;

    private String msg;

    CommentYesOrNoEnum(String code, String msg) {
        this.code = code;
        this.msg = msg;
    }
}

1.3、实现该注解的校验逻辑


import com.demo.validation.rule.AbstractStatusValueHandlerRule;
import com.demo.validation.rule.StatusValueHandlerRule;
import lombok.SneakyThrows;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.Objects;




public class StringStatusValueValidated implements ConstraintValidator<StatusValue,String> {


    private StatusValueHandlerRule statusValueRule;


    private boolean isNull;

    @SneakyThrows
    @Override
    public void initialize(StatusValue constraintAnnotation) {
        ConstraintValidator.super.initialize(constraintAnnotation);
        Class<? extends AbstractStatusValueHandlerRule> handlerRule = constraintAnnotation.handlerRule();
        statusValueRule = handlerRule.newInstance();
        isNull = constraintAnnotation.isNull();
    }



    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (isNull && Objects.isNull(value)) {
            return true;
        }


        if (Objects.isNull(value)){
            return false;
        }
        return statusValueRule.isValidation(value);
    }
}

2、多值以拼接方式取值格式

例如:入参取值格式内容为:1-2-3-4-5。

2.1、自定义注解

import cn.hutool.core.util.StrUtil;




import javax.validation.Constraint;

import javax.validation.Payload;

import java.lang.annotation.Documented;

import java.lang.annotation.Repeatable;

import java.lang.annotation.Retention;

import java.lang.annotation.Target;





import static java.lang.annotation.ElementType.ANNOTATION_TYPE;

import static java.lang.annotation.ElementType.CONSTRUCTOR;

import static java.lang.annotation.ElementType.FIELD;

import static java.lang.annotation.ElementType.METHOD;

import static java.lang.annotation.ElementType.PARAMETER;

import static java.lang.annotation.ElementType.TYPE_USE;

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



@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })

@Retention(RUNTIME)

@Repeatable(MontageStrValue.List.class)
@Documented

@Constraint(validatedBy = {MontageStrValueValidated.class})
public @interface MontageStrValue {



    /**

     *
     * 是否需要为空(true:为空,false:不为为空)
     */
    boolean isBlank() default false;


    /**
     * 分割之后数组元素最大个数
     */
    int maxSize() default 0;
    /**
     * 分割符
     */
    String separator() default StrUtil.COMMA;

    String message() default "{javax.validation.constraints.MontageStrValue.message}";

    Class<?>[] groups() default { };

    Class<? extends Payload>[] payload() default { };


    @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
    @Retention(RUNTIME)
    @Documented
    @interface List {
        MontageStrValue[] value();
    }
}

2.2、实现该注解的校验逻辑

public class MontageStrValueValidated implements ConstraintValidator<MontageStrValue,String> {
    /**

     * 数组最大个数
     */
    private int maxSize;
    /**
     * 是否为空
     */
    private boolean isBlank;
    /**
     * 分割符
     */
    private String separator;


    @Override
    public void initialize(MontageStrValue constraintAnnotation) {
        ConstraintValidator.super.initialize(constraintAnnotation);
        maxSize = constraintAnnotation.maxSize();
        isBlank = constraintAnnotation.isBlank();
        separator = constraintAnnotation.separator();
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (isBlank && StrUtil.isBlank(value)) {
            return true;
        }
        if (StrUtil.isBlank(value)) {
            return false;
        }
        String[] split = value.split(separator);
        return split.length <= maxSize;
    }
}

三、总结

使用JSR303注解要实现个性化入参校验,只需要做到两步即可,第一步:自定义一个注解。第二步:实现该注解的校验逻辑。

第一步:自定义一个注解

自定义注解时重点注意以下注解:@Constraint(validatedBy = {}),这个注解很关键,里面的validatedBy = {}是可以指定具体的校验类,例如:validatedBy = {MontageStrValueValidated.class},具体的校验逻辑在MontageStrValueValidated类里面实现。另外就是注解里面的一些属性,例如message、groups、payload和内部的一个@List注解,这里可以参考validation已有的注解,基本都是很有用的,然后就是定义自己需要的一些特殊的属性,方便校验,例如MontageStrValue注解中就包含了isBlank、maxSize、separator。

第二步:实现该注解的校验逻辑

例如: MontageStrValueValidated implements ConstraintValidator<MontageStrValue,String> 实现两个方法,分别是initialize(初始化方法)和isValid(校验方法),initialize()主要是加载读取注解上的值并赋值给类变量,isValid()是实现具体的校验逻辑,此处可自行实现具体的校验逻辑。

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MY4x2arh' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片