Java最高

开始使用Spring 5和Spring Boot 2,通过学习的春天课程:

>>查看课程

1.介绍

在教程中Java Bean验证基础,我们看到了我们如何申请javax.验证使用JSR 380各种类型。在教程中Spring MVC自定义验证,我们看到如何创建自定义验证。

在下一个教程中,我们将专注于建设使用自定义注释验证枚举。

2.验证枚举

不幸的是,大多数标准注释不能应用于枚举

例如,在应用时@Pattern注释给枚举我们收到与Hibernate Validator这样的错误:

javax.validation.unexpectedtypeexception:hv000030:无法找到constraint'javax.validation.constraints.pattern'验证类型'com.baeldung.javaxval.enums.demo.customertomerty金宝搏188体育mertype'。检查“customertypematchespattern”的配置

实际上,唯一可以应用于enum的标准注释是@NotNull@空值。

3.验证枚举的模式

让我们先定义一个注释来验证枚举的模式:

@target({方法,字段,annotation_type,构造函数,参数,type_use})@retention(运行时)@documented @constraint(validatedby = enumnamepatternvalidator.cuass)public @interface enumnamnamepepattern {string regexp();字符串消息()默认值“必须匹配\”{regexp} \“”;类<?> []组()默认{};类< ?扩展负载>[]负载()默认{};}

现在我们可以简单地使用正则表达式将这个新注释添加到CustomerType枚举:

@enumnamepattern(regexp =“new |默认”)私有CustomerType CoveryerType;

正如我们所看到的,注释实际上并不包含验证逻辑。因此,我们需要提供ConstraintValidator:

public class EnumNamePatternValidator implements ConstraintValidator< enumnamepatterator, Enum>{私有模式模式;@Override public void initialize(enumnameppattern annotation) {try {pattern = pattern .compile(annotation.regexp());} catch (PatternSyntaxException e){抛出新的IllegalArgumentException(“给定的正则表达式是无效的”,e);@Override public boolean isValid(Enum value, ConstraintValidatorContext context) {if (value == null) {return true;} Matcher m = pattern.matcher(value.name());返回m.matches ();}}

在此示例中,实现与标准非常相似@Pattern验证器。但是,这一次,我们匹配枚举的名称。

4.验证枚举的子集

用正则表达式匹配枚举不是类型安全的。相反,与枚举的实际值进行比较更有意义

但是,由于注释的局限性,这样的注释不能成为泛型。这是因为注解的参数只能是特定枚举的具体值,而不是枚举父类的实例。

让我们看,了解如何为我们的验证创建特定的子集验证注释CustomerType枚举:

@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) @Retention(RUNTIME) @Documented @Constraint(validatedBy = CustomerTypeSubSetValidator.class) public @interface CustomerTypeSubset {CustomerType[] anyOf();String message()默认“必须是{anyOf}中的任何一个”;类<?> []组()默认{};类< ?扩展负载>[]负载()默认{};}

然后可以将该注释应用于该类型的枚举CustomerType:

@CustomerTypeSubset(任何= {CustomerType。NEW, CustomerType. old}) private CustomerType CustomerType;

接下来,我们需要定义CustomerTypeSubSetValidator检查给定枚举值的列表是否包含当前值:

公共类CustomertypeSubsetValidator实现ConstraintValidator  {private customertype []子集;@override public void initialize(customertypesubset约束){this.subset = contrultaint.anyof();} @Override公共布尔ISValid(CustomerType值,ContramizeValidatorContext上下文){return值== null ||arrays.aslist(子集).Contains(价值);}}

虽然注释必须具体对某个枚举,但我们当然可以分享不同验证器之间的代码。

5.验证字符串是否匹配Enum的值

而不是验证枚举匹配a字符串,我们也可以相反。为此,我们可以创建一个检查何处的注释字符串对特定枚举有效。

@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) @Retention(RUNTIME) @Documented @Constraint(validatedBy = ValueOfEnumValidator.class) public @interface ValueOfEnum {Class > enumClass ();String message()默认“必须是enum {enumClass}的任何一个”;类<?> []组()默认{};类< ?扩展负载>[]负载()默认{};}

可以将该注释添加到a字符串字段,我们可以传递任何枚举类。

@ValueOfEnum(enumClass = CustomerType.class) private String customerTypeString;

我们定义的ValueOfEnumValidator查询是否字符串(或任何CharSequence进行)是否包含在枚举中:

公共类valueofenumvalidator实现ConstraintValidator  {私有列表 AcceptedValues;@override public void initialize(valueofenum注释){acceptedvalues = stream.of(annotation.enumclass()。getEnumConstants()).map(枚举:: name).collect(polormors.tolist());@Override公共布尔IsValid(CharSequence值,ConstraindValidatorContext上下文){if(value == null){return true;}返回AcceptedValues.Contains(value.tostring());}}

这种验证在处理JSON对象时尤其有用。当将一个不正确的值从JSON对象映射到enum时,会出现以下异常:

不能从String值'UNDEFINED'中反序列化CustomerType类型的值:值不是声明的Enum实例名之一:[…]

当然,我们可以处理这个例外。但是,这不允许我们立即报告所有违规行为。

我们可以将值映射到枚举,而不是映射到枚举字符串。然后使用验证器检查它是否与枚举值匹配。

6.把所有东西放在一起

我们现在可以使用我们的任何新验证验证Bean。最重要的是,我们所有的验证都接受空值值。因此,我们也可以将其与注释相结合@NotNull:

public class customertype {@ValueOfEnum(enumClass = CustomerType.class) private String customerTypeString;@NotNull @CustomerTypeSubset(anyOf = {CustomerType。NEW, CustomerType. old}) private CustomerType customerTypeOfSubset;@ enumnameppattern (regexp = "NEW|DEFAULT") private CustomerType customerTypeMatchesPattern;//构造函数,getter等}

在下一节中,我们将看到我们如何测试新的注释。

7.测试枚举的Javax验证

要测试我们的验证器,我们将设置一个支持我们新定义的注释的验证器。我们会客户做我们所有的测试

首先,我们要确保一个有效的客户实例不会导致任何违规行为:

@Test public void whenAllAcceptable_thenShouldNotGiveConstraintViolations(){客户客户=新客户();customer.setCustomerTypeOfSubset (CustomerType.NEW);设置违规= validator.validate(customer);为了(违反).isEmpty ();}

其次,我们希望我们的新注释支持和接受空值值。我们只期望有一个违反。这应该报告customerTypeOfSubset由这件事@NotNull注释:

@Test公共void whenallnull_thenonlynotnullshouldgiveConstraintViolations(){客户客户=新客户();设置违规= validator.validate(客户);assertthat(违规行为)。isequalto(1);assertthat(违规).Anymatch(具有备pertypath(“customertypeofsubset”)。(havemessage(“不可能为null”)));}

最后,我们验证我们的验证器以报告违规行为,当输入无效时:

@test public void whenallinvalid_thenviolationshouldbereported(){客户客户=新客户();customer.setCustomertyPestring(“无效”);customer.setcustomertypeofsubset(customertype.default);customer.setCustomertypeMatchEspattn(Customertype.old);设置违规= validator.validate(客户);assertthat(违规行为)。isequalto(3);Assertthat(违规).Anymatch(happropertypath(“customertypestring”)。(havemessage(“必须是enum class com.baeldung.javaxval.enums.demo.c金宝搏188体育ustomertype)));assertthat(违规).Anymatch(havepropertypath(“customertypeofsubset”).and(havemessage(“必须是任何[新,旧]”))));Assertthat(违规).Anymatch(具有备pertypath(“customertypeMatchattnattern”).and(haveMessage(“必须匹配”新|默认\“”))));}

8.结论

在本教程中,我们介绍了使用自定义注释和验证器验证枚举的三个选项。

首先,我们学习了如何使用正则表达式验证枚举的名称。

其次,我们讨论了对特定枚举值子集的验证。我们还解释了为什么不能构建通用注释来实现这一点。

最后,我们还介绍了如何为字符串构建验证器。为了检查是否a字符串符合给定枚举的特定值。

与往常一样,本文的完整源代码是可用的在Github

Java底部

开始使用Spring 5和Spring Boot 2,通过学习的春天课程:

>>查看课程
评论在本文上关闭!