热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

javabean属性验证框架valid

项目介绍java开发中,参数校验是非常常见的需求。但是hibernate-validator在使用过程中,依然会存在一些问题。特性支持fluent-validati

项目介绍

java 开发中,参数校验是非常常见的需求。

但是 hibernate-validator 在使用过程中,依然会存在一些问题。

特性

  • 支持 fluent-validation

  • 支持 jsr-303 注解

  • 支持 i18n

  • 支持用户自定义策略

  • 支持用户自定义注解

开源地址

valid

创作目的

hibernate-validator 无法满足的场景

如今 java 最流行的 hibernate-validator 框架,但是有些场景是无法满足的。

比如:

  1. 验证新密码和确认密码是否相同。(同一对象下的不同属性之间关系)

  2. 当一个属性值满足某个条件时,才进行其他值的参数校验。

  3. 多个属性值,至少有一个不能为 null

其实,在对于多个字段的关联关系处理时,hibernate-validator 就会比较弱。

本项目结合原有的优点,进行这一点的功能强化。

validation-api 过于复杂

validation-api 提供了丰富的特性定义,也同时带来了一个问题。

实现起来,特别复杂。

然而我们实际使用中,常常不需要这么复杂的实现。

valid-api 提供了一套简化很多的 api,便于用户自行实现。

自定义缺乏灵活性

hibernate-validator 在使用中,自定义约束实现是基于注解的,针对单个属性校验不够灵活。

本项目中,将属性校验约束和注解约束区分开,便于复用和拓展。

过程式编程 vs 注解式编程

hibernate-validator 核心支持的是注解式编程,基于 bean 的校验。

一个问题是针对属性校验不灵活,有时候针对 bean 的校验,还是要自己写判断。

本项目支持 fluent-api 进行过程式编程,同时支持注解式编程。

尽可能兼顾灵活性与便利性。

项目模块说明

模块名称 说明
valid-api 核心 api 及注解定义
valid-core 针对 valid-api 的核心实现
valid-jsr 针对 JSR-303 标准注解的实现
valid-test 测试代码模块

依赖说明

valid-core 默认引入 valid-api

valid-jsr 默认引入 valid-core

快速开始

准备工作

JDK1.7+

Maven 3.X+

maven 引入


    com.github.houbb
    valid-jsr
    0.1.2

例子

我们直接利用 jsr 内置的约束类:

public void helloValidTest() {
    IResult result = ValidBs.on(null, JsrConstraints.notNullConstraint())
            .result()
            .print();
    Assert.assertFalse(result.pass());
}

对应日志输出为:

DefaultResult{pass=false, notPassList=[DefaultConstraintResult{pass=false, message='预期值为 ,实际值为 ', value=null, cOnstraint='NotNullConstraint', expectValue='not null'}], allList=null}

方法初步说明

ValidBs 用来进行验证的引导类,上述的写法等价于如下:

public void helloValidAllConfigTest() {
    IResult result = ValidBs.on(null, JsrConstraints.notNullConstraint())
            .fail(Fails.failFast())
            .group()
            .valid(DefaultValidator.getInstance())
            .result()
            .print();
    Assert.assertFalse(result.pass());
}

on(Object value, IConstraint... constraints) 指定约束

Object 可以是对象,也可以是普通的值。

constraints 为对应的约束列表,为默认的约束验证提供便利性。

IConstraint 相关创建工具类 ConstraintsJsrConstraints

fail(IFail fail)

可以指定失败时的处理策略,支持用户自定义失败策略。

实现 说明
failOver 失败后继续验证,直到验证完所有属性
failFast 失败后快速返回

group(Class[] classes) 支持分组验证

有时候我们希望,只验证指定某一分组的约束。

可以通过 group() 属性指定,与 IConstraint 中的 group() 属性匹配的约束才会被执行。

valid(IValidator validator) 支持验证策略

默认为 DefaultValidator,为 valid-api 的实现验证。

如果你希望使用 jsr-303 注解,可以使用 JsrValidator

支持自定义验证策略。

result(IResultHandler resultHandler) 验证结果处理

默认为 simple() 的简单结果处理。

可以指定为 detail() 进行详细结果处理查看。

支持用户自定义结果处理策略。

IResult 内置方法

simple()/detail() 处理的结果为 IResult 实现类。

IResult 支持如下方法:

  • print()

对结果进行打印,主要便于调试。

  • throwEx()

对于参数的校验,一般都是基于异常结合 spring aop来处理的。

throwsEx 会在验证不通过时,抛出 ValidRuntimeException 异常,对应 message 为提示消息。

@Test(expected = ValidRuntimeException.class)
public void resultThrowsExTest() {
    ValidBs.on(null, notNullValidatorEntry())
            .valid()
            .result()
            .throwsEx();
}

内置的属性约束

上面我们对 ValidBs 有了一个整体的了解,下面来看一看系统内置的属性约束有哪些。

每个属性约束都有对应注解。

针对单个属性,直接使用属性约束即可,灵活快捷。

针对 bean 校验,可以结合注解实现,类似于 hibernate-validator。

valid-core

核心内置属性约束实现。

enumRangesConstraint

枚举类指定范围约束

  • 创建方式

参见工具类 Constraints#enumRangesConstraint

/**
 * 枚举范围内约束
 * (1)当前值必须在枚举类对应枚举的 toString() 列表中。
 * @param enumClass 枚举类,不可为空
 * @return 约束类
 * @since 0.1.1
 * @see com.github.houbb.valid.core.annotation.constraint.EnumRanges 枚举类指定范围注解
 */
public static IConstraint enumRangesConstraint(final Class enumClass)
  • 测试案例

参见测试类 EnumsRangesConstraintTest

IResult result = ValidBs.on("DEFINE", Constraints.enumRangesConstraint(FailTypeEnum.class))
    .result();

Assert.assertFalse(result.pass());
  • 说明

FailTypeEnum 是 valid-api 内置的枚举类,枚举值为 FAIL_FAST/FAIL_OVER。

只有属性值在枚举值范围内,验证才会通过。

rangesConstraint

指定属性范围内约束

  • 创建方式

参见工具类 Constraints#rangesConstraint


 * 值在指定范围内约束
 * (1)这里为了和注解保持一致性,暂时只支持 String
 * @param strings 对象范围
 * @return 约束类
 * @since 0.1.1
 * @see com.github.houbb.valid.core.annotation.constraint.Ranges String 指定范围内注解
 */
public static IConstraint rangesConstraint(String ... strings)
  • 测试案例

参见测试类 RangesConstraintTest

IResult result = ValidBs.on("DEFINE", Constraints.rangesConstraint("FAIL_OVER",
        "FAIL_FAST"))
    .result();

Assert.assertFalse(result.pass());
  • 说明

这个相对于枚举值,更加灵活一些。

可以根据自己的需要,指定属性的范围。

valid-jsr

valid-jsr 中内置注解,和 jsr-303 标准一一对应,此处不再赘述。

创建方式见工具类 JsrConstraints,测试代码见 xxxConstraintTest。

对应列表如下:

属性约束 注解 简介
AssertFalseConstraint @AssertFalse 指定值必须为 false
AssertTrueConstraint @AssertTrue 指定值必须为 true
MinConstraint @Min 指定值必须大于等于最小值
MaxConstraint @Max 指定值必须小于等于最大值
DecimalMinConstraint @DecimalMin 指定金额必须大于等于最小值
DecimalMaxConstraint @DecimalMax 指定金额必须小于等于最大值
DigitsConstraint @Digits 指定值位数必须符合要求
FutureConstraint @Future 指定日期必须在未来
PastConstraint @Past 指定日期必须在过去
PatternConstraint @Pattern 指定值必须满足正则表达式
SizeConstraint @Size 指定值必须在指定大小内

自定义约束实现

需求

实际业务需求的是不断变化的,内置的属性约束常常无法满足我们的实际需求。

我们可以通过自定义属性,来实现自己的需求。

例子

参见类 DefineConstraintTest

自定义 notNullConstraint

notNullConstraint 对于 null 值是严格的。

所以继承自 AbstractStrictConstraint,如下:

IResult result = ValidBs.on(null, new AbstractStrictConstraint() {
    @Override
    protected boolean pass(IConstraintContext context, Object value) {
        return value != null;
    }
}).result();

Assert.assertFalse(result.pass());

自定义 assertTrueConstraint

在 jsr-303 标准中,除却 @NotNull 对于 null 值都是非严格校验的。

继承自 AbstractConstraint 即可,如下:

IConstraint assertTrueCOnstraint= new AbstractConstraint() {
    @Override
    protected boolean pass(IConstraintContext context, Boolean value) {
        return false;
    }
};

IResult nullValid = ValidBs.on(null, assertTrueConstraint)
        .result();
Assert.assertTrue(nullValid.pass());

IResult falseValid = ValidBs.on(false, assertTrueConstraint)
        .result();
Assert.assertFalse(falseValid.pass());

core 模块注解验证

内置注解

注解 说明
@AllEquals 当前字段及指定字段值必须全部相等
@HasNotNull 当前字段及指定字段值至少有一个不为 null
@EnumRanges 当前字段值必须在枚举属性范围内
@Ranges 当前字段值必须在指定属性范围内

测试对象

  • User.java
public class User {

    /**
     * 名称
     */
    @HasNotNull({"nickName"})
    private String name;

    /**
     * 昵称
     */
    private String nickName;

    /**
     * 原始密码
     */
    @AllEquals("password2")
    private String password;

    /**
     * 新密码
     */
    private String password2;

    /**
     * 性别
     */
    @Ranges({"boy", "girl"})
    private String sex;

    /**
     * 失败类型枚举
     */
    @EnumRanges(FailTypeEnum.class)
    private String failType;

    //fluent getter & setter
}

我们限制 name/nickName 至少有一个不为空,password/password2 值要一致。

以及限定了 sex 的范围值和 failType 的枚举值。

测试代码

User user = new User();
user.sex("what").password("old").password2("new")
    .failType("DEFINE");

IResult result = ValidBs.on(user)
        .fail(Fails.failOver())
        .result()
        .print();

Assert.assertFalse(result.pass());
  • 日志
DefaultResult{pass=false, notPassList=[DefaultConstraintResult{pass=false, message='值  不是预期值', value=null, cOnstraint='HasNotNullConstraint', expectValue=''}, DefaultConstraintResult{pass=false, message='值  不是预期值', value=old, cOnstraint='AllEqualsConstraint', expectValue=''}, DefaultConstraintResult{pass=false, message='值  不是预期值', value=what, cOnstraint='RangesConstraint', expectValue=''}, DefaultConstraintResult{pass=false, message='值  不是预期值', value=DEFINE, cOnstraint='EnumRangesConstraint', expectValue=''}], allList=null}

jsr 模块注解验证

注解

与 jsr-303 注解标准保持一致。

对象定义

为了演示,简单定义如下:

  • JsrUser.java
public class JsrUser {

    @Null
    private Object nullVal;

    @NotNull
    private String notNullVal;

    @AssertFalse
    private boolean assertFalse;

    @AssertTrue
    private boolean assertTrue;

    @Pattern(regexp = "[123456]{2}")
    private String pattern;

    @Size(min = 2, max = 5)
    private String size;

    @DecimalMax("12.22")
    private BigDecimal decimalMax;

    @DecimalMin("1.22")
    private BigDecimal decimalMin;

    @Min(10)
    private long min;

    @Max(10)
    private long max;

    @Past
    private Date past;

    @Future
    private Date future;

    @Digits(integer = 2, fraction = 4)
    private Long digits;

    //fluent getter and setter
}

测试代码

参见测试类 ValidBsJsrBeanTest

public void beanFailTest() {
    Date future = DateUtil.getFormatDate("90190101", DateUtil.PURE_DATE_FORMAT);
    Date past = DateUtil.getFormatDate("20190101", DateUtil.PURE_DATE_FORMAT);

    JsrUser jsrUser = new JsrUser();
    jsrUser.assertFalse(true)
            .assertTrue(false)
            .decimalMin(new BigDecimal("1"))
            .decimalMax(new BigDecimal("55.55"))
            .min(5)
            .max(20)
            .digits(333333L)
            .future(past)
            .past(future)
            .nullVal("123")
            .notNullVal(null)
            .pattern("asdfasdf")
            .size("22222222222222222222");

    IResult result = ValidBs.on(jsrUser)
            .fail(Fails.failOver())
            .valid(JsrValidator.getInstance())
            .result()
            .print();

    Assert.assertFalse(result.pass());
}
  • 日志
DefaultResult{pass=false, notPassList=[DefaultConstraintResult{pass=false, message='值必须为空', value=123, cOnstraint='NullConstraint', expectValue='null'}, DefaultConstraintResult{pass=false, message='值必须为非空', value=null, cOnstraint='NotNullConstraint', expectValue='not null'}, DefaultConstraintResult{pass=false, message='值必须为假', value=true, cOnstraint='AssertFalseConstraint', expectValue='false'}, DefaultConstraintResult{pass=false, message='值必须为真', value=false, cOnstraint='AssertTrueConstraint', expectValue='true'}, DefaultConstraintResult{pass=false, message='值必须满足正则表达式', value=asdfasdf, cOnstraint='PatternConstraint', expectValue='必须匹配正则表达式 [123456]{2}'}, DefaultConstraintResult{pass=false, message='值必须为在指定范围内', value=22222222222222222222, cOnstraint='SizeConstraint', expectValue='大小必须在范围内 [2, 5]'}, DefaultConstraintResult{pass=false, message='值必须小于金额最大值', value=55.55, cOnstraint='DecimalMaxConstraint', expectValue='小于等于 12.22'}, DefaultConstraintResult{pass=false, message='值必须大于金额最小值', value=1, cOnstraint='DecimalMinConstraint', expectValue='大于等于 1.22'}, DefaultConstraintResult{pass=false, message='值必须大于最小值', value=5, cOnstraint='MinConstraint', expectValue='大于等于 10'}, DefaultConstraintResult{pass=false, message='值必须小于最大值', value=20, cOnstraint='MaxConstraint', expectValue='小于等于 10'}, DefaultConstraintResult{pass=false, message='时间必须在过去', value=Fri Jan 01 00:00:00 CST 9019, cOnstraint='PastConstraint', expectValue='小于等于 Sun Oct 13 12:12:07 CST 2019'}, DefaultConstraintResult{pass=false, message='时间必须在未来', value=Tue Jan 01 00:00:00 CST 2019, cOnstraint='FutureConstraint', expectValue='大于等于 Sun Oct 13 12:12:07 CST 2019'}, DefaultConstraintResult{pass=false, message='值必须满足位数', value=333333, cOnstraint='DigitsConstraint', expectValue='整数位数 [2], 小数位数 [4]'}], allList=null}

@Valid 递归属性验证

需求

有时候我们一个对象中,会引入其他子对象。

我们希望对子对象也进行相关属性的验证,这时候就可以使用 @Valid 注解。

该注解为 jsr-303 标准注解。

对象定义

public class ValidUser {

    /**
     * 子节点
     */
    @Valid
    private User user;

    //fluent setter & getter

}

测试代码

参见测试类 ValidBsValidBeanTest

public void beanFailTest() {
    User user = new User();
    user.sex("default").password("old").password2("new")
            .failType("DEFINE");

    ValidUser validUser = new ValidUser();
    validUser.user(user);

    IResult result = ValidBs.on(validUser)
            .fail(Fails.failOver())
            .result()
            .print();

    Assert.assertFalse(result.pass());
}
  • 日志信息
DefaultResult{pass=false, notPassList=[DefaultConstraintResult{pass=false, message='值  不是预期值', value=null, cOnstraint='HasNotNullConstraint', expectValue=''}, DefaultConstraintResult{pass=false, message='值  不是预期值', value=old, cOnstraint='AllEqualsConstraint', expectValue=''}, DefaultConstraintResult{pass=false, message='值  不是预期值', value=default, cOnstraint='RangesConstraint', expectValue=''}, DefaultConstraintResult{pass=false, message='值  不是预期值', value=DEFINE, cOnstraint='EnumRangesConstraint', expectValue=''}], allList=null}

自引用问题

有时候我们可能会引用自身,这个也做了测试,是符合预期的。

参见 ValidBsSelfValidBeanTest

i18n 支持

需求

不同国家对于语言的要求肯定也不同。

本项目目前支持中文/英文国际化支持,默认以当前地区编码为准,如果不存在,则使用英文。

感觉其他语言,暂时使用中没有用到。(个人也不会,错了也不知道。暂时不添加)

指定为英文

测试代码参加 ValidBsI18NTest

public void i18nEnTest() {
    Locale.setDefault(Locale.ENGLISH);
    IResult result = ValidBs.on(null, JsrConstraints.notNullConstraint())
            .result()
            .print();

    Assert.assertEquals("Expect is , but actual is .", result.notPassList().get(0).message());
}

指定为中文

public void i18nZhTest() {
    Locale.setDefault(Locale.CHINESE);
    IResult result = ValidBs.on(null, JsrConstraints.notNullConstraint())
            .result()
            .print();

    Assert.assertEquals("预期值为 ,实际值为 ", result.notPassList().get(0).message());
}

IFail 失败策略接口详解

需求

对于不符合约束条件的处理方式,主要有以下两种:

  • failFast

快速失败。遇到一个约束不符合条件,直接返回。

优点:耗时较短。

  • failOver

全部验证,将所有的属性都验证一遍。

优点:可以一次性获得所有失败信息。

创建方式

参见工具类 Fails,返回的实例为单例,且线程安全。

测试代码

参见测试类 ValidBsFailTest

failFast

我们指定要求属性值长度最小为3,且必须满足正则表达式。

IResult result = ValidBs.on("12", JsrConstraints.sizeConstraintMin(3),
        JsrConstraints.patternConstraint("[678]{3}"))
        .fail(Fails.failFast())
        .result()
        .print();

Assert.assertEquals(1, result.notPassList().size());
  • 日志

采用快速失败模式,只有一个失败验证结果。

DefaultResult{pass=false, notPassList=[DefaultConstraintResult{pass=false, message='预期值为 <必须匹配正则表达式 [678]{3}>,实际值为 <12>', value=12, cOnstraint='PatternConstraint', expectValue='必须匹配正则表达式 [678]{3}'}], allList=null}

failOver

保持其他部分不变,我们调整下失败处理策略。

IResult result = ValidBs.on("12", JsrConstraints.sizeConstraintMin(3),
        JsrConstraints.patternConstraint("[678]{3}"))
        .fail(Fails.failOver())
        .result()
        .print();

Assert.assertEquals(2, result.notPassList().size());
  • 日志

此时失败处理结果为2,日志如下:

DefaultResult{pass=false, notPassList=[DefaultConstraintResult{pass=false, message='预期值为 <必须匹配正则表达式 [678]{3}>,实际值为 <12>', value=12, cOnstraint='PatternConstraint', expectValue='必须匹配正则表达式 [678]{3}'}, DefaultConstraintResult{pass=false, message='预期值为 <大小必须在范围内 [3, 2147483647]>,实际值为 <2>', value=12, cOnstraint='SizeConstraint', expectValue='大小必须在范围内 [3, 2147483647]'}], allList=null}

IValidator 验证策略接口详解

需求

为了便于集成不同框架的测试验证,本框架支持 IValidator。

同时也允许用户自定义自己的实现方式。

默认验证器策略-DefaultValidator

指定 valid 对应的验证器,通过 ValidBs.valid(IValidator) 方法指定。

默认为 DefaultValidator。

该验证策略,支持符合 valid-api 的内置注解,及用户自定义注解。

JSR-303 验证器策略-JsrValidator

JsrValidator 支持 jsr-303 标准注解,及 valid-api 标准的相关注解实现和约束实现。

  • 使用方式

通过 valid 方法指定即可。

IResult result = ValidBs.on(jsrUser)
                .valid(JsrValidator.getInstance())
                .result()
                .print();

自定义验证器策略

如果你想添加自己的实现,直接实现 IValidator,并且在 valid() 中指定即可。

可以参考 DefaultValidator,建议继承自 AbstractValidator

IResultHandler 结果处理策略接口详解

需求

对于验证的结果,不同的场景,需求也各不相同。

你可能有如下需求:

(1)输出验证失败的信息

(2)输出所有验证信息

(3)针对验证失败的信息抛出异常

(4)对验证结果进行自定义处理。

为了满足上述需求,提供了如下的接口,及内置默认实现。

接口

public interface IResultHandler {

    /**
     * 对约束结果进行统一处理
     * @param constraintResultList 约束结果列表
     * @return 结果
     */
    T handle(final List constraintResultList);

}

如果你想自定义处理方式,实现此接口。

并在 ValidBs.result(IResultHandler) 方法中指定使用即可。

简单实现

  • 说明

仅仅对没有通过测试的验证结果进行保留。

  • 测试代码

参见测试代码 ValidBsResultHandlerTest

ValidBs.on("12", JsrConstraints.sizeConstraintMin(2))
        .result(ResultHandlers.simple())
        .print();
  • 日志
DefaultResult{pass=true, notPassList=[], allList=null}

详细实现

  • 说明

保留所有验证结果信息,包含通过验证测试的明细信息。

  • 测试代码

参见测试代码 ValidBsResultHandlerTest

ValidBs.on("12", JsrConstraints.sizeConstraintMin(2))
        .result(ResultHandlers.detail())
        .print();
  • 测试日志
DefaultResult{pass=true, notPassList=[], allList=[DefaultConstraintResult{pass=true, message='null', value=12, cOnstraint='SizeConstraint', expectValue='null'}]}

IResult 结果接口详解

说明

IResult 为验证结果处理的内置实现接口。

拥有以下常见方法:

方法 说明
pass() 是否通过验证
notPassList() 未通过验证的列表
allList() 所有验证的列表
print() 控台输出验证结果
throwsEx() 针对未通过验证的信息抛出 ValidRuntimeException

测试代码

@Test(expected = ValidRuntimeException.class)
public void methodsTest() {
    IResult result = ValidBs.on("12", JsrConstraints.sizeConstraintMin(3))
            .result(ResultHandlers.detail())
            .print()
            .throwsEx();

    Assert.assertFalse(result.pass());
    Assert.assertEquals(1, result.notPassList().size());
    Assert.assertEquals(1, result.allList().size());
}
  • 日志
DefaultResult{pass=false, notPassList=[DefaultConstraintResult{pass=false, message='预期值为 <大小必须在范围内 [3, 2147483647]>,实际值为 <2>', value=12, cOnstraint='SizeConstraint', expectValue='大小必须在范围内 [3, 2147483647]'}], allList=[DefaultConstraintResult{pass=false, message='预期值为 <大小必须在范围内 [3, 2147483647]>,实际值为 <2>', value=12, cOnstraint='SizeConstraint', expectValue='大小必须在范围内 [3, 2147483647]'}]}

IConstraint 约束接口详解

需求

Hibernate-validator 主要是基于注解的 Bean 验证,所以将注解和实现耦合在了一起。

Valid 作为一个 fluent-api 验证框架,支持过程式编程,所以将针对属性验证的约束独立出来,便于复用。

接口说明

public interface IConstraint {

    /**
     * 触发约束规则
     * @param context 上下文
     * @return 结果
     * @since 0.0.3
     */
    IConstraintResult constraint(final IConstraintContext context);

}

自定义说明

前面的例子已经演示了如何自定义实现。

直接实现上述接口也可以,建议继承 AbstractConstraint 等内置的各种约束抽象类。

IValidEntry 验证明细接口详解

说明

当我们将 IConstraint 独立出来时,同时有下面的一些问题:

(1)如何指定对应 message

(2)如何指定约束生效条件 condition

(3)如何指定约束的分组信息 group

IValidEntry 接口就是为了解决这些问题,在 IConstraint 的基础之上进行一系列的功能增强。

使用方式

测试代码,参见类 ValidBsValidEntryTest

IValidEntry validEntry = ValidEntry.of(JsrConstraints.notNullConstraint());

IResult result = ValidBs.on(null, validEntry)
    .result()
    .print();

Assert.assertFalse(result.pass());

message() 自定义提示消息

我们可以自定义改约束条件的提示消息。

final IValidEntry validEntry = ValidEntry.of(JsrConstraints.notNullConstraint())
        .message("自定义:指定值不能为空");

IResult result = ValidBs.on(null, validEntry)
        .valid()
        .result();

Assert.assertEquals("自定义:指定值不能为空", result.notPassList().get(0).message());

group() 分组验证

需求

有时候我们希望只验证某一种分组的约束条件。

测试代码

按照如下方式制定,只有当 ValidEntry 的 group 信息与 ValidBs.group() 符合时,才会被执行。

final IValidEntry firstEntry = ValidEntry.of(JsrConstraints.sizeConstraint(5, 10))
        .group(String.class);

final IValidEntry otherEntry = ValidEntry.of(JsrConstraints.sizeConstraint(3, 20))
        .group(Integer.class);

IResult result = ValidBs
        .on("12", firstEntry, otherEntry)
        .fail(Fails.failOver())
        .group(String.class)
        .result();

Assert.assertEquals(1, result.notPassList().size());

condition 拓展

其实可以 group() 只是 condition 的一个特例。

后续将实现 ICondition 接口的相关内置支持,和 @Condition 注解的相关支持。

自定义注解

需求

说到 hibernate-validator,个人觉得最灵魂的设计就是支持用户自定义注解了。

注解使得使用便利,自定义注解同时保证了灵活性。

下面来看看,如何实现自定义注解。

核心设计理念

你可以认为内置注解也是一种自定义注解。

本框架的所有实现理念都是如此,可以认为所有的内置实现,都是可以被替换的。

@AllEquals 注解解析

我们以 @AllEquals 注解为例,

注解内容

@Inherited
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(AtAllEqualsConstraint.class)
public @interface AllEquals {

    /**
     * 当前字段及其指定的字段 全部相等
     * 1. 字段类型及其他字段相同
     * @return 指定的字段列表
     */
    String[] value();

    /**
     * 提示消息
     * @return 错误提示
     */
    String message() default "";

    /**
     * 分组信息
     * @return 分组类
     * @since 0.1.2
     */
    Class[] group() default {};

}

其中 group()/message() 和 IValidEntry 中的方法一一对应。

当然你设计的注解中如果没有这两个方法也没关系,建议提供这两个属性。

注解与约束的关系

@Constraint(AtAllEqualsConstraint.class) 这个注解指定了当前注解与对应的约束实现,是最核心的部分。

@Constraint 注解

@Inherited
@Documented
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Constraint {

    /**
     * 约束条件实现类
     * @return 实现类 class
     */
    Class value();

}

IAnnotationConstraint 接口

这个就是注解相关的约束接口,内容如下:

/**
 * 注解约束规则接口
 * 注意:所有的实现类都需要提供无参构造函数。
 * @author binbin.hou
 * @since 0.0.9
 */
public interface IAnnotationConstraint extends IConstraint {

    /**
     * 初始化映射关系
     * @param annotation 注解信息
     * @since 0.0.9
     */
    void initialize(A annotation);

}

后期特性

  • 丰富 IConstraintResult 特性

  • 优化 IResult 使用体验

  • @Condition 注解支持和 ICondition 的支持。

  • 集成 hibernate-validator 校验

参考项目

JSR 标准

JSR 380

JSR 303

bean validation 2.0

bean 验证框架

hibernate validate

apache bval

Fluent 框架

fluent-validator

FluentValidation


推荐阅读
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • 使用Ubuntu中的Python获取浏览器历史记录原文: ... [详细]
  • eclipse学习(第三章:ssh中的Hibernate)——11.Hibernate的缓存(2级缓存,get和load)
    本文介绍了eclipse学习中的第三章内容,主要讲解了ssh中的Hibernate的缓存,包括2级缓存和get方法、load方法的区别。文章还涉及了项目实践和相关知识点的讲解。 ... [详细]
  • 标题: ... [详细]
  • 本文介绍了Android 7的学习笔记总结,包括最新的移动架构视频、大厂安卓面试真题和项目实战源码讲义。同时还分享了开源的完整内容,并提醒读者在使用FileProvider适配时要注意不同模块的AndroidManfiest.xml中配置的xml文件名必须不同,否则会出现问题。 ... [详细]
  • 本文介绍了一个题目的解法,通过二分答案来解决问题,但困难在于如何进行检查。文章提供了一种逃逸方式,通过移动最慢的宿管来锁门时跑到更居中的位置,从而使所有合格的寝室都居中。文章还提到可以分开判断两边的情况,并使用前缀和的方式来求出在任意时刻能够到达宿管即将锁门的寝室的人数。最后,文章提到可以改成O(n)的直接枚举来解决问题。 ... [详细]
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
author-avatar
jackystorm岁月_657
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有