Spring boot - @Validated @Valid 优雅校验

写在开头

编写不易,如有问题欢迎留言。转载请带原文链接

使用场景

1.Spring MVC 传入参数校验 结合@ControllerAdvice || @RestControllerAdvice
2.结合@ConfigurationProperties 从yml || properties中读取配置构造bean时候使用
3.结合@Value可以对@Value值做校验
4.实际业务中Service层也有公司用来校验

常见组合用法

1.如果使用@ConfigurationProperties || @Value对配置做校验,常搭配META-INF/additional-spring-configuration-metadata-json ,META-INF/spring-configuration-metadata.json 一起使用
2.在SpringMVC中校验常用DTO对入参对象校验,会涉及到@Validation(value=xxx.class) 用分组来对不同业务操作校验

@Validated @Valid区别

1.@Valid用户嵌套校验,其余如果不深究使用@Validated就可以了
2.Validated是org.springframework的annotation,@Valid 注解是javax.validation的注解.
3.@Validated:提供分组功能,可以在参数验证时,根据不同的分组采用不同的验证机制,@Valid:没有分组功能
4.@Validated:用在类型、方法和方法参数上。但不能用于成员属性(field),@Valid:可以用在方法、构造函数、方法参数和成员属性(field)上

ConfigurationProperties Validation

@ConfigurationProperties(prefix="acme")
@Validated
public class AcmeProperties {

	@NotNull
	private InetAddress remoteAddress;

	// ... getters and setters

}

详情可查看@ConfigurationProperties Validation

Service Validation

@Service
@Validated
public class MyBean {

	public Archive findByCodeAndAuthor(@Size(min = 8, max = 10) String code,
			Author author) {
		...
	}

}

详情可查看 @Service Validation

快速记忆使用方式

Bean类加@Validation,需要校验的对象前加@Validation,对象里的嵌套校验加@Valid

@Validated 原理

1.基于Spring Boot的AutoConfiguration: org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration
2.创建MethodValidationPostProcessor的Bean对象

MethodValidationPostProcessor 源码分析

在这里插入图片描述
实现InitializingBean接口
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

由其中可看出仅对带有@Validated的Bean对象做PointCut处理

验证框架

自定义校验框架

Group Class

public interface GroupClasses {

    interface Template {

    }

    interface Add {

    }

    interface Query {

    }

    interface Update {

    }

    interface Delete {

    }

}

CheckAtLeastOneNotBlankValidator

在类上添加注解

e.g. 在类上添加

@CheckAtLeastOneNotBlank(message = "image || imageUrl 参数必填一项", fieldNames = {"image", "imageUrl"})

Annotation -> CheckAtLeastOneNotBlank

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

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;

/**
 * @author <a href="mailto:boommanpro@gmail.com">boommanpro</a>
 */

@Target({TYPE})
@Retention(RUNTIME)
@Constraint(validatedBy = CheckAtLeastOneNotBlankValidator.class)
@Documented
public @interface CheckAtLeastOneNotBlank {

    String message() default "";

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

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

    String[] fieldNames();

}

CheckAtLestOneNotBlankValidator

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.tomcat.util.IntrospectionUtils;
import org.springframework.stereotype.Component;

/**
 * @author <a href="mailto:boommanpro@gmail.com">boommanpro</a>
 */
@Slf4j
@Component
class CheckAtLeastOneNotBlankValidator implements ConstraintValidator<CheckAtLeastOneNotBlank, Object> {

    private String[] fieldNames;

    @Override
    public void initialize(CheckAtLeastOneNotBlank constraintAnnotation) {
        this.fieldNames = constraintAnnotation.fieldNames();
    }

    @Override
    public boolean isValid(Object object, ConstraintValidatorContext constraintContext) {
        if (object == null) {
            return true;
        }
        for (String fieldName : fieldNames) {
            String property = (String) IntrospectionUtils.getProperty(object, fieldName);
            if (StringUtils.isNoneBlank(property)) {
                return true;
            }
        }
        return false;
    }

}

ValidateMobile

在字段上添加@ValidateMobile

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

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

// 约束注解应用的目标元素类型(TYPE   ,METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER等)
@Target({TYPE, METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
// 约束注解应用的时机
@Retention(RUNTIME)
@Documented
// 与约束注解关联的验证器
@Constraint(validatedBy = { MobileValidator.class})
public @interface ValidateMobile {

    //是否校验
    boolean required() default true;
    // 约束注解验证时的输出消息
    String message() default "手机号码格式不正确";
    // 约束注解在验证时所属的组别
    Class<?>[] groups() default { };
    // 约束注解的有效负载
    Class<? extends Payload>[] payload() default { };

}

MobileValidator


import cn.boommanpro.util.MobileUtil;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class MobileValidator implements ConstraintValidator<ValidateMobile,String> {


    private boolean required = false;

    @Override
    public void initialize(ValidateMobile constraintAnnotation) {
        required = constraintAnnotation.required();
    }


    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (required) {
            return MobileUtil.isMobile(value);
        }
        return true;

    }


}

MobileUtil 判断手机号码的工具类

public class MobileUtil {

    /**
     * 判断字符串是否符合手机号码格式
     * 移动号段: 134,135,136,137,138,139,147,150,151,152,157,158,159,170,178,182,183,184,187,188
     * 联通号段: 130,131,132,145,155,156,170,171,175,176,185,186
     * 电信号段: 133,149,153,170,173,177,180,181,189
     *
     * @return 待检测的字符串
     */
    public static boolean isMobile(String mobileNums) {
        // "[1]"代表第1位为数字1,"[358]"代表第二位可以为3、5、8中的一个,"\\d{9}"代表后面是可以是0~9的数字,有9位。
        String telRegex = "^(0|86|17951)?(13[0-9]|15[012356789]|16[123456789]|17[0135678]|199|18[0-9]|14[579])[0-9|*]{4}[0-9]{4}$";

        if (StringUtils.isNullOrEmpty(mobileNums)) {
            return false;
        } else {
            return mobileNums.matches(telRegex);
        }

    }

    public static boolean isNotMobile(String mobileNums) {
        return !isMobile(mobileNums);
    }
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值