播放地址


4、解读Hibernate-Validator官方文档-自定义容器类型泛型校验-入门篇 http://v.youku.com/v_show/id_XMzk1NTc5MjA3Mg==.html 5、解读Hibernate-Validator官方文档Value-Extractor-进阶 http://v.youku.com/v_show/id_XMzk1NTg0NDY0NA==.html

7. Value extraction(提取)

值提取是从容器中提取值以便验证它们的过程。

它用于处理容器元素约束和容器内的级联验证。

7.1 Built-in value extractors (内置值提取器)

Hibernate验证器带有通常的Java容器类型的内置值提取器,因此,除了使用自定义容器类型(或外部库(如Guava MultMAP)之外,您不必添加自己的值提取器)。

所有容器类型都有内置值提取器:

  • java.util.Iterable;
  • java.util.List;
  • java.util.Map: for keys and values;
  • java.util.Optional, java.util.OptionalInt, java.util.OptionalLong and java.util.OptionalDouble

在Bean Validation规范中可以找到完整的内置值提取器列表,其中包含有关它们行为的所有细节。

7.2. Implementing a ValueExtractor

要从自定义容器中提取值,需要实现一个ValueExtractor提除器

NOTE : Implementing a ValueExtractor​ is not enough, you also need to register it. See Section 7.5, “Registering a ValueExtractor” for more details.

ValueExtractor一个非常简单的API,因为值提取器的唯一目的是将提取的值提供给ValueReceiver。

For instance, let’s consider the case of Guava’s Optional. It is an easy example as we can shape its value extractor after the java.util.Optional one:

Example 7.1: A ValueExtractor for Guava’s Optional

package org.hibernate.validator.referenceguide.chapter07.valueextractor;

public class OptionalValueExtractor
implements ValueExtractor<Optional<@ExtractedValue ?>> {

@Override
public void extractValues(Optional<?> originalValue, ValueReceiver receiver) {
receiver.value( null, originalValue.orNull() );
}
}

有些解释是按顺序进行的:

  • The @ExtractedValue annotation marks the type argument under consideration: it is going to be used to resolve the type of the validated value;(释标记了考虑中的类型参数:它将用于解析已验证值的类型)
  • We use the value() method of the receiver as Optional is a pure wrapper type; 我们使用接收者的ValueMe()方法作为一个纯包装类型;
  • We don’t want to add a node to the property path of the constraint violation as we want the violation to be reported as if it were directly on the property so we pass a null node name to value(). 我们不想向约束违规的属性路径添加节点,因为我们希望报告违规,就好像它直接在属性上一样,所以我们将空节点名传递给value()。

一个更有趣的例子是Guava的Multiimap:我们希望能够验证这个容器类型的键和值。

Example 7.2: A ValueExtractor for Multimap values

package org.hibernate.validator.referenceguide.chapter07.valueextractor;

public class MultimapValueValueExtractor
implements ValueExtractor<Multimap<?, @ExtractedValue ?>> {

@Override
public void extractValues(Multimap<?, ?> originalValue, ValueReceiver receiver) {
for ( Entry<?, ?> entry : originalValue.entries() ) {
receiver.keyedValue( "<multimap value>", entry.getKey(), entry.getValue() );
}
}
}

它允许验证Multimap值的约束:

Example 7.3: Constraints on the values of a Multimap

private Multimap<String, @NotBlank String> map1;

另一个值提取器需要能够对多聚一键的键施加约束:

Example 7.4: A ValueExtractor for Multimap keys

package org.hibernate.validator.referenceguide.chapter07.valueextractor;

public class MultimapKeyValueExtractor
implements ValueExtractor<Multimap<@ExtractedValue ?, ?>> {

@Override
public void extractValues(Multimap<?, ?> originalValue, ValueReceiver receiver) {
for ( Object key : originalValue.keySet() ) {
receiver.keyedValue( "<multimap key>", key, key );
}
}
}

一旦注册了这两个值提取器,就可以对Multimap的键和值声明约束:

Example 7.5: Constraints on the keys and values of a Multimap

private Multimap<@NotBlank String, @NotBlank String> map2;

这两种值提取器之间的差异乍一看可能有点微妙,因此让我们对它们进行一些说明:(可能第二个值才是需要校验的)

  • The @ExtractedValue annotation marks the targeted type argument (either K or V in this case)
  • We use different node names (<multimap key> vs. <multimap value>).
  • In one case, we pass the values to the receiver (third argument of the keyedValue() call), in the other, we pass the keys. 据容器类型,您应该选择适合最佳值的ValueRever方法:
  • value() for a simple wrapping container - it is used for Optionals
  • iterableValue() for an iterable container - it is used for Sets
  • indexedValue() for a container containing indexed values - it is used for Lists
  • keyedValue() for a container containing keyed values - it is used for Maps. It is used for both the keys and the values. In the case of keys, the key is also passed as the validated value. 对于所有这些方法,都需要传递节点名:它是添加到约束违规的属性路径的节点中包括的名称。如前所述,如果节点名为空,则不向属性路径添加任何节点:这对于类似于Optional的纯包装器类型很有用。

所使用的方法的选择很重要,因为它将上下文信息添加到约束违反的属性路径,例如已验证值的索引或键。

7.3 Non generic containers(非泛型的容器)

您可能已经注意到,到目前为止,我们只为通用容器实现了值提取器。

Hibernate验证器还支持非泛型容器的值提取。

让我们以java.util.OptionalInt为例,它将原语int包装到类Optional容器中。

OptionalInt的值提取器的第一次尝试看起来像:

Example 7.6: A ValueExtractor for OptionalInt

package org.hibernate.validator.referenceguide.chapter07.nongeneric;

public class OptionalIntValueExtractor
implements ValueExtractor<@ExtractedValue(type = Integer.class) OptionalInt> {

@Override
public void extractValues(OptionalInt originalValue, ValueReceiver receiver) {
receiver.value( null, originalValue.isPresent() ? originalValue.getAsInt() : null );
}
}

非泛型容器有一个明显的东西:我们没有类型参数。它有两个后果:

  • 不能使用类型参数确定已验证值的类型;
  • we cannot add constraints on the type argument (e.g. Container<@NotNull String>).

First things first, we need a way to tell Hibernate Validator that the value extracted from an OptionalInt​ is of type Integer​. As you can see in the above example, the type​ attribute of the @ExtractedValue annotation allows to provide this information to the validation engine.

然后,您必须告诉验证引擎,要添加到OptionalInt属性的Min约束与包装的值有关,而不是与包装器有关。

Bean Validation provides the Unwrapping.Unwrap payload for this situation:

/**
* Set of interfaces used in the {@code payload()} of a constraint to indicate if a value
* should be unwrapped before validation.
* <p>
* This is used to overwrite the default configuration defined on the {@link ValueExtractor}.
*
* @author Guillaume Smet
* @since 2.0
*/
public interface Unwrapping {

/**
* Unwrap the value before validation.
*
* @since 2.0
*/
public interface Unwrap extends Payload {
}

/**
* Skip the unwrapping if it has been enabled on the {@link ValueExtractor} by the
* {@link UnwrapByDefault}
* annotation.
*
* @since 2.0
*/
public interface Skip extends Payload {
}

如果我们后退一步,大多数(如果不是全部)我们想要添加到OptionalInt属性的约束将应用于包装的值,因此有使它成为默认值的方法会很好。

This is exactly what the @UnwrapByDefault annotation is for:

Example 7.8: A ValueExtractor​ for OptionalInt​ marked with @UnwrapByDefault

package org.hibernate.validator.referenceguide.chapter07.nongeneric;

@UnwrapByDefault
public class UnwrapByDefaultOptionalIntValueExtractor
implements ValueExtractor<@ExtractedValue(type = Integer.class) OptionalInt> {

@Override
public void extractValues(OptionalInt originalValue, ValueReceiver receiver) {
receiver.value( null, originalValue.isPresent() ? originalValue.getAsInt() : null );
}
}

When declaring this value extractor for OptionalInt, constraint annotations will by default be applied to the wrapped value:

Example 7.9: Implicit unwrapping thanks to @UnwrapByDefault

@Min(5)
private OptionalInt optionalInt2;

注意,您仍然可以通过使用Unwrapping.Skip有效负载为包装器本身声明注释:(意思就是当你想校验这个包装器的本身的时候,可以跳过)

Example 7.10: Avoid implicit unwrapping with Unwrapping.Skip

@NotNull(payload = Unwrapping.Skip.class)
@Min(5)
private OptionalInt optionalInt3;

OptionalInt的@UnwrapByDefault值提取器是内置值提取器的一部分:不需要添加一个。

7.4. JavaFX value extractors

7.5. Registering a ​​ValueExtractor​​

Hibernate Validator does not detect automatically the value extractors in the classpath so they have to be registered.

There are several ways to register value extractors (in increasing order of priority):

Provided by the validation engine itself

See Section 7.1, “Built-in value extractors”.

Via the Java service loader mechanism

The file META-INF/services/javax.validation.valueextraction.ValueExtractor must be provided, with the fully-qualified names of one or more value extractor implementations as its contents, each on a separate line.

In the META-INF/validation.xml file

See Section 8.1, “Configuring the validator factory in validation.xml” for more information about how to register value extractors in the XML configuration.

By calling Configuration#addValueExtractor(ValueExtractor<?>)

See Section 9.2.6, “Registering ValueExtractors” for more information.

By invoking ValidatorContext#addValueExtractor(ValueExtractor<?>)

It only declares the value extractor for this Validator instance.

A value extractor for a given type and type parameter specified at a higher priority overrides any other extractors for the same type and type parameter given at lower priorities.