Hibernate Validator 中文操作指南
3. Declaring and validating method constraints
從 Bean Validation 1.1 開始,約束不只能應用於 JavaBean 及其屬性,還能應用於任何 Java 類型的方法和建構函式的參數和返回值。這樣就可以使用 Jakarta Bean Validation 約束來指定
As of Bean Validation 1.1, constraints can not only be applied to JavaBeans and their properties, but also to the parameters and return values of the methods and constructors of any Java type. That way Jakarta Bean Validation constraints can be used to specify
-
the preconditions that must be satisfied by the caller before a method or constructor may be invoked (by applying constraints to the parameters of an executable)
-
the postconditions that are guaranteed to the caller after a method or constructor invocation returns (by applying constraints to the return value of an executable)
为了本参考指南的目的,除非另有说明,method constraint 一词既表示方法,又表示构造函数约束。有时,executable 一词用于指代方法和构造函数。
For the purpose of this reference guide, the term method constraint refers to both, method and constructor constraints, if not stated otherwise. Occasionally, the term executable is used when referring to methods and constructors. |
与检查参数和返回值正确性的传统方法相比,这种方法有以下几个优点:
This approach has several advantages over traditional ways of checking the correctness of parameters and return values:
-
the checks don’t have to be performed manually (e.g. by throwing IllegalArgumentException or similar), resulting in less code to write and maintain
-
an executable’s pre- and postconditions don’t have to be expressed again in its documentation, since the constraint annotations will automatically be included in the generated JavaDoc. This avoids redundancies and reduces the chance of inconsistencies between implementation and documentation
为了让注释显示在注释元素的 JavaDoc 中,注释类型本身必须使用元注释 @Documented 注释。对于所有内置约束都需如此,并被认为对任何自定义约束都是最佳实践。
In order to make annotations show up in the JavaDoc of annotated elements, the annotation types themselves must be annotated with the meta annotation @Documented. This is the case for all built-in constraints and is considered a best practice for any custom constraints. |
在本篇剩余内容中,您将了解如何声明参数和返回值约束,以及如何使用 ExecutableValidator API 验证它们。
In the remainder of this chapter you will learn how to declare parameter and return value constraints and how to validate them using the ExecutableValidator API.
3.1. Declaring method constraints
3.1.1. Parameter constraints
通过向其参数添加约束注解来指定方法或构造函数的先决条件,如 Example 3.1, “Declaring method and constructor parameter constraints”中所示。
You specify the preconditions of a method or constructor by adding constraint annotations to its parameters as demonstrated in Example 3.1, “Declaring method and constructor parameter constraints”.
. Example 3.1: Declaring method and constructor parameter constraints
package org.hibernate.validator.referenceguide.chapter03.parameter;
public class RentalStation {
public RentalStation(@NotNull String name) {
//...
}
public void rentCar(
@NotNull Customer customer,
@NotNull @Future Date startDate,
@Min(1) int durationInDays) {
//...
}
}
此处声明了以下先决条件:
The following preconditions are declared here:
-
The name passed to the RentalCar constructor must not be null
-
When invoking the rentCar() method, the given customer must not be null, the rental’s start date must not be null as well as be in the future and finally the rental duration must be at least one day
请注意,声明方法或构造函数约束本身不会在执行调用时自动导致其验证。相反,必须使用 _ExecutableValidator_API(参见 Section 3.2, “Validating method constraints”)执行验证,这通常使用 AOP 等方法拦截工具或代理对象来完成。
Note that declaring method or constructor constraints itself does not automatically cause their validation upon invocation of the executable. Instead, the ExecutableValidator API (see Section 3.2, “Validating method constraints”) must be used to perform the validation, which is often done using a method interception facility such as AOP, proxy objects etc.
约束仅可应用于实例方法,即,不支持声明静态方法的约束。根据您用于触发方法验证的拦截机制,可能存在其他限制,例如,相对于作为拦截目标支持的方法的可见性。请参阅拦截技术的文档,以了解是否存在此类限制。
Constraints may only be applied to instance methods, i.e. declaring constraints on static methods is not supported. Depending on the interception facility you use for triggering method validation, additional restrictions may apply, e.g. with respect to the visibility of methods supported as target of interception. Refer to the documentation of the interception technology to find out whether any such limitations exist.
3.1.1.1. Cross-parameter constraints
有时,验证不仅仅依赖于单个参数,而是依赖于方法或构造器的多个参数,甚至所有参数。可借助跨参数约束来满足这类要求。
Sometimes validation does not only depend on a single parameter but on several or even all parameters of a method or constructor. This kind of requirement can be fulfilled with help of a cross-parameter constraint.
跨参数约束可视为类级别约束中方法验证的等效部分。两者都可用于实现基于多个元素的验证要求。类级别约束适用于 Bean 的多个属性,而跨参数约束适用于可执行文件的多个参数。
Cross-parameter constraints can be considered as the method validation equivalent to class-level constraints. Both can be used to implement validation requirements which are based on several elements. While class-level constraints apply to several properties of a bean, cross-parameter constraints apply to several parameters of an executable.
与单参数约束不同,跨参数约束在方法或构造函数上声明,如你可以在 Example 3.2, “Declaring a cross-parameter constraint”中看到。在这里,针对 _load()_方法声明的跨参数约束 _@LuggageCountMatchesPassengerCount_用于确保任何乘客的行李件数不超过两件。
In contrast to single-parameter constraints, cross-parameter constraints are declared on the method or constructor as you can see in Example 3.2, “Declaring a cross-parameter constraint”. Here the cross- parameter constraint @LuggageCountMatchesPassengerCount declared on the load() method is used to ensure that no passenger has more than two pieces of luggage.
. Example 3.2: Declaring a cross-parameter constraint
package org.hibernate.validator.referenceguide.chapter03.crossparameter;
public class Car {
@LuggageCountMatchesPassengerCount(piecesOfLuggagePerPassenger = 2)
public void load(List<Person> passengers, List<PieceOfLuggage> luggage) {
//...
}
}
正如你将在下一节中了解到,返回值约束也在方法级别上声明。要从返回值约束中区分跨参数约束,则在 _ConstraintValidator_实现中使用 _@SupportedValidationTarget_注解配置约束目标。你可以在 Section 6.3, “Cross-parameter constraints”中找到详细信息,它展示了如何实现你自己的跨参数约束。
As you will learn in the next section, return value constraints are also declared on the method level. In order to distinguish cross-parameter constraints from return value constraints, the constraint target is configured in the ConstraintValidator implementation using the @SupportedValidationTarget annotation. You can find out about the details in Section 6.3, “Cross-parameter constraints” which shows how to implement your own cross-parameter constraint.
在某些情况下,可将一个约束应用于可执行文件的参数(即它是一个跨参数约束),还可应用于返回值。一个示例是允许使用表达式或脚本语言指定验证规则的自定义约束。
In some cases a constraint can be applied to an executable’s parameters (i.e. it is a cross- parameter constraint), but also to the return value. One example for this are custom constraints which allow to specify validation rules using expression or script languages.
此类约束必须定义一个成员 validationAppliesTo(),可在声明时用于指定约束目标。如 Example 3.3, “Specifying a constraint’s target”中所示,通过指定 _validationAppliesTo = ConstraintTarget.PARAMETERS_向执行对象的的参数应用约束,而 _ConstraintTarget.RETURN_VALUE_用于向执行的返回值应用约束。
Such constraints must define a member validationAppliesTo() which can be used at declaration time to specify the constraint target. As shown in Example 3.3, “Specifying a constraint’s target” you apply the constraint to an executable’s parameters by specifying validationAppliesTo = ConstraintTarget.PARAMETERS, while ConstraintTarget.RETURN_VALUE is used to apply the constraint to the executable return value.
. Example 3.3: Specifying a constraint’s target
package org.hibernate.validator.referenceguide.chapter03.crossparameter.constrainttarget;
public class Garage {
@ELAssert(expression = "...", validationAppliesTo = ConstraintTarget.PARAMETERS)
public Car buildCar(List<Part> parts) {
//...
return null;
}
@ELAssert(expression = "...", validationAppliesTo = ConstraintTarget.RETURN_VALUE)
public Car paintCar(int color) {
//...
return null;
}
}
尽管此类约束可应用于可执行文件的参数和返回值,但通常可以自动推断出目标。在以下情况下会这样,如果约束是在以下内容上声明的:
Although such a constraint is applicable to the parameters and return value of an executable, the target can often be inferred automatically. This is the case, if the constraint is declared on
-
a void method with parameters (the constraint applies to the parameters)
-
an executable with return value but no parameters (the constraint applies to the return value)
-
neither a method nor a constructor, but a field, parameter etc. (the constraint applies to the annotated element)
在这些情况下,您不必指定约束目标。如果这对提升源代码的可读性有所帮助,则仍然建议您这样做。如果没有指定约束目标,而在不能自动推断目标的情况下,则会引发 ConstraintDeclarationException。
In these situations you don’t have to specify the constraint target. It is still recommended to do so if it increases readability of the source code. If the constraint target is not specified in situations where it can’t be determined automatically, a ConstraintDeclarationException is raised.
3.1.2. Return value constraints
方法或构造函数的后置条件通过向执行对象添加约束注解来声明,如 Example 3.4, “Declaring method and constructor return value constraints”中所示。
The postconditions of a method or constructor are declared by adding constraint annotations to the executable as shown in Example 3.4, “Declaring method and constructor return value constraints”.
. Example 3.4: Declaring method and constructor return value constraints
package org.hibernate.validator.referenceguide.chapter03.returnvalue;
public class RentalStation {
@ValidRentalStation
public RentalStation() {
//...
}
@NotNull
@Size(min = 1)
public List<@NotNull Customer> getCustomers() {
//...
return null;
}
}
以下约束适用于 RentalStation 的可执行文件:
The following constraints apply to the executables of RentalStation:
-
Any newly created RentalStation object must satisfy the @ValidRentalStation constraint
-
The customer list returned by getCustomers() must not be null and must contain at least on element
-
The customer list returned by getCustomers() must no contain null objects
如你所见,在上述示例中,方法返回值支持容器元素约束。它们还支持方法参数。
As you can see in the above example, container element constraints are supported on method return value. They are also supported on method parameters. |
3.1.3. Cascaded validation
类似于 JavaBeans 属性的级联验证(参见 Section 2.1.6, “Object graphs”),_@Valid_注解可用于标记执行参数和返回值以进行级联验证。在验证用 _@Valid_注解的参数或返回值时,同时验证在参数或返回值对象中声明的约束。
Similar to the cascaded validation of JavaBeans properties (see Section 2.1.6, “Object graphs”), the @Valid annotation can be used to mark executable parameters and return values for cascaded validation. When validating a parameter or return value annotated with @Valid, the constraints declared on the parameter or return value object are validated as well.
在 Example 3.5, “Marking executable parameters and return values for cascaded validation”中,方法 _Garage#checkCar()_的 _car_参数以及 _Garage_构造函数的返回值被标记为级联验证。
In Example 3.5, “Marking executable parameters and return values for cascaded validation”, the car parameter of the method Garage#checkCar() as well as the return value of the Garage constructor are marked for cascaded validation.
. Example 3.5: Marking executable parameters and return values for cascaded validation
package org.hibernate.validator.referenceguide.chapter03.cascaded;
public class Garage {
@NotNull
private String name;
@Valid
public Garage(String name) {
this.name = name;
}
public boolean checkCar(@Valid @NotNull Car car) {
//...
return false;
}
}
package org.hibernate.validator.referenceguide.chapter03.cascaded;
public class Car {
@NotNull
private String manufacturer;
@NotNull
@Size(min = 2, max = 14)
private String licensePlate;
public Car(String manufacturer, String licencePlate) {
this.manufacturer = manufacturer;
this.licensePlate = licencePlate;
}
//getters and setters ...
}
在验证 checkCar() 方法的参数时,还将评估传入的 Car 对象属性的约束。同样,在验证 Garage 构造器的返回值时,将检查 Garage 的名称字段上的 @NotNull 约束。
When validating the arguments of the checkCar() method, the constraints on the properties of the passed Car object are evaluated as well. Similarly, the @NotNull constraint on the name field of Garage is checked when validating the return value of the Garage constructor.
一般来说,级联验证适用于可执行文件的方式与适用于 JavaBean 属性的方式完全相同。
Generally, the cascaded validation works for executables in exactly the same way as it does for JavaBeans properties.
特别是,null 值在级联验证期间将被忽略(自然地,这不会在构造器返回值验证期间发生),并且级联验证是递归执行的,即,如果标记为进行级联验证的参数或返回值对象本身具有用 @Valid 标记的属性,则对所引用元素声明的约束也将进行验证。
In particular, null values are ignored during cascaded validation (naturally this can’t happen during constructor return value validation) and cascaded validation is performed recursively, i.e. if a parameter or return value object which is marked for cascaded validation itself has properties marked with @Valid, the constraints declared on the referenced elements will be validated as well.
与字段和属性相同,也可以对容器元素(例如,返回值和参数的集合、映射或自定义容器元素)声明级联验证。
Same as for fields and properties, cascaded validation can also be declared on container elements (e.g. elements of collections, maps or custom containers) of return values and parameters.
在这种情况下,容器中包含的每个元素都得到验证。因此,在验证 Example 3.6, “Container elements of method parameter marked for cascaded validation”中 checkCars()_方法的参数时,传递的列表中的每个元素实例都将被验证,当任何包含的 _Car_实例无效时都会创建一个 _ConstraintViolation。
In this case, each element contained by the container gets validated. So when validating the arguments of the checkCars() method in Example 3.6, “Container elements of method parameter marked for cascaded validation”, each element instance of the passed list will be validated and a ConstraintViolation created when any of the contained Car instances is invalid.
. Example 3.6: Container elements of method parameter marked for cascaded validation
package org.hibernate.validator.referenceguide.chapter03.cascaded.containerelement;
public class Garage {
public boolean checkCars(@NotNull List<@Valid Car> cars) {
//...
return false;
}
}
3.1.4. Method constraints in inheritance hierarchies
在继承层次结构中声明方法约束时,需要注意以下规则:
When declaring method constraints in inheritance hierarchies, it is important to be aware of the following rules:
-
The preconditions to be satisfied by the caller of a method may not be strengthened in subtypes
-
The postconditions guaranteed to the caller of a method may not be weakened in subtypes
这些规则源于 behavioral subtyping 的概念,该概念要求无论在何处使用类型 T,都可以使用 T 的子类型 S,而不会更改程序的行为。
These rules are motivated by the concept of behavioral subtyping which requires that wherever a type T is used, also a subtype S of T may be used without altering the program’s behavior.
例如,考虑一个类使用该对象上的一个方法调用,具有静态类型 T。如果该对象的运行时类型是 S,且 S 施加了其他先决条件,则客户端类可能无法满足这些先决条件(因为它不知道这些条件)。行为子类型的规则也称为 Liskov substitution principle。
As an example, consider a class invoking a method on an object with the static type T. If the runtime type of that object was S and S imposed additional preconditions, the client class might fail to satisfy these preconditions as is not aware of them. The rules of behavioral subtyping are also known as the Liskov substitution principle.
Jakarta Bean Validation 规范通过禁止对重写或实现超类(超类或接口)中声明的方法的方法上的参数约束来实现第一条规则。 Example 3.7, “Illegal method parameter constraint in subtype”显示了此规则的违规行为。
The Jakarta Bean Validation specification implements the first rule by disallowing parameter constraints on methods which override or implement a method declared in a supertype (superclass or interface). Example 3.7, “Illegal method parameter constraint in subtype” shows a violation of this rule.
. Example 3.7: Illegal method parameter constraint in subtype
package org.hibernate.validator.referenceguide.chapter03.inheritance.parameter;
public interface Vehicle {
void drive(@Max(75) int speedInMph);
}
package org.hibernate.validator.referenceguide.chapter03.inheritance.parameter;
public class Car implements Vehicle {
@Override
public void drive(@Max(55) int speedInMph) {
//...
}
}
Car#drive() 的 @Max 约束是非法的,因为此方法实现了接口方法 Vehicle#drive()。请注意,如果超类型方法本身未声明任何参数约束,则重写方法的参数约束也会被禁止。
The @Max constraint on Car#drive() is illegal since this method implements the interface method Vehicle#drive(). Note that parameter constraints on overriding methods are also disallowed, if the supertype method itself doesn’t declare any parameter constraints.
此外,如果一个方法重写或实现多个并行超类(例如两个不相互扩展的接口或一个类和一个该类未实现的接口)中声明的方法,则不能为涉及类型中的任何方法指定参数约束。 Example 3.8, “Illegal method parameter constraint in parallel types of a hierarchy”中的类型演示了该规则的违规情况。方法 RacingCar#drive()_重写 _Vehicle#drive()_和 _Car#drive()。因此,_Vehicle#drive()_上的约束是非法的。
Furthermore, if a method overrides or implements a method declared in several parallel supertypes (e.g. two interfaces not extending each other or a class and an interface not implemented by that class), no parameter constraints may be specified for the method in any of the involved types. The types in Example 3.8, “Illegal method parameter constraint in parallel types of a hierarchy” demonstrate a violation of that rule. The method RacingCar#drive() overrides Vehicle#drive() as well as Car#drive(). Therefore the constraint on Vehicle#drive() is illegal.
. Example 3.8: Illegal method parameter constraint in parallel types of a hierarchy
package org.hibernate.validator.referenceguide.chapter03.inheritance.parallel;
public interface Vehicle {
void drive(@Max(75) int speedInMph);
}
package org.hibernate.validator.referenceguide.chapter03.inheritance.parallel;
public interface Car {
void drive(int speedInMph);
}
package org.hibernate.validator.referenceguide.chapter03.inheritance.parallel;
public class RacingCar implements Car, Vehicle {
@Override
public void drive(int speedInMph) {
//...
}
}
前面描述的限制仅适用于参数约束。相比之下,可以在重写或实现任何超类型方法的方法中添加返回值约束。
The previously described restrictions only apply to parameter constraints. In contrast, return value constraints may be added in methods overriding or implementing any supertype methods.
在这种情况下,所有方法的返回值约束都适用于子类型方法,即在子类型方法本身声明的约束以及重写/实现的超类型方法的任何返回值约束。这是合法的,因为实施额外的返回值约束永远不会减弱对方法调用者的后置条件的保证。
In this case, all the method’s return value constraints apply for the subtype method, i.e. the constraints declared on the subtype method itself as well as any return value constraints on overridden/implemented supertype methods. This is legal as putting additional return value constraints in place may never represent a weakening of the postconditions guaranteed to the caller of a method.
因此,在验证 Example 3.9, “Return value constraints on supertype and subtype method”中所示方法 _Car#getPassengers()_的返回值时,方法本身上的 _@Size_约束以及实现的接口方法 _Vehicle#getPassengers()_上的 _@NotNull_约束适用。
So when validating the return value of the method Car#getPassengers() shown in Example 3.9, “Return value constraints on supertype and subtype method”, the @Size constraint on the method itself as well as the @NotNull constraint on the implemented interface method Vehicle#getPassengers() apply.
. Example 3.9: Return value constraints on supertype and subtype method
package org.hibernate.validator.referenceguide.chapter03.inheritance.returnvalue;
public interface Vehicle {
@NotNull
List<Person> getPassengers();
}
package org.hibernate.validator.referenceguide.chapter03.inheritance.returnvalue;
public class Car implements Vehicle {
@Override
@Size(min = 1)
public List<Person> getPassengers() {
//...
return null;
}
}
如果验证引擎检测到违反上述任何规则,则将引发 ConstraintDeclarationException。
If the validation engine detects a violation of any of the aforementioned rules, a ConstraintDeclarationException will be raised.
本节中描述的规则仅适用于方法,不适用于构造函数。根据定义,构造函数永远不会覆盖超类构造函数。因此,在验证构造函数调用的参数或返回值时,只应用在构造函数本身上声明的约束,而永远不会应用在超类构造函数上声明的任何约束。 |
The rules described in this section only apply to methods but not constructors. By definition, constructors never override supertype constructors. Therefore, when validating the parameters or the return value of a constructor invocation only the constraints declared on the constructor itself apply, but never any constraints declared on supertype constructors. |
在创建 Validator 实例之前,可以通过设置 HibernateValidatorConfiguration 的 MethodValidationConfiguration 属性中包含的配置参数来放宽这些规则的执行。另请参阅 Section 12.3, “Relaxation of requirements for method validation in class hierarchies”。 |
Enforcement of these rules may be relaxed by setting the configuration parameters contained in the MethodValidationConfiguration property of the HibernateValidatorConfiguration before creating the Validator instance. See also Section 12.3, “Relaxation of requirements for method validation in class hierarchies”. |
3.2. Validating method constraints
使用 ExecutableValidator 接口对方法约束进行验证。
The validation of method constraints is done using the ExecutableValidator interface.
在 Section 3.2.1, “Obtaining an ExecutableValidator instance” 中,您将了解如何获取 ExecutableValidator 实例,而 Section 3.2.2, “ExecutableValidator methods” 介绍如何使用此接口提供的不同方法。
In Section 3.2.1, “Obtaining an ExecutableValidator instance” you will learn how to obtain an ExecutableValidator instance while Section 3.2.2, “ExecutableValidator methods” shows how to use the different methods offered by this interface.
通常不直接在应用程序代码中调用 ExecutableValidator 方法,而是通过 AOP、代理对象等方法拦截技术来调用它们。这会导致在调用方法或构造函数时自动透明地验证可执行约束。通常当任何约束被违反时,集成层会引发 ConstraintViolationException。
Instead of calling the ExecutableValidator methods directly from within application code, they are usually invoked via a method interception technology such as AOP, proxy objects, etc. This causes executable constraints to be validated automatically and transparently upon method or constructor invocation. Typically a ConstraintViolationException is raised by the integration layer in case any of the constraints is violated.
3.2.1. Obtaining an ExecutableValidator instance
您可以通过 Validator#forExecutables() 检索 ExecutableValidator 实例,如 Example 3.10, “Obtaining an ExecutableValidator instance” 中所示。
You can retrieve an ExecutableValidator instance via Validator#forExecutables() as shown in Example 3.10, “Obtaining an ExecutableValidator instance”.
. Example 3.10: Obtaining an ExecutableValidator instance
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
executableValidator = factory.getValidator().forExecutables();
在示例中,可执行验证器从默认验证器工厂中检索,但如果需要,您还可以引导一个专门配置的工厂,如 Chapter 9, Bootstrapping 中所述,例如为了使用特定的参数名称提供程序(请参阅 Section 9.2.4, “ParameterNameProvider” )。
In the example the executable validator is retrieved from the default validator factory, but if required you could also bootstrap a specifically configured factory as described in Chapter 9, Bootstrapping, for instance in order to use a specific parameter name provider (see Section 9.2.4, “ParameterNameProvider”).
3.2.2. ExecutableValidator methods
ExecutableValidator 接口总共提供四种方法:
The ExecutableValidator interface offers altogether four methods:
-
validateParameters() and validateReturnValue() for method validation
-
validateConstructorParameters() and validateConstructorReturnValue() for constructor validation
与 Validator 上的方法一样,所有这些方法都返回 Set<ConstraintViolation>,其中包含一个 ConstraintViolation 实例(每个违反的约束一个),如果验证成功,则为空。此外,所有这些方法都有 var-args group 参数,您可以通过它传递验证中要考虑的验证组。
Just as the methods on Validator, all these methods return a Set<ConstraintViolation> which contains a ConstraintViolation instance for each violated constraint and which is empty if the validation succeeds. Also all the methods have a var-args groups parameter by which you can pass the validation groups to be considered for validation.
以下部分中的示例基于 Car 的 Example 3.11, “Class Car with constrained methods and constructors” 类构造方法中的方法。
The examples in the following sections are based on the methods on constructors of the Car class shown in Example 3.11, “Class Car with constrained methods and constructors”.
. Example 3.11: Class Car with constrained methods and constructors
package org.hibernate.validator.referenceguide.chapter03.validation;
public class Car {
public Car(@NotNull String manufacturer) {
//...
}
@ValidRacingCar
public Car(String manufacturer, String team) {
//...
}
public void drive(@Max(75) int speedInMph) {
//...
}
@Size(min = 1)
public List<Passenger> getPassengers() {
//...
return Collections.emptyList();
}
}
3.2.2.1. ExecutableValidator#validateParameters()
validateParameters() 方法用于验证方法调用的参数。 Example 3.12, “Using ExecutableValidator#validateParameters()” 展示了一个示例。该验证导致对 drive() 方法参数的 @Max 约束违规。
The method validateParameters() is used to validate the arguments of a method invocation. Example 3.12, “Using ExecutableValidator#validateParameters()” shows an example. The validation results in a violation of the @Max constraint on the parameter of the drive() method.
. Example 3.12: Using ExecutableValidator#validateParameters()
Car object = new Car( "Morris" );
Method method = Car.class.getMethod( "drive", int.class );
Object[] parameterValues = { 80 };
Set<ConstraintViolation<Car>> violations = executableValidator.validateParameters(
object,
method,
parameterValues
);
assertEquals( 1, violations.size() );
Class<? extends Annotation> constraintType = violations.iterator()
.next()
.getConstraintDescriptor()
.getAnnotation()
.annotationType();
assertEquals( Max.class, constraintType );
请注意,validateParameters() 验证方法的所有参数约束,即各个参数的约束以及跨参数约束。
Note that validateParameters() validates all the parameter constraints of a method, i.e. constraints on individual parameters as well as cross-parameter constraints.
3.2.2.2. ExecutableValidator#validateReturnValue()
使用 validateReturnValue() 可以验证方法的返回值。 Example 3.13, “Using ExecutableValidator#validateReturnValue()” 中的验证导致一次约束违规,因为 getPassengers() 方法预期至少返回一个 Passenger 实例。
Using validateReturnValue() the return value of a method can be validated. The validation in Example 3.13, “Using ExecutableValidator#validateReturnValue()” yields one constraint violation since the getPassengers() method is expected to return at least one Passenger instance.
. Example 3.13: Using ExecutableValidator#validateReturnValue()
Car object = new Car( "Morris" );
Method method = Car.class.getMethod( "getPassengers" );
Object returnValue = Collections.<Passenger>emptyList();
Set<ConstraintViolation<Car>> violations = executableValidator.validateReturnValue(
object,
method,
returnValue
);
assertEquals( 1, violations.size() );
Class<? extends Annotation> constraintType = violations.iterator()
.next()
.getConstraintDescriptor()
.getAnnotation()
.annotationType();
assertEquals( Size.class, constraintType );
3.2.2.3. ExecutableValidator#validateConstructorParameters()
构造函数调用的参数可以用 validateConstructorParameters() 进行验证,如方法 Example 3.14, “Using ExecutableValidator#validateConstructorParameters()” 中所示。由于 manufacturer 参数上的 @NotNull 约束,验证调用返回一次约束违规。
The arguments of constructor invocations can be validated with validateConstructorParameters() as shown in method Example 3.14, “Using ExecutableValidator#validateConstructorParameters()”. Due to the @NotNull constraint on the manufacturer parameter, the validation call returns one constraint violation.
. Example 3.14: Using ExecutableValidator#validateConstructorParameters()
Constructor<Car> constructor = Car.class.getConstructor( String.class );
Object[] parameterValues = { null };
Set<ConstraintViolation<Car>> violations = executableValidator.validateConstructorParameters(
constructor,
parameterValues
);
assertEquals( 1, violations.size() );
Class<? extends Annotation> constraintType = violations.iterator()
.next()
.getConstraintDescriptor()
.getAnnotation()
.annotationType();
assertEquals( NotNull.class, constraintType );
3.2.2.4. ExecutableValidator#validateConstructorReturnValue()
最后,通过使用 validateConstructorReturnValue() 您可以验证构造函数的返回值。在 Example 3.15, “Using ExecutableValidator#validateConstructorReturnValue()” 中, validateConstructorReturnValue() 返回一次约束违规,因为构造函数返回的 Car 实例不满足 @ValidRacingCar 约束(未显示)。
Finally, by using validateConstructorReturnValue() you can validate a constructor’s return value. In Example 3.15, “Using ExecutableValidator#validateConstructorReturnValue()”, validateConstructorReturnValue() returns one constraint violation, since the Car instance returned by the constructor doesn’t satisfy the @ValidRacingCar constraint (not shown).
. Example 3.15: Using ExecutableValidator#validateConstructorReturnValue()
//constructor for creating racing cars
Constructor<Car> constructor = Car.class.getConstructor( String.class, String.class );
Car createdObject = new Car( "Morris", null );
Set<ConstraintViolation<Car>> violations = executableValidator.validateConstructorReturnValue(
constructor,
createdObject
);
assertEquals( 1, violations.size() );
Class<? extends Annotation> constraintType = violations.iterator()
.next()
.getConstraintDescriptor()
.getAnnotation()
.annotationType();
assertEquals( ValidRacingCar.class, constraintType );
3.2.3. ConstraintViolation methods for method validation
除了 Section 2.2.3, “ConstraintViolation” 中引入的方法, ConstraintViolation 还提供了两个更具体的方法,用于验证可执行参数和返回值。
In addition to the methods introduced in Section 2.2.3, “ConstraintViolation”, ConstraintViolation provides two more methods specific to the validation of executable parameters and return values.
对于方法或构造函数参数验证,ConstraintViolation#getExecutableParameters() 返回经过验证的参数数组,而 ConstraintViolation#getExecutableReturnValue() 在返回值验证时提供对已验证对象的访问。
ConstraintViolation#getExecutableParameters() returns the validated parameter array in case of method or constructor parameter validation, while ConstraintViolation#getExecutableReturnValue() provides access to the validated object in case of return value validation.
所有其他 ConstraintViolation 方法一般来说适用于方法验证,方式与验证 Bean 相同。请参阅 JavaDoc 了解在 Bean 和方法验证期间各个方法的行为及其返回值的更多信息。
All the other ConstraintViolation methods generally work for method validation in the same way as for validation of beans. Refer to the JavaDoc to learn more about the behavior of the individual methods and their return values during bean and method validation.
请注意,_getPropertyPath()_在获取有关已验证参数或返回值的详细信息(例如用于日志记录目的)时非常有用。具体而言,你可以从路径节点中检索相关方法的名称和参数类型以及相关参数的索引。如何在 Example 3.16, “Retrieving method and parameter information”中完成此操作。
Note that getPropertyPath() can be very useful in order to obtain detailed information about the validated parameter or return value, e.g. for logging purposes. In particular, you can retrieve name and argument types of the concerned method as well as the index of the concerned parameter from the path nodes. How this can be done is shown in Example 3.16, “Retrieving method and parameter information”.
. Example 3.16: Retrieving method and parameter information
Car object = new Car( "Morris" );
Method method = Car.class.getMethod( "drive", int.class );
Object[] parameterValues = { 80 };
Set<ConstraintViolation<Car>> violations = executableValidator.validateParameters(
object,
method,
parameterValues
);
assertEquals( 1, violations.size() );
Iterator<Node> propertyPath = violations.iterator()
.next()
.getPropertyPath()
.iterator();
MethodNode methodNode = propertyPath.next().as( MethodNode.class );
assertEquals( "drive", methodNode.getName() );
assertEquals( Arrays.<Class<?>>asList( int.class ), methodNode.getParameterTypes() );
ParameterNode parameterNode = propertyPath.next().as( ParameterNode.class );
assertEquals( "speedInMph", parameterNode.getName() );
assertEquals( 0, parameterNode.getParameterIndex() );
参数名称使用当前 ParameterNameProvider 确定(参见 Section 9.2.4, “ParameterNameProvider” )。
The parameter name is determined using the current ParameterNameProvider (see Section 9.2.4, “ParameterNameProvider”).
3.3. Built-in method constraints
除了 Section 2.3, “Built-in constraints”中讨论的内置 bean 和属性级约束外,Hibernate Validator 目前提供一个方法级约束 @ParameterScriptAssert。这是一个通用的跨参数约束,它允许使用任何 JSR 223 兼容的(“JavaTM 平台的脚本化”)脚本语言实现验证例程,前提是在类路径上提供了此语言的引擎。
In addition to the built-in bean and property-level constraints discussed in Section 2.3, “Built-in constraints”, Hibernate Validator currently provides one method-level constraint, @ParameterScriptAssert. This is a generic cross-parameter constraint which allows to implement validation routines using any JSR 223 compatible ("Scripting for the JavaTM Platform") scripting language, provided an engine for this language is available on the classpath.
要从表达式中引用可执行的参数,请使用从活动参数名称提供程序获得的名称(参见 Section 9.2.4, “ParameterNameProvider” )。 Example 3.17, “Using @ParameterScriptAssert” 展示了如何借助 @ParameterScriptAssert 表达 Example 3.2, “Declaring a cross-parameter constraint” 中 @LuggageCountMatchesPassengerCount 约束的验证逻辑。
To refer to the executable’s parameters from within the expression, use their name as obtained from the active parameter name provider (see Section 9.2.4, “ParameterNameProvider”). Example 3.17, “Using @ParameterScriptAssert” shows how the validation logic of the @LuggageCountMatchesPassengerCount constraint from Example 3.2, “Declaring a cross-parameter constraint” could be expressed with the help of @ParameterScriptAssert.
. Example 3.17: Using @ParameterScriptAssert
package org.hibernate.validator.referenceguide.chapter03.parameterscriptassert;
public class Car {
@ParameterScriptAssert(lang = "groovy", script = "luggage.size() <= passengers.size() * 2")
public void load(List<Person> passengers, List<PieceOfLuggage> luggage) {
//...
}
}