Выбрать главу

В листинге 3.12 показана реализация ограничения @ChronologicalDates. Подобно тем ограничениям, что были рассмотрены выше, оно реализует интерфейс ConstraintValidator, обобщенный тип которого — Order. Метод isValid проверяет, находятся ли три даты в правильном хронологическом порядке, и если это так — возвращает true.

Листинг 3.12. Реализация ограничения ChronologicalDates, действующего на уровне класса

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 у параметров методов.

Листинг 3.13. Сервис с конструктором и ограничениями на уровне метода

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, то валидируются не только его ограничения, но и ограничения, налагаемые на родительский класс.

Листинг 3.14. Суперкласс Item, использующий ограничения

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;

··}

}

Листинг 3.15. Класс CD, расширяющий Item

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;