通过Bean Validation 可以减少大量重复代码,可以很容通过注解配置约束,可以将约束与模型凝聚在一起。
依赖jar
<!-- bean validator -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.1.0.Final</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
Annotation | Supported data types | Use |
---|---|---|
@AssertFalse | Boolean, boolean | Checks that the annotated element is false |
@AssertTrue | Boolean, boolean | Checks that the annotated element is true |
@DecimalMax(value=,inclusive=) | BigDecimal,BigInteger,CharSequence, byte,short, int, long and the respective wrappers of the primitive types; | Checks whether the annotated value is less than the specified maximum, when inclusive=false. Otherwise whether the value is less than or equal to the specified maximum. The parameter value is the string representation of the max value according to the BigDecimal string representation. |
@DecimalMin(value=,inclusive=) | BigDecimal, BigInteger,CharSequence, byte,short, int, long and the respective wrappers of the primitive types; | Checks whether the annotated value is larger than the specified minimum, when inclusive=false. Otherwise whether the value is larger than or equal to the specified minimum. The parameter value is the string representation of the min value according to the BigDecimal string representation. |
@Digits(integer=,fraction=) | BigDecimal, BigInteger, CharSequence,byte,short, int, long and therespective wrappers of the primitive types; | Checks whether the annoted value is a number having up to integer digits and fraction fractional digits |
@Future | java.util.Date, java.util.Calendar; | Checks whether the annotated date is in the future |
@Max(value=) | BigDecimal, BigInteger, byte, short, int,long and the respective wrappers of the primitive types;any sub-type of Number | Checks whether the annotated value is less than or equal to the specified maximum |
@Min(value=) | BigDecimal,BigInteger,byte,short,int, long and the respective wrappers of the primitive types;, any sub-type of Number | Checks whether the annotated value is higher than or equal to the specified minimum |
@NotNull | Any type | Checks that the annotated value is not null. |
@Null | Any type | Checks that the annotated value is null |
@Past | java.util.Date, java.util.Calendar; | Checks whether the annotated date is in the past |
@Pattern(regex=,flag=) | CharSequence | Checks if the annotated string matches the regular expression regex considering the given flag match |
@Size(min=,max=) | CharSequence,Collection,Map and arrays | Checks if the annotated element's size is between min and max (inclusive) |
@Valid | Any non-primitive type | Performs validation recursively on the associated object. If the object is a collection or an array, the elements are validated recursively. If the object is a map, the value elements are validated recursively. |
In addition to the constraints defined by the Bean Validation API Hibernate Validator provides several useful custom constraints which are listed in lower table;
@CreditCardNumber | CharSequence | Checks that the annotated character sequence passes the Luhn checksum test. Note, this validation aims to check for user mistakes, not credit card validity! |
CharSequence | Checks whether the specified character sequence is a valid email address. The optional parameters regexp and flags allow to specify an additional regular expression (including regular expression flags)which the email must match. | |
@Length(min=, max=) | CharSequence | Validates that the annotated character sequence is between min and max included |
@NotBlank | CharSequence | |
@NotEmpty | CharSequence,Collection,Map and arrays | Checks whether the annotated element is not null nor empty |
@Range(min=,max=) | BigDecimal,BigInteger,CharSequence,byte,short, int, long and the respective wrappers of the primitive types | Checks whether the annotated value lies between (inclusive) the specified minimum and maximum |
@ScriptAssert(lang=, script=, alias=) | Any type |
例子:属性校验
package org.hibernate.validator.referenceguide.chapter02.inheritance;
public class Car {
private String manufacturer;
@NotNull
public String getManufacturer() {
return manufacturer;
}
//...
}
package org.hibernate.validator.referenceguide.chapter02.inheritance;
public class RentalCar extends Car {
private String rentalStation;
@NotNull
public String getRentalStation() {
return rentalStation;}
}
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Car car = new Car( null, true );
Set<ConstraintViolation<Car>> constraintViolations = validator.validate( car );
assertEquals( 1, constraintViolations.size() );
assertEquals( "may not be null", constraintViolations.iterator().next().getMessage() );
Car car = new Car( null, true );
Set<ConstraintViolation<Car>> constraintViolations = validator.validateProperty(
car,
"manufacturer"
);
assertEquals( 1, constraintViolations.size() );
assertEquals( "may not be null", constraintViolations.iterator().next().getMessage() );
例子:方法参数校验
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
executableValidator = factory.getValidator().forExecutables();
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() {
//...
}
}
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 );
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 );
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 );
//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 );
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) {
//...
}
}
package org.hibernate.validator.referenceguide.chapter03.crossparameter.constrainttarget;
public class Garage {
@ELAssert(expression = "...", validationAppliesTo = ConstraintTarget.PARAMETERS)
public Car buildCar(List<Part> parts) {
//...
}
@ELAssert(expression = "...", validationAppliesTo = ConstraintTarget.RETURN_VALUE)
public Car paintCar(int color) {
//...
}
}
例子:指定错误信息
public class Car {
@NotNull
private String manufacturer;
@Size(min = 2, max = 14, message = "The license plate must be between {min} and {max} characters long")
private String licensePlate;
@Min(value = 2, message = "There must be at least {value} seat${value > 1 ? 's' : ''}")
private int seatCount;
@DecimalMax(value = "350", message = "The top speed ${formatter.format('%1$.2f', validatedValue)} is higher than {value}")
private double topSpeed;
@DecimalMax(value = "100000", message = "Price must not be higher than ${value}")
private BigDecimal price;
public Car(String manufacturer, String licensePlate, int seatCount,
double topSpeed, BigDecimal price) {
this.manufacturer = manufacturer;
this.licensePlate = licensePlate;
this.seatCount = seatCount;
this.topSpeed = topSpeed;
this.price = price;
}
// getters and setters ...
}
Car car = new Car(null, "A", 1, 400.123456, BigDecimal.valueOf(200000));
String message = validator.validateProperty(car, "manufacturer")
.iterator().next().getMessage();
assertEquals("may not be null", message);
message = validator.validateProperty(car, "licensePlate").iterator()
.next().getMessage();
assertEquals(
"The license plate must be between 2 and 14 characters long",
message);
message = validator.validateProperty(car, "seatCount").iterator()
.next().getMessage();
assertEquals("There must be at least 2 seats", message);
message = validator.validateProperty(car, "topSpeed").iterator().next()
.getMessage();
assertEquals("The top speed 400.12 is higher than 350", message);
message = validator.validateProperty(car, "price").iterator().next()
.getMessage();
assertEquals("Price must not be higher than $100000", message);
自定义约束--参数级别
public enum CaseMode {
UPPER,
LOWER;
}
@Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = CheckCaseValidator.class)
@Documented
public @interface CheckCase {
String message() default "{org.hibernate.validator.referenceguide.chapter06.CheckCase."
+ "message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
CaseMode value();
@Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Documented
@interface List {
CheckCase[] value();
}
}
public class CheckCaseValidator implements
ConstraintValidator<CheckCase, String> {
private CaseMode caseMode;
@Override
public void initialize(CheckCase constraintAnnotation) {
this.caseMode = constraintAnnotation.value();
}
@Override
public boolean isValid(String object,
ConstraintValidatorContext constraintContext) {
if (object == null) {
return true;
}
if (caseMode == CaseMode.UPPER) {
return object.equals(object.toUpperCase());
} else {
return object.equals(object.toLowerCase());
}
}
}
public class Car {
@NotNull
private String manufacturer;
@NotNull
@Size(min = 2, max = 14)
@CheckCase(CaseMode.UPPER)
private String licensePlate;
@Min(2)
private int seatCount;
public Car(String manufacturer, String licencePlate, int seatCount) {
this.manufacturer = manufacturer;
this.licensePlate = licencePlate;
this.seatCount = seatCount;
}
// getters and setters ...
}
// invalid license plate
Car car = new Car("Morris", "dd-ab-123", 4);
Set<ConstraintViolation<Car>> constraintViolations = validator
.validate(car);
assertEquals(1, constraintViolations.size());
assertEquals("Case mode must be UPPER.", constraintViolations
.iterator().next().getMessage());
// valid license plate
car = new Car("Morris", "DD-AB-123", 4);
constraintViolations = validator.validate(car);
assertEquals(0, constraintViolations.size());
自定义约束--类级别
@Target({ TYPE, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = { ValidPassengerCountValidator.class })
@Documented
public @interface ValidPassengerCount {
String message() default "{org.hibernate.validator.referenceguide.chapter06.classlevel."
+ "ValidPassengerCount.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class ValidPassengerCountValidator implements
ConstraintValidator<ValidPassengerCount, Car> {
@Override
public void initialize(ValidPassengerCount constraintAnnotation) {
}
@Override
public boolean isValid(Car car, ConstraintValidatorContext context) {
if (car == null) {
return true;
}
return car.getPassengers().size() <= car.getSeatCount();
}
}
约束组合
@NotNull
@Size(min = 2, max = 14)
@CheckCase(CaseMode.UPPER)
@Target({ METHOD, FIELD, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = {})
@Documented
public @interface ValidLicensePlate {
String message() default "{org.hibernate.validator.referenceguide.chapter06."
+ "constraintcomposition.ValidLicensePlate.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class Car {
@ValidLicensePlate
private String licensePlate;
//...
}