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

assertEquals(0, constr.size());

Set<ConstraintViolation<CD>> constr = validator.validateValue(CD.class, "numberOfCDs", 7);

assertEquals(1, constr.size());

Валидация методов

Методы для валидации параметров и возвращаемых значений методов и конструкторов вы найдете в интерфейсе javax.validation.ExecutableValidator. Метод Validator.forExecutables() возвращает этот ExecutableValidator, с которым вы можете вызывать validateParameters, validateReturnValue, validateConstructorParameters или validateConstructorReturnValue.

В следующем коде мы вызываем метод calculatePrice, передавая значение 1.2. В результате возникает нарушение ограничения, налагаемого на параметр: не выполняется условие @DecimalMin("1.4"). Для этого в коде сначала нужно создать объект java.lang.reflect.Method, нацеленный на метод calculatePrice с параметром типа Float. После этого он получает объект ExecutableValidator и вызывает validateParameters, передавая компонент, метод для вызова и значение параметра (здесь — 1.2). Затем метод удостоверяется в том, что никакие ограничения нарушены не были.

CD cd = new CD("Kind of Blue", 12.5f);

Method method = CD.class.getMethod("calculatePrice", Float.class);

ExecutableValidator methodValidator = validator. forExecutables();

Set<ConstraintViolation<CD>> violations = methodValidator.validateParameters(cd, method,

·················new Object[]{new Float(1.2)});

assertEquals(1, violations.size());

Валидация групп

Группа определяет подмножество ограничений. Вместо валидации всех ограничений для данного компонента проверяется только нужное подмножество. При объявлении каждого ограничения указывается список групп, в которые входит это ограничение. Если явно не объявлена ни одна группа, то ограничение относится к группе Default. Что касается проверки, у всех валидационных методов есть параметр с переменным количеством аргументов, указывающий, сколько групп должно учитываться при выполнении валидации. Если этот параметр не задан, будет использоваться указанная по умолчанию валидационная группа (javax.validation.groups.Default). Если вместо Default указана другая группа, то Default не валидируется.

В листинге 3.21 все ограничения, за исключением применяемых с атрибутом description, относятся к группе Default. Описание (@NotNull @Size(min = 100, max = 5000)) требуется лишь в том случае, если диск должен быть упомянут в каталоге (группа PrintingCatalog). Итак, если мы создаем CD без заголовка, цены и описания, после чего проверяем лишь условия из группы Default, то будут нарушены всего два ограничения @NotNull, касающиеся title и price.

CD cd = new CD();

cd.setDescription("Best Jazz CD ever");

Set<ConstraintViolation<CD>> violations = validator.validate(cd, Default.class);

assertEquals(2, violations.size());

Обратите внимание: в предыдущем коде при валидации явно упоминается группа Default, но это слово можно пропустить. Итак, следующий код идентичен предыдущему:

Set<ConstraintViolation<CD>> violations = validator. validate(cd);

С другой стороны, если бы мы решили проверить CD только для группы PrintingCatalog, то следующий код нарушал бы лишь ограничение, налагаемое на description, так как предоставляемое значение было бы слишком коротким:

CD cd = new CD();

cd.setDescription("Too short");

Set<ConstraintViolation<CD>> violations = validator.validate(cd, PrintingCatalog.class);

assertEquals(1, violations.size());

Если бы вы хотели проверить компонент на соответствие обеим группам — Default и PrintingCatalog, то у вас было бы нарушено три ограничения (@NotNull для title и price, а также очень краткое описание):

CD cd = new CD();

cd.setDescription("Too short");

Set<ConstraintViolation<CD>> violations = validator.validate(cd, Default.class, PrintingCatalog.class);

assertEquals(3, violations.size());

Все вместе

Теперь рассмотрим все изученные концепции вместе и напишем Java-компоненты, с которыми сможем использовать встроенные ограничения, а также разработать наше собственное. В этом примере применяются CDI и ограничения валидации компонентов на основе Java SE (пока не требуется что-либо развертывать в GlassFish). Кроме того, выполняется два интеграционных теста, проверяющих правильность использованных ограничений.

На рис. 3.2 показан класс Customer, имеющий адрес доставки (Address). Оба компонента снабжены встроенными ограничениями (@NotNull, @Size и @Past), которые применяются с их атрибутами. Но нам остается разработать еще два собственных ограничения:

• @Email — агрегированное ограничение, проверяющее правильность адреса электронной почты;

• @ZipCode — ограничение, проверяющее правильность почтового ZIP-кода (для США). В состав ограничения входит как аннотация, так и класс реализации (ZipCodeValidator). Обратите внимание: ZipCodeValidator внедряет вспомогательный класс ZipCodeChecker с аннотацией @Inject (и CDI-квалификатором @USA).