写在开头
编写不易,如有问题欢迎留言。转载请带原文链接
使用场景
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);
}
}