В листинге 3.12 показана реализация ограничения @ChronologicalDates. Подобно тем ограничениям, что были рассмотрены выше, оно реализует интерфейс ConstraintValidator, обобщенный тип которого — Order. Метод isValid проверяет, находятся ли три даты в правильном хронологическом порядке, и если это так — возвращает true.
public class ChronologicalDatesValidator implements
ConstraintValidator<ChronologicalDates, Order> {
··@Override
··public void initialize(ChronologicalDates constraintAnnotation) {
··}
··@Override
··public boolean isValid(Order order, ConstraintValidatorContext context) {
····return order.getCreationDate(). getTime() <
···········order.getPaymentDate(). getTime() &&
···········order.getPaymentDate(). getTime() <··
···········order.getDeliveryDate(). getTime();
··}
}
Ограничения, действующие на уровне методов, появились в спецификации Bean Validation 1.1. Существуют ограничения, объявляемые для методов, а также для конструкторов (геттеры не считаются ограниченными методами). Эти ограничения могут быть добавлены к параметрам метода (это будут «ограничения параметров») или к самому методу («ограничения возвращаемых значений»). Таким образом, спецификация Bean Validation может использоваться для описания и валидации соглашения, применяемого с заданным методом или конструктором. Так строится хорошо известный стиль «Программирование по соглашениям»:
• предусловия должны выполняться вызывающей стороной еще до вызова метода или конструктора;
• постусловия гарантированно выполняются для вызывающей стороны после возврата вызова, направленного к методу или конструктору.
В листинге 3.13 показано несколько способов использования ограничений на уровне метода. Сервис CardValidator проверяет кредитную карту в соответствии с конкретным валидационным алгоритмом. Для этого конструктор использует ограничение @NotNull с параметром ValidationAlgorithm. Затем два метода validate возвращают Boolean (валидна кредитная карта или нет?) с ограничением @AssertTrue для возвращаемого типа и ограничениями @NotNull и @Future у параметров методов.
public class CardValidator {
··private ValidationAlgorithm validationAlgorithm;
··public CardValidator(@NotNull ValidationAlgorithm validationAlgorithm) {
····this.validationAlgorithm = validationAlgorithm;
··}
··@AssertTrue
··public Boolean validate(@NotNull CreditCard creditCard) {
····return validationAlgorithm.validate(creditCard.getNumber(),
creditCard.getCtrlNumber());
··}
··@AssertTrue
··public Boolean validate(@NotNull String number, @Future Date expiryDate,
··Integer controlNumber, String type) {
····return validationAlgorithm.validate(number, controlNumber);
··}
}
Часто в бизнес-модели действует механизм наследования. При использовании валидации компонентов приходится накладывать ограничения на классы, суперклассы или интерфейсы вашей бизнес-модели. Наследование ограничений у свойств работает точно так же, как и обычное наследование в Java: оно является кумулятивным. Таким образом, если один компонент наследует от другого, то ограничения наследуемого компонента также заимствуются и будут валидироваться.
В листинге 3.15 показан класс CD, расширяющий Item (листинг 3.14). Оба класса имеют атрибуты и применяемые с ними ограничения. Если валидируется экземпляр CD, то валидируются не только его ограничения, но и ограничения, налагаемые на родительский класс.
Public class Item {
··@NotNull
··protected Long id;
··@NotNull @Size(min = 4, max = 50)
··protected String title;
··protected Float price;
··protected String description;
··@NotNull
··public Float calculateVAT() {
····return price * 0.196f;
··}
··@NotNull
··public Float calculatePrice(@DecimalMin("1.2") Float rate) {
····return price * rate;
··}
}
public class CD extends Item {
··@Pattern(regexp = "[A-Z][a-z]{1,}")
··private String musicCompany;
··@Max(value = 5)
··private Integer numberOfCDs;
··private Float totalDuration;
··@MusicGenre
··private String genre;