栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

spring-java使用Validation参数验证-自定义校验器

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

spring-java使用Validation参数验证-自定义校验器

在开发JAVA服务器端代码时,我们会遇到对外部传来的参数合法性进行验证,而hibernate-validator提供了一些常用的参数校验注解,我们可以拿来使用。
spring-boot-web,内嵌了hibernate-validator,并且hibernate-validator依赖tomcat-el包。在使用webflux容器下,会报错。将 hibernate-validator 改为 spring-boot-starter-validation,其中有jakarta.el替代实现el

参考

JAVA中通过Hibernate-Validation进行参数验证
Validation校验框架的自定义注解
Spring WebFlux - 使用@Valid校验

使用-Tomcat


pom引入jar

    
        spring-boot-starter-parent
        org.springframework.boot
        2.0.9.RELEASE
        
    
    
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            org.hibernate.validator
            hibernate-validator
        
    

配置类,快速失败模式

import org.hibernate.validator.HibernateValidator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
@Configuration
public class ValidationCfg {
    private static final Logger log = LoggerFactory.getLogger(ValidationCfg.class);
    @Bean
    public Validator validator(){
        // 如果存在 自定义注解校验器,请 增加入参 AutowireCapableBeanFactory autowireCapableBeanFactory
        // 并增加调用
        // .failFast( true ) .constraintValidatorFactory(new SpringConstraintValidatorFactory(autowireCapableBeanFactory))
        log.info("spring validator 快速失败模式");
        try(ValidatorFactory vf = Validation.byProvider(HibernateValidator.class)
                .configure().failFast(true)
                .buildValidatorFactory()){
            return vf.getValidator();
        }
    }
}

实体类dto字段使用校验注解

使用方式

import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import org.hibernate.validator.HibernateValidator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Resource;
public class ValidationUtil{
    private static final Logger logger = LoggerFactory.getLogger(ValidationUtil.class);
	
	@Resource
	private Validator validator;
	
    public  void validateBean(T t){
        logger.debug("使用HibernateValidator校验实体类, {}",t);
        Set> validate = validator.validate(t);
        if(!validate.isEmpty()){
            List list = validate.stream().map(ConstraintViolation::getMessage)
            .collect(Collectors.toList());
            logger.debug("使用HibernateValidator校验实体类-, {}",list);
            throw new RuntimeException(list.get(0));
        }
    }
    
	
	private static Validator validate = Validation.byProvider(HibernateValidator.class).configure()
	.failFast(true).buildValidatorFactory().getValidator();
	
	public static  void validate(T obj) {
		Set> constraintViolations = validate.validate(obj);
		// 抛出检验异常
		if (constraintViolations.size() > 0) {
			throw new RuntimeException(String.format("0001参数校验失败:%s",
			constraintViolations.iterator().next().getMessage()));
		}
  }
}
自定义校验注解

validation框架的自定义注解实现必要容易

  1. 自定义一个注解类
  2. 重写校验器
定义接口-固定校验方法

参考别人的自定义代码时候,发现使用反射方式+方法字符串,执行验证方法。就考虑怎么固定方法并且不适用反射方式。通过接口方式就实现了。

public interface AbsValid {
    
    boolean eq(String val);
    
    String key();
}
自定义校验注解
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


@Constraint (validatedBy = EnumValidator.class)
@Retention (RetentionPolicy.RUNTIME)
@Target ({ElementType.ANNOTATION_TYPE,ElementType.FIELD,ElementType.METHOD})
public @interface EnumValid {
    
    String message() default "值不匹配";
    
    Class[] groups() default {};
    
    Class[] payload() default {};
    
    Class> enumClass();
}
校验器-改进后
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;


public class EnumValidator implements ConstraintValidator {
    private Class> enumClass;
    private Enum[] ecs;
    @Override
    public void initialize(EnumValid ca) {
        enumClass = ca.enumClass();
        // 获取枚举列表
        ecs = enumClass.getEnumConstants();
    }
    @Override
    public boolean isValid(String val, ConstraintValidatorContext cvc) {
        if( null == val || val.isEmpty() ){ return true; }
        if( null == enumClass || null == ecs){ return true; }
        for (Enum ec : ecs) {
            if(((AbsValid) ec).eq(val)){ return true; }
        }
        return false;
    }
}
自定义枚举-校验使用
public enum GenderEnum implements AbsValid {
    M("1","男"),
    W("2","女"),
    X("9","未知"),
    ;
    private final String key;
    private final String name;
    GenderEnum(String key,String name){
        this.key = key;
        this.name = name;
    }
    @Override
    public boolean eq(String val) {
        return key.equals(val);
    }
    @Override
    public String key() {
        return key;
    }
    public String getName() {
        return name;
    }
}
测试-bean
import org.springframework.validation.annotation.Validated;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;


@Validated
public class ValidBean {
    @NotNull (message = "name不能为null")
    private Long id;
    @NotBlank (message = "name不能为空")
    private String name;
    @NotBlank(message = "gender不能为空")
    @EnumValid(enumClass = GenderEnum.class, message = "gender非法")
    private String gender;

    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public ValidBean id(Long id) { this.id = id; return this; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public ValidBean name(String name) { this.name = name;return this; }
    public String getGender() { return gender; }
    public void setGender(String gender) { this.gender = gender; }
    public ValidBean gender(String gender) { this.gender = gender;return this; }

    @Override
    public String toString() {
        return "ValidBean{id=" + id + ", name='" + name + "', gender='" + gender + "'}";
    }
}
测试类
import org.hibernate.validator.HibernateValidator;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import java.util.Set;


public class ValidTest {
    public static void main(String[] args) {
        Validator validator = Validation.byProvider(HibernateValidator.class)
                .configure().failFast(true).buildValidatorFactory().getValidator();
        ValidBean bean = new ValidBean().id(1L).name("a").gender("1");
        valid(validator,bean);
        bean = new ValidBean().id(1L).name("b").gender("3");
        valid(validator,bean);
        bean = new ValidBean().id(null).name(null).gender(null);
        valid(validator,bean);
    }
    public static  void valid(Validator validator,T t){
        System.out.println("start:"+t);
        Set> vs = validator.validate(t);
        if( null == vs || vs.isEmpty()){
            System.out.println("通过");
        }else {
            for (ConstraintViolation v : vs) {
                System.out.println("异常:"+v.getMessage());
            }
        }
        System.out.println("end<<<<");
    }
}
测试-日志
start:ValidBean{id=1, name='a', gender='1'}
通过
end<<<<
start:ValidBean{id=1, name='b', gender='3'}
异常:gender非法
end<<<<
start:ValidBean{id=null, name='null', gender='null'}
异常:gender不能为空
end<<<<

使用-webflux

webflux容器启动模式下,hibernate-validator使用会报错,除了pom其他都相同
pom

    
        spring-boot-starter-parent
        org.springframework.boot
        2.3.1.RELEASE
        
    
    


        
            org.springframework.boot
            spring-boot-starter-webflux
        
        
            org.hibernate.validator
            hibernate-validator
        
    

执行ValidTest测试类,报错

16:06:25.298 [main] DEBUG org.jboss.logging - Logging Provider: org.jboss.logging.Log4j2LoggerProvider
16:06:25.309 [main] INFO org.hibernate.validator.internal.util.Version - HV000001: Hibernate Validator 6.1.5.Final
16:06:25.323 [main] DEBUG org.hibernate.validator.internal.xml.config.ValidationXmlParser - Trying to load META-INF/validation.xml for XML based Validator configuration.
16:06:25.326 [main] DEBUG org.hibernate.validator.internal.xml.config.ResourceLoaderHelper - Trying to load META-INF/validation.xml via TCCL
16:06:25.327 [main] DEBUG org.hibernate.validator.internal.xml.config.ResourceLoaderHelper - Trying to load META-INF/validation.xml via Hibernate Validator's class loader
16:06:25.328 [main] DEBUG org.hibernate.validator.internal.xml.config.ValidationXmlParser - No META-INF/validation.xml found. Using annotation based configuration only.
16:06:25.341 [main] DEBUG org.hibernate.validator.internal.engine.resolver.TraversableResolvers - Cannot find javax.persistence.Persistence on classpath. Assuming non JPA 2 environment. All properties will per default be traversable.
16:06:25.405 [main] DEBUG org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator - Failed to load expression factory via classloader sun.misc.Launcher$AppClassLoader@18b4aac2
java.lang.NoClassDefFoundError: javax/el/ExpressionFactory
	at org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator.canLoadExpressionFactory(ResourceBundleMessageInterpolator.java:216)
	at org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator.buildExpressionFactory(ResourceBundleMessageInterpolator.java:170)
	at org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator.(ResourceBundleMessageInterpolator.java:94)
	at org.hibernate.validator.internal.engine.AbstractConfigurationImpl.getDefaultMessageInterpolator(AbstractConfigurationImpl.java:570)
	at org.hibernate.validator.internal.engine.AbstractConfigurationImpl.getDefaultMessageInterpolatorConfiguredWithClassLoader(AbstractConfigurationImpl.java:790)
	at org.hibernate.validator.internal.engine.AbstractConfigurationImpl.getMessageInterpolator(AbstractConfigurationImpl.java:480)
	at org.hibernate.validator.internal.engine.ValidatorFactoryImpl.(ValidatorFactoryImpl.java:151)
	at org.hibernate.validator.HibernateValidator.buildValidatorFactory(HibernateValidator.java:38)
	at org.hibernate.validator.internal.engine.AbstractConfigurationImpl.buildValidatorFactory(AbstractConfigurationImpl.java:430)
	at *.*.*.*.test.ValidTest.main(ValidTest.java:20)
Caused by: java.lang.ClassNotFoundException: javax.el.ExpressionFactory
	at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:335)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	... 10 common frames omitted
16:06:25.407 [main] DEBUG org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator - Failed to load expression factory via classloader sun.misc.Launcher$AppClassLoader@18b4aac2
java.lang.NoClassDefFoundError: javax/el/ExpressionFactory
	at org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator.canLoadExpressionFactory(ResourceBundleMessageInterpolator.java:216)
	at org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator.buildExpressionFactory(ResourceBundleMessageInterpolator.java:183)
	at org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator.(ResourceBundleMessageInterpolator.java:94)
	at org.hibernate.validator.internal.engine.AbstractConfigurationImpl.getDefaultMessageInterpolator(AbstractConfigurationImpl.java:570)
	at org.hibernate.validator.internal.engine.AbstractConfigurationImpl.getDefaultMessageInterpolatorConfiguredWithClassLoader(AbstractConfigurationImpl.java:790)
	at org.hibernate.validator.internal.engine.AbstractConfigurationImpl.getMessageInterpolator(AbstractConfigurationImpl.java:480)
	at org.hibernate.validator.internal.engine.ValidatorFactoryImpl.(ValidatorFactoryImpl.java:151)
	at org.hibernate.validator.HibernateValidator.buildValidatorFactory(HibernateValidator.java:38)
	at org.hibernate.validator.internal.engine.AbstractConfigurationImpl.buildValidatorFactory(AbstractConfigurationImpl.java:430)
	at *.*.*.*.test.ValidTest.main(ValidTest.java:20)
Exception in thread "main" javax.validation.ValidationException: HV000183: Unable to initialize 'javax.el.ExpressionFactory'. Check that you have the EL dependencies on the classpath, or use ParameterMessageInterpolator instead
	at org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator.buildExpressionFactory(ResourceBundleMessageInterpolator.java:199)
	at org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator.(ResourceBundleMessageInterpolator.java:94)
	at org.hibernate.validator.internal.engine.AbstractConfigurationImpl.getDefaultMessageInterpolator(AbstractConfigurationImpl.java:570)
	at org.hibernate.validator.internal.engine.AbstractConfigurationImpl.getDefaultMessageInterpolatorConfiguredWithClassLoader(AbstractConfigurationImpl.java:790)
	at org.hibernate.validator.internal.engine.AbstractConfigurationImpl.getMessageInterpolator(AbstractConfigurationImpl.java:480)
	at org.hibernate.validator.internal.engine.ValidatorFactoryImpl.(ValidatorFactoryImpl.java:151)
	at org.hibernate.validator.HibernateValidator.buildValidatorFactory(HibernateValidator.java:38)
	at org.hibernate.validator.internal.engine.AbstractConfigurationImpl.buildValidatorFactory(AbstractConfigurationImpl.java:430)
	at *.*.*.*.test.ValidTest.main(ValidTest.java:20)
Caused by: java.lang.NoClassDefFoundError: javax/el/ELManager
	at org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator.buildExpressionFactory(ResourceBundleMessageInterpolator.java:191)
	... 8 more
Caused by: java.lang.ClassNotFoundException: javax.el.ELManager
	at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:335)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	... 9 more

解决方法

将 hibernate-validator 改为 spring-boot-starter-validation,其中有jakarta.el替代实现el

        
            org.springframework.boot
            spring-boot-starter-validation
        

日志

start:ValidBean{id=1, name='a', gender='1'}
通过
end<<<<
start:ValidBean{id=1, name='b', gender='3'}
异常:gender非法
end<<<<
start:ValidBean{id=null, name='null', gender='null'}
异常:name不能为空
end<<<<
校验注解列表 bean-javax提供
注解说明
@AssertTrue用于boolean字段,该字段只能为true
@AssertFalse该字段的值只能为false
@DecimalMax只能小于或等于该值
@DecimalMin只能大于或等于该值
@Digits(integer=,fraction=)检查是否是一种数字的整数、分数,小数位数的数字
@Future检查该字段的日期是否是属于将来的日期
@Max该字段的值只能小于或等于该值
@Min该字段的值只能大于或等于该值
@NotNull不能为null,任何对象的value不能为null
@Null检查该字段为空
@Past检查该字段的日期是在过去
@Pattern(regex=,flag=)被注释的元素必须符合指定的正则表达式
@Size(min=, max=)检查该字段的size是否在min和max之间,可以是字符串、数组、集合、Map等
@Valid该注解主要用于字段为一个包含其他对象的集合或map或数组的字段,或该字段直接为一个其他对象的引用,这样在检查当前对象的同时也会检查该字段所引用的对象
Hibernate
注解说明
@Email检查是否是一个有效的email地址
@Length(min=,max=)检查所属的字段的长度是否在min和max之间,只能用于字符串
@NotEmpty不能为空,这里的空是指空字符串,集合对象的元素不为0,即集合不为空,也可以用于字符串不为null
@Range(min=,max=,message=)被注释的元素必须在合适的范围内
@NotBlank不能为空,检查时会将空格忽略,只能用于字符串不为null,并且字符串trim()以后length要大于0
@URL(protocol=,host=, port=, regexp=, flags=)检查是否是一个有效的URL,如果提供了protocol,host等,则该URL还需满足提供的条件
@CreditCardNumber对信用卡号进行一个大致的验证
@Validated支持分组,用在类型、方法和方法参数上。但不能用在成员属性上
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1021314.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号