privatestatic Validator validator; @BeforeClass publicstaticvoidsetUpValidator(){ ValidatorFactory factory = Validation .buildDefaultValidatorFactory(); validator = factory.getValidator(); } /** * org.junit.ComparisonFailure: * Expected :must not be null * Actual :不能为null */ @Test publicvoidmanufacturerIsNull(){ Car car = new Car( null, "DD-AB-123", 4 ); Set<ConstraintViolation<Car>> constraintViolations = validator.validate( car ); assertEquals( 0, constraintViolations.size() ); assertEquals( "must not be null", constraintViolations.iterator().next().getMessage()); } /** * org.junit.ComparisonFailure: * Expected :size must be between 2 and 14 * Actual :个数必须在2和14之间 */ @Test publicvoidlicensePlateTooShort(){ Car car = new Car( "Morris", "D", 1 ); Set<ConstraintViolation<Car>> constraintViolations = validator.validate( car ); assertEquals( 2, constraintViolations.size() ); assertEquals( "size must be between 2 and 14", constraintViolations.iterator().next().getMessage() ); } /** * org.junit.ComparisonFailure: * Expected :must be greater than or equal to 2 * Actual :最小不能小于2 */ @Test publicvoidseatCountTooLow(){ Car car = new Car( "Morris", "DD-AB-123", 1 ); Set<ConstraintViolation<Car>> constraintViolations = validator.validate( car ); assertEquals( 1, constraintViolations.size() ); assertEquals( "must be greater than or equal to 2", constraintViolations.iterator().next().getMessage() ); } @Test publicvoidcarIsValid(){ Car car = new Car( "Morris", "DD-AB-123", 2 ); Set<ConstraintViolation<Car>> constraintViolations = validator.validate( car ); assertEquals( 0, constraintViolations.size() ); } }
Chapter 2. 声明和校验Bean约束(Declaring and validating bean constraints)
the various implementations of JavaFX’s javafx.beans.observable.ObservableValue.
2.1.3.1. with set
1 2 3 4 5 6 7 8 9
import java.util.HashSet; import java.util.Set; publicclassCar{ private Set<@ValidPart String> parts = new HashSet<>(); publicvoidaddPart(String part){ parts.add( part ); } //... }
1 2 3 4 5 6 7 8 9 10 11
Car car = new Car(); car.addPart( "Wheel" ); car.addPart( null ); Set<ConstraintViolation<Car>> constraintViolations = validator.validate( car ); assertEquals( 1, constraintViolations.size() ); ConstraintViolation<Car> constraintViolation = constraintViolations.iterator().next(); assertEquals( "'null' is not a valid car part.", constraintViolation.getMessage() ); assertEquals( "parts[].<iterable element>", constraintViolation.getPropertyPath().toString() );
2.1.3.2. with List
1 2 3 4 5 6 7
publicclassCar{ private List<@ValidPart String> parts = new ArrayList<>(); publicvoidaddPart(String part){ parts.add( part ); } //... }
1 2 3 4 5 6 7 8 9 10
Car car = new Car(); car.addPart( "Wheel" ); car.addPart( null ); Set<ConstraintViolation<Car>> constraintViolations = validator.validate( car ); assertEquals( 1, constraintViolations.size()); ConstraintViolation<Car> constraintViolation = constraintViolations.iterator().next(); assertEquals( "'null' is not a valid car part.", constraintViolation.getMessage() ); assertEquals( "parts[1].<list element>", constraintViolation.getPropertyPath().toString() );
2.1.3.3. with Map
1 2 3 4 5 6 7 8 9 10 11 12 13
import java.util.HashMap; import java.util.Map; import javax.validation.constraints.NotNull; publicclassCar{ publicenum FuelConsumption { CITY, HIGHWAY } private Map<@NotNull FuelConsumption, @MaxAllowedFuelConsumption Integer> fuelConsumption = new HashMap<>(); publicvoidsetFuelConsumption(FuelConsumption consumption, int value){ fuelConsumption.put( consumption, value ); } //... }
1 2 3 4 5 6 7 8 9 10
Car car = new Car(); car.setFuelConsumption( Car.FuelConsumption.HIGHWAY, 20 ); Set<ConstraintViolation<Car>> constraintViolations = validator.validate( car ); assertEquals( 1, constraintViolations.size() ); ConstraintViolation<Car> constraintViolation = constraintViolations.iterator().next(); assertEquals( "20 is outside the max fuel consumption.", constraintViolation.getMessage() ); assertEquals( "fuelConsumption[HIGHWAY].<map value>", constraintViolation.getPropertyPath().toString() );
1 2 3 4 5 6 7 8 9 10
Car car = new Car(); car.setFuelConsumption( null, 5 ); Set<ConstraintViolation<Car>> constraintViolations = validator.validate( car ); assertEquals( 1, constraintViolations.size() ); ConstraintViolation<Car> constraintViolation = constraintViolations.iterator().next(); assertEquals( "must not be null", constraintViolation.getMessage() ); assertEquals( "fuelConsumption<K>[].<map key>", constraintViolation.getPropertyPath().toString() );
publicclassCar{ @NotNull private String manufacturer; @Size( min = 2, max = 14, message = "The license plate '${validatedValue}' 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' : ''}" ) privateint 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 ... }
publicclassDriverextendsPerson{ @Min( value = 18, message = "You have to be 18 to drive a car", groups = DriverChecks.class ) publicint age; @AssertTrue( message = "You first have to pass the driving test", groups = DriverChecks.class ) publicboolean hasDrivingLicense; publicDriver(String name){ super( name ); } publicvoidpassedDrivingTest(boolean b){ hasDrivingLicense = b; } publicintgetAge(){ return age; } publicvoidsetAge(int age){ this.age = age; } }
Car car = new Car( "Morris", "DD-AB-123", 2 ); Set<ConstraintViolation<Car>> constraintViolations = validator.validate( car ); assertEquals( 0, constraintViolations.size() ); // but has it passed the vehicle inspection? constraintViolations = validator.validate( car, CarChecks.class ); assertEquals( 1, constraintViolations.size() ); assertEquals( "The car has to pass the vehicle inspection first", constraintViolations.iterator().next().getMessage() ); // let's go to the vehicle inspection car.setPassedVehicleInspection( true ); assertEquals( 0, validator.validate( car, CarChecks.class ).size() ); // now let's add a driver. He is 18, but has not passed the driving test yet Driver john = new Driver( "John Doe" ); john.setAge( 18 ); car.setDriver( john ); constraintViolations = validator.validate( car, DriverChecks.class ); assertEquals( 1, constraintViolations.size() ); assertEquals( "You first have to pass the driving test", constraintViolations.iterator().next().getMessage() ); // ok, John passes the test john.passedDrivingTest( true ); assertEquals( 0, validator.validate( car, DriverChecks.class ).size() ); // just checking that everything is in order now assertEquals( 0, validator.validate( car, Default.class, CarChecks.class, DriverChecks.class ).size() );
组继承:
1 2 3 4 5 6 7 8
publicclassSuperCarextendsCar{ @AssertTrue( message = "Race car must have a safety belt", groups = RaceCarChecks.class ) privateboolean safetyBelt; // getters and setters ... }
// create a supercar and check that it's valid as a generic Car assertEquals( "must be greater than or equal to 2", validator.validate( superCar ).iterator().next().getMessage() ); // check that this supercar is valid as generic car and also as race car Set<ConstraintViolation<SuperCar>> constraintViolations = validator .validate( superCar, RaceCarChecks.class ); assertThat( constraintViolations ).extracting( "message" ).containsOnly( "Race car must have a safety belt", "must be greater than or equal to 2" );
Defines the supported target element types for the constraint. @CheckCase may be used on fields (element type FIELD), JavaBeans properties as well as method return values (METHOD), method/constructor parameters (PARAMETER) and type argument of parameterized types (TYPE_USE). The element type ANNOTATION_TYPE allows for the creation of composed constraints (see Section 6.4, “Constraint composition”) based on @CheckCase. When creating a class-level constraint (see Section 2.1.4, “Class-level constraints”), the element type TYPE would have to be used. Constraints targeting the return value of a constructor need to support the element type CONSTRUCTOR. Cross-parameter constraints (see Section 6.3, “Cross-parameter constraints”) which are used to validate all the parameters of a method or constructor together, must support METHOD or CONSTRUCTOR, respectively.
@Repeatable(List.class) List 是容器声明类型,该注解在同一个地方可以使用多次。(不懂)
二、实现一个 validator
上文已经定义了一个注解,加下来需要创建一个约束校验器,它可以用CheckCase 注解来校验元素。此处必须要实现ConstraintValidator 接口: Example 6.3: Implementing a constraint validator for the constraint @CheckCase