hibernate-validator如何使用校验框架

目录
  • 一、前言
  • 二、常用注解说明
  • 三、定义校验分组
  • 四、定义校验Bean
  • 五、validator基本使用
  • 六、借助BindingResult
  • 七、全局拦截校验器
  • 八、借助ValidatorUtils工具类
  • 九、自定义校验器

一、前言

高效、合理的使用hibernate-validator校验框架可以提高程序的可读性,以及减少不必要的代码逻辑。接下来会介绍一下常用一些使用方式。

二、常用注解说明

限制 说明
@Null 限制只能为null
@NotNull 限制必须不为null
@AssertFalse 限制必须为false
@AssertTrue 限制必须为true
@DecimalMax(value) 限制必须为一个不大于指定值的数字
@DecimalMin(value) 限制必须为一个不小于指定值的数字
@Digits(integer,fraction) 限制必须为一个小数,且整数部分的位数不能超过integer,小数部分的位数不能超过fraction
@Future 限制必须是一个将来的日期
@Max(value) 限制必须为一个不大于指定值的数字
@Min(value) 限制必须为一个不小于指定值的数字
@Past 限制必须是一个过去的日期
@Pattern(value) 限制必须符合指定的正则表达式
@Size(max,min) 限制字符长度必须在min到max之间
@Past 验证注解的元素值(日期类型)比当前时间早
@NotEmpty 验证注解的元素值不为null且不为空(字符串长度不为0、集合大小不为0)
@NotBlank 验证注解的元素值不为空(不为null、去除首位空格后长度为0),不同于@NotEmpty,@NotBlank只应用于字符串且在比较时会去除字符串的空格
@Email 验证注解的元素值是Email,也可以通过正则表达式和flag指定自定义的email格式

三、定义校验分组

public class ValidateGroup {
    public interface FirstGroup {
    }

    public interface SecondeGroup {
    }

    public interface ThirdGroup {
    }
}

四、定义校验Bean

@Validated
@GroupSequence({ValidateGroup.FirstGroup.class, BaseMessageRequestBean.class})
public class BaseMessageRequestBean {

    //渠道类型
    @NotNull(message = "channelType为NULL", groups = ValidateGroup.FirstGroup.class)
    private String channelType;

    //消息(模板消息或者普通消息)
    @NotNull(message = "data为NUll", groups = ValidateGroup.FirstGroup.class)
    @Valid
    private Object data;

    //业务类型
    @NotNull(message = "bizType为NULL", groups = ValidateGroup.FirstGroup.class)
    private String bizType;

    //消息推送对象
    @NotBlank(message = "toUser为BLANK", groups = ValidateGroup.FirstGroup.class)
    private String toUser;

    private long createTime = Instant.now().getEpochSecond();

    ......
}

请自行参考:@Validated和@Valid区别

五、validator基本使用

@RestController
public class TestValidatorController {
    @RequestMapping("/test/validator")
    public void test(@Validated BaseMessageRequestBean bean){
        ...
    }
}

  这种使用方式有一个弊端,不能自定义返回异常。spring如果验证失败,则直接抛出异常,一般不可控。

六、借助BindingResult

@RestController
public class TestValidatorController {
    @RequestMapping("/test/validator")
    public void test(@Validated BaseMessageRequestBean bean, BindingResult result){
        result.getAllErrors();
        ...
    }
}

  如果方法中有BindingResult类型的参数,spring校验完成之后会将校验结果传给这个参数。通过BindingResult控制程序抛出自定义类型的异常或者返回不同结果。

七、全局拦截校验器

  当然了,需要在借助BindingResult的前提下...

@Aspect
@Component
public class ControllerValidatorAspect {
    @Around("execution(* com.*.controller..*.*(..)) && args(..,result)")
    public Object doAround(ProceedingJoinPoint pjp, result result) {
        result.getFieldErrors();
        ...
    }
}

  这种方式可以减少controller层校验的代码,校验逻辑统一处理,更高效。

八、借助ValidatorUtils工具类

@Bean
public Validator validator() {
    return new LocalValidatorFactoryBean();
}

LocalValidatorFactoryBean官方示意

  LocalValidatorFactoryBean是Spring应用程序上下文中javax.validation(JSR-303)设置的中心类:它引导javax.validation.ValidationFactory并通过Spring Validator接口以及JSR-303 Validator接口和ValidatorFactory公开它。界面本身。通过Spring或JSR-303 Validator接口与该bean的实例进行通信时,您将与底层ValidatorFactory的默认Validator进行通信。这非常方便,因为您不必在工厂执行另一个调用,假设您几乎总是会使用默认的Validator。这也可以直接注入Validator类型的任何目标依赖项!从Spring 5.0开始,这个类需要Bean Validation 1.1+,特别支持Hibernate Validator 5.x(参见setValidationMessageSource(org.springframework.context.MessageSource))。这个类也与Bean Validation 2.0和Hibernate Validator 6.0运行时兼容,有一个特别说明:如果你想调用BV 2.0的getClockProvider()方法,通过#unwrap(ValidatorFactory.class)获取本机ValidatorFactory,在那里调用返回的本机引用上的getClockProvider()方法。Spring的MVC配置命名空间也使用此类,如果存在javax.validation API但未配置显式Validator。

public class ValidatorUtils implements ApplicationContextAware {

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        ValidatorUtils.validator = (Validator) applicationContext.getBean("validator");
    }

    private static Validator validator;

    public static Optional<String> validateResultProcess(Object obj)  {
        Set<ConstraintViolation<Object>> results = validator.validate(obj);
        if (CollectionUtils.isEmpty(results)) {
            return Optional.empty();
        }
        StringBuilder sb = new StringBuilder();

        for (Iterator<ConstraintViolation<Object>> iterator = results.iterator(); iterator.hasNext(); ) {
            sb.append(iterator.next().getMessage());
            if (iterator.hasNext()) {
                sb.append(" ,");
            }
        }
        return Optional.of(sb.toString());
    }
}

  为什么要使用这个工具类呢?

  1、controller方法中不用加入BindingResult参数

  2、controller方法中需要校验的参数也不需要加入@Valid或者@Validated注解

  怎么样是不是又省去了好多代码,开不开心。

  具体使用,在controller方法或者全局拦截校验器中调用ValidatorUtils.validateResultProcess(需要校验的Bean) 直接获取校验的结果。

  请参考更多功能的ValidatorUtils工具类

九、自定义校验器

  定义一个MessageRequestBean,继承BaseMessageRequestBean,signature字段需要我们自定义校验逻辑。

@Validated
@GroupSequence({ValidateGroup.FirstGroup.class, ValidateGroup.SecondeGroup.class, MessageRequestBean.class})
@LogicValidate(groups = ValidateGroup.SecondeGroup.class)
public class MessageRequestBean extends BaseMessageRequestBean {

    //签名信息(除该字段外的其他字段按照字典序排序,将值顺序拼接在一起,进行md5+Base64签名算法)
    @NotBlank(message = "signature为BLANK", groups = ValidateGroup.FirstGroup.class)
    private String signature;
    ...
}

  实现自定义校验逻辑也很简单......

  1、自定义一个带有 @Constraint注解的注解@LogicValidate,validatedBy 属性指向该注解对应的自定义校验器

@Target({TYPE})
@Retention(RUNTIME)
//指定验证器
@Constraint(validatedBy = LogicValidator.class)
@Documented
public @interface LogicValidate {
    String message() default "校验异常";
    //分组
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

  2、自定义校验器LogicValidator,泛型要关联上自定义的注解和需要校验bean的类型

public class LogicValidator implements ConstraintValidator<LogicValidate, MessageRequestBean> {

    @Override
    public void initialize(LogicValidate logicValidate) {
    }

    @Override
    public boolean isValid(MessageRequestBean messageRequestBean, ConstraintValidatorContext context) {
        String toSignature = StringUtils.join( messageRequestBean.getBizType()
                , messageRequestBean.getChannelType()
                , messageRequestBean.getData()
                , messageRequestBean.getToUser());
        String signature = new Base64().encodeAsString(DigestUtils.md5(toSignature));
        if (!messageRequestBean.getSignature().equals(signature)) {
            context.disableDefaultConstraintViolation();
            context.buildConstraintViolationWithTemplate("signature校验失败")
                    .addConstraintViolation();
            return false;
        }
        return true;
    }
}

  可以通过ConstraintValidatorContext禁用掉默认的校验配置,然后自定义校验配置,比如校验失败后返回的信息。

十、springboot国际化信息配置

@Configuration
@ConditionalOnMissingBean(value = MessageSource.class, search = SearchStrategy.CURRENT)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Conditional(ResourceBundleCondition.class)
@EnableConfigurationProperties
@ConfigurationProperties(prefix = "spring.messages")
public class MessageSourceAutoConfiguration {

    private static final Resource[] NO_RESOURCES = {};

    /**
     * Comma-separated list of basenames, each following the ResourceBundle convention.
     * Essentially a fully-qualified classpath location. If it doesn't contain a package
     * qualifier (such as "org.mypackage"), it will be resolved from the classpath root.
     */
    private String basename = "messages";

    /**
     * Message bundles encoding.
     */
    private Charset encoding = Charset.forName("UTF-8");

    /**
     * Loaded resource bundle files cache expiration, in seconds. When set to -1, bundles
     * are cached forever.
     */
    private int cacheSeconds = -1;

    /**
     * Set whether to fall back to the system Locale if no files for a specific Locale
     * have been found. if this is turned off, the only fallback will be the default file
     * (e.g. "messages.properties" for basename "messages").
     */
    private boolean fallbackToSystemLocale = true;

    /**
     * Set whether to always apply the MessageFormat rules, parsing even messages without
     * arguments.
     */
    private boolean alwaysUseMessageFormat = false;

    @Bean
    public MessageSource messageSource() {
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        if (StringUtils.hasText(this.basename)) {
            messageSource.setBasenames(StringUtils.commaDelimitedListToStringArray(
                    StringUtils.trimAllWhitespace(this.basename)));
        }
        if (this.encoding != null) {
            messageSource.setDefaultEncoding(this.encoding.name());
        }
        messageSource.setFallbackToSystemLocale(this.fallbackToSystemLocale);
        messageSource.setCacheSeconds(this.cacheSeconds);
        messageSource.setAlwaysUseMessageFormat(this.alwaysUseMessageFormat);
        return messageSource;
    }

    public String getBasename() {
        return this.basename;
    }

    public void setBasename(String basename) {
        this.basename = basename;
    }

    public Charset getEncoding() {
        return this.encoding;
    }

    public void setEncoding(Charset encoding) {
        this.encoding = encoding;
    }

    public int getCacheSeconds() {
        return this.cacheSeconds;
    }

    public void setCacheSeconds(int cacheSeconds) {
        this.cacheSeconds = cacheSeconds;
    }

    public boolean isFallbackToSystemLocale() {
        return this.fallbackToSystemLocale;
    }

    public void setFallbackToSystemLocale(boolean fallbackToSystemLocale) {
        this.fallbackToSystemLocale = fallbackToSystemLocale;
    }

    public boolean isAlwaysUseMessageFormat() {
        return this.alwaysUseMessageFormat;
    }

    public void setAlwaysUseMessageFormat(boolean alwaysUseMessageFormat) {
        this.alwaysUseMessageFormat = alwaysUseMessageFormat;
    }

    protected static class ResourceBundleCondition extends SpringBootCondition {

        private static ConcurrentReferenceHashMap<String, ConditionOutcome> cache = new ConcurrentReferenceHashMap<String, ConditionOutcome>();

        @Override
        public ConditionOutcome getMatchOutcome(ConditionContext context,
                AnnotatedTypeMetadata metadata) {
            String basename = context.getEnvironment()
                    .getProperty("spring.messages.basename", "messages");
            ConditionOutcome outcome = cache.get(basename);
            if (outcome == null) {
                outcome = getMatchOutcomeForBasename(context, basename);
                cache.put(basename, outcome);
            }
            return outcome;
        }

        private ConditionOutcome getMatchOutcomeForBasename(ConditionContext context,
                String basename) {
            ConditionMessage.Builder message = ConditionMessage
                    .forCondition("ResourceBundle");
            for (String name : StringUtils.commaDelimitedListToStringArray(
                    StringUtils.trimAllWhitespace(basename))) {
                for (Resource resource : getResources(context.getClassLoader(), name)) {
                    if (resource.exists()) {
                        return ConditionOutcome
                                .match(message.found("bundle").items(resource));
                    }
                }
            }
            return ConditionOutcome.noMatch(
                    message.didNotFind("bundle with basename " + basename).atAll());
        }

        private Resource[] getResources(ClassLoader classLoader, String name) {
            try {
                return new PathMatchingResourcePatternResolver(classLoader)
                        .getResources("classpath*:" + name + ".properties");
            }
            catch (Exception ex) {
                return NO_RESOURCES;
            }
        }

    }

}

  从上面的MessageSource自动配置可以看出,可以通过spring.message.basename指定要配置国际化文件位置,默认值是“message”。spring boot默认就支持国际化的,默认会去resouces目录下寻找message.properties文件。

  这里就不进行过多关于国际化相关信息的介绍了,肯定少不了区域解析器。springboot国际化相关知识请参考:Spring Boot国际化(i18n)

到此这篇关于hibernate-validator如何使用校验框架的文章就介绍到这了,更多相关hibernate-validator校验内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Springboot hibernate-validator 6.x快速校验示例代码

    目录 一.版本信息及maven依赖 二.定义实体bean 三.测试Controller 四.hibernate-validator全局异常处理 五.hibernate-validator快速校验 一.版本信息及maven依赖 hibernate-validator 7.x版本有问题,暂时以6.2.1.Final为例,6.2.1.Final版本解决了Log4j版本的漏洞 <properties> <!-- JDK版本 --> <java.version>1.8</j

  • SpringMVC使用hibernate-validator进行参数校验最佳实践记录

    在我们用Controller接收参数后,往往需要对参数进行校验.如果我们手写校验的话,就会有一堆的判空代码,看起来很不优雅,写起来也费时费力.下面来看下通过hibernate-validator来进行优雅的参数校验. 首先需要引入依赖: <dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> <v

  • Hibernate Validator实现更简洁的参数校验及一个util

    代码地址 https://github.com/wheel-organization/wheel 简介 hibernate-validator是Hibernate项目中的一个数据校验框架,是Bean Validation 的参考实现,hibernate-validator除了提供了JSR 303规范中所有内置constraint 的实现,还有一些附加的constraint. 使用hibernate-validator能够将数据校验从业务代码中脱离出来,增加代码可读性,同时也让数据校验变得更加方便

  • springboot使用hibernate validator校验方式

    一.参数校验 在开发中经常需要写一些字段校验的代码,比如字段非空,字段长度限制,邮箱格式验证等等,写这些与业务逻辑关系不大的代码个人感觉有两个麻烦: 验证代码繁琐,重复劳动 方法内代码显得冗长 每次要看哪些参数验证是否完整,需要去翻阅验证逻辑代码 hibernate validator(官方文档)提供了一套比较完善.便捷的验证实现方式. spring-boot-starter-web包里面有hibernate-validator包,不需要引用hibernate validator依赖. 二.hi

  • SpringBoot 使用hibernate validator校验

    本文将全面的介绍如何使用 validator 进行数据校验 本文源码: https://gitee.com/yintianwen7/taven-springboot-learning/tree/master/springboot-validate 准备工作 我们只需要引入 spring-boot-starter-web 包即可使用 1.常用注解 常用注解 2.简单的实体校验 public class CardDTO { @NotBlank private String cardId; @Size

  • Hibernate validator使用以及自定义校验器注解

    JSR-303 是 JAVA EE 6 中的一项子规范,叫做 Bean Validation,用于对 Java Bean 中的字段的值进行验证.Hibernate Validator则是Hibdernate提供的一种对该规范的实现. --即Hibernate Validator是用来做参数校验. 使用Hibernate Validator提供的注解进行参数校验 注解 释义 @Null 必须为null @NotNull 不能为null @AssertTrue 必须为true @AssertFals

  • springboot使用校验框架validation校验的示例

    b/s系统中对http请求数据的校验多数在客户端进行,这也是出于简单及用户体验性上考虑,但是在一些安全性要求高的系统中服务端校验是不可缺少的. Spring3支持JSR-303验证框架,JSR-303 是Java EE 6 中的一项子规范,叫做BeanValidation,官方参考实现是hibernate Validator(与Hibernate ORM 没有关系),JSR 303 用于对Java Bean 中的字段的值进行验证. Validator主要是校验用户提交的数据的合理性的,比如是否为

  • hibernate-validator如何使用校验框架

    目录 一.前言 二.常用注解说明 三.定义校验分组 四.定义校验Bean 五.validator基本使用 六.借助BindingResult 七.全局拦截校验器 八.借助ValidatorUtils工具类 九.自定义校验器 一.前言 高效.合理的使用hibernate-validator校验框架可以提高程序的可读性,以及减少不必要的代码逻辑.接下来会介绍一下常用一些使用方式. 二.常用注解说明 限制 说明 @Null 限制只能为null @NotNull 限制必须不为null @AssertFa

  • Spring自带的校验框架Validation的使用实例

    1.首先来一个项目结构图以及所需jar包 可以看到,没有添加除了日志和jstl相关的jar包外,没有其他spring以外的第三方jar包(即使用的是spring自带的校验框架). 2.从前台页面开始,首先是接收用户信息的表单loginForm.jsp <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@

  • springboot validator枚举值校验功能实现

    这篇文章主要介绍了springboot validator枚举值校验功能实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 一.前言 在spring项目中,校验参数功能使用hibernate validator是一个不错的选择,我们的项目中也是使用它来进行校验的,省去了很多难看的校验逻辑,使代码的可读性也大大增加,本章将带你使用hibernate validator自定义注解功能实现一个 枚举值校验的逻辑. 二.需求 我们先明确下我们的需求,在

  • SpringBoot整合Hibernate Validator实现参数验证功能

    在前后端分离的开发模式中,后端对前端传入的参数的校验成了必不可少的一个环节.但是在多参数的情况下,在controller层加上参数验证,会显得特别臃肿,并且会有许多的重复代码.这里可以引用Hibernate Validator来解决这个问题,直接在实体类进行参数校验,验证失败直接返回错误信息给前端,减少controller层的代码量. 一.xml引入Hibernate Validator <!-- 验证器 --> <dependency> <groupId>org.hi

  • springboot使用hibernate validation对参数校验的实现方法

    springboot天生支持使用hibernate validation对参数的优雅校验,如果不使用它,只能对参数挨个进行如下方式的手工校验,不仅难看,使用起来还很不方便: if(StringUtils.isEmpty(userName)){ throw new RuntimeException("用户名不能为空"); } 下面将介绍hibernate validation的基本使用方法. 一.引入依赖 这里在springboot 2.4.1中进行实验,引入以下依赖: <pare

随机推荐