JSR 310新日期/时间API的自定义JSR 303 Bean验证约束

借助JSR 310,Java 8终于为我们带来了不错的日期和时间API。 对于仍在使用Java 7的那些人(例如我目前在我的当前项目中),有很好的反向移植,请访问www.threeten.org了解更多详细信息。 但是,由于有关该主题的博客已经很多,因此我将不涉及使用新API的任何细节。 在本文中,我将向您展示的是如何通过编写自己的自定义批注将Date / Time API与JSR 303 Bean验证API结合使用。

如果您同时使用bean验证和新的日期/时间API,则可能需要结合使用它们。 API和Hibernate Validator之类的实现仅提供少数约束,例如NotEmpty@Pattern 。 但是,到目前为止,JSR 310还没有现成的约束。幸运的是,创建自己的约束非常容易。 作为示例,我将演示如何编写自己的@Past批注以验证java.time.LocalDate字段。

为了进行测试,我们将从一个非常简单的类开始,该类包含一个日期和一个dateTime。 这些字段应该代表过去的日期。 因此,它们使用@Past注释进行注释:

ClassWithPastDates

package it.jdev.example.jsr310.validator;

import java.time.LocalDate;
import java.time.LocalDateTime;

public class ClassWithPastDates {

    @Past
    private LocalDate date;

    @Past
    private LocalDateTime dateTime;
    
    public LocalDate getDate() {
        return date;
    }
    
    public void setDate(LocalDate date) {
        this.date = date;
    }
    public LocalDateTime getDateTime() {
        return dateTime;
    }
    
    public void setDateTime(LocalDateTime dateTime) {
        this.dateTime = dateTime;
    }

}

接下来,我们将为@Past约束编写一个非常基本的单元测试,以证明我们的意图:显然,除了过去的日期之外,我们还希望null引用有效,但将来的日期无效,甚至今天也算作无效。

过去测试

package it.jdev.example.jsr310.validator;

import static org.junit.Assert.assertEquals;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;

import org.junit.Before;
import org.junit.Test;

public class PastTest {
    
    private ClassWithPastDates classUnderTest;

    @Before
    public void setup() {
        classUnderTest = new ClassWithPastDates();
    }

    @Test
    public void thatNullIsValid() {
        Set<ConstraintViolation<ClassWithPastDates>> violations = validateClass(classUnderTest);
        assertEquals(violations.size(), 0);
    }

    @Test
    public void thatYesterdayIsValid() throws Exception {
        classUnderTest.setDate(LocalDate.now().minusDays(1));
        classUnderTest.setDateTime(LocalDateTime.now().minusDays(1));
        Set<ConstraintViolation<ClassWithPastDates>> violations = validateClass(classUnderTest);
        assertEquals(violations.size(), 0);
    }

    @Test
    public void thatTodayIsInvalid() throws Exception {
        classUnderTest.setDate(LocalDate.now());
        classUnderTest.setDateTime(LocalDateTime.now());
        Set<ConstraintViolation<ClassWithPastDates>> violations = validateClass(classUnderTest);
        assertEquals(violations.size(), 2);
    }

    @Test
    public void thatTomorrowIsInvalid() throws Exception {
        classUnderTest.setDate(LocalDate.now().plusDays(1));
        classUnderTest.setDateTime(LocalDateTime.now().plusDays(1));
        Set<ConstraintViolation<ClassWithPastDates>> violations = validateClass(classUnderTest);
        assertEquals(violations.size(), 2);
    }

    private Set<ConstraintViolation<ClassWithPastDates>> validateClass(ClassWithPastDates myClass) {
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        Validator validator = factory.getValidator();
        Set<ConstraintViolation<ClassWithPastDates>> violations = validator.validate(myClass);
        return violations;
    }

}

现在我们已经建立了基本测试,我们可以实现约束本身。 这包括两个步骤。 首先,我们必须编写注释,然后必须实现ConstraintValidator 。 从注释开始:

@interface过去

package it.jdev.example.jsr310.validator;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.validation.Constraint;
import javax.validation.Payload;

@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PastValidator.class)
@Documented
public @interface Past {

    String message() default "it.jdev.example.jsr310.validator.Past.message";

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

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

}

如您所见, @Past注释不是很壮观。 要注意的主要事情是@Constraint批注,在其中我们指定将使用哪个类执行实际验证。

PastValidator

package it.jdev.example.jsr310.validator;

import java.time.LocalDate;
import java.time.temporal.Temporal;

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

public class PastValidator implements ConstraintValidator<Past, Temporal> {

    @Override
    public void initialize(Past constraintAnnotation) {
    }

    @Override
    public boolean isValid(Temporal value, ConstraintValidatorContext context) {
        if (value == null) {
            return true;
        }
        LocalDate ld = LocalDate.from(value);
        if (ld.isBefore(LocalDate.now())) {
            return true;
        }
        return false;
    }

}

PastValidator是所有魔术发生的地方。 通过实现ConstraintValidator接口,我们不得不提供两种方法,但对于我们的示例,仅使用isValid()方法,这是我们执行实际验证的地方。

请注意,我们使用java.time.temporal.Temporal作为类型,因为它是LocalDate和LocalDateTime类共同拥有的接口。 这使我们可以对LocalDate和LocalDateTime字段使用相同的@Past

真正的全部就是它。 在这个非常基本的示例中,我展示了创建自己的定制JSR 303 bean验证约束是多么容易。

翻译自: https://www.javacodegeeks.com/2014/09/custom-jsr-303-bean-validation-constraints-for-the-jsr-310-new-datetime-api.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值