public class Book {
··@NotNull
··private String title;
··@NotNull @Min(2)
··private Float price;
··@Size(max = 2000)
··private String description;
··private String isbn;
··private Integer nbOfPage;
··// Конструкторы, геттеры, сеттеры
}
В листинге 3.1 показан класс Book с атрибутами, конструкторами, геттерами, сеттерами и аннотациями. Некоторые из этих атрибутов аннотированы с применением встроенных ограничений, таких как @NotNull, @Min и @Size. Так мы указываем валидационной среде исполнения, что заголовок title книги не может быть равен нулю и описание description не может превышать 2000 символов. Как видите, к атрибуту могут быть применены несколько ограничений (например, price не может быть равно нулю и его значение не может быть меньше 2).
Внутренняя организация ограничения
Ограничение определяется как комбинация ограничивающей аннотации и списка реализаций валидации ограничения. Ограничивающая аннотация применяется с типами, методами, полями или другими ограничивающими аннотациями (в случае с составными элементами). В большинстве спецификаций Java EE разработчики используют заранее определенные аннотации (например, @Entity, @Stateless и @Path). Но в случае с CDI (об этом шла речь в предыдущей главе) и при валидации компонентов программистам приходится писать собственные аннотации. Известно, что ограничение при валидации компонентов состоит из:
• аннотации, определяющей ограничение;
• списка классов, реализующих ограничивающий алгоритм с заданным типом.
В то же время аннотация выражает ограничение, действующее в предметной модели. Таким образом, реализация валидации определяет, удовлетворяет ли конкретное значение заданному ограничению.
Ограничение, применяемое с JavaBean, выражается с помощью одной или нескольких аннотаций. Аннотация считается ограничивающей, если применяемая в ней политика хранения содержит RUNTIME и если сама она аннотирована javax.validation.Constraint (эта аннотация ссылается на список реализаций валидации). В листинге 3.2 показана ограничивающая аннотация NotNull. Как видите, @Constraint(validatedBy = {}) указывает на класс реализации NotNullValidator.
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = NotNullValidator.class)
public @interface NotNull {
··String message() default "{javax.validation.constraints.NotNull.message}";
··Class<?>[] groups() default {};
··Class<? extends Payload>[] payload() default {};
}
Ограничивающие аннотации — это самые обычные аннотации, поэтому с ними приходится определять своеобразные метааннотации:
• @Target({METHOD, FIELD…}) — указывает цель, для которой может использоваться аннотация (подробнее об этом — ниже);
• @Retention(RUNTIME) — определяет, как мы будем обращаться с аннотацией. Необходимо использовать как минимум RUNTIME, чтобы поставщик мог проверять ваш объект во время выполнения;
• @Constraint(validatedBy = NotNullValidator.class) — указывает класс (в случае агрегации ограничений — нуль либо список классов), в котором инкапсулирован валидационный алгоритм;
• @Documented — определяет, будет эта аннотация включена в Javadoc или нет. Опциональная метааннотация.
Поверх этих общих метааннотаций спецификация Bean Validation требует задавать для каждой ограничивающей аннотации три дополнительных атрибута:
• message — обеспечивает для аннотации возможность возвращения интернационализированного сообщения об ошибке, выдаваемого в том случае, если ограничение недопустимо. По умолчанию данный атрибут равен ключу;
• groups — используется для контроля за порядком, в котором интерпретируются ограничения, либо для выполнения частичной валидации;
• payload — применяется для ассоциирования метаинформации с ограничением.
Если ваше ограничение определяет все обязательные метааннотации и ограничения, можете добавить любой интересующий вас конкретный параметр. Например, ограничение, проверяющее длину строки, может использовать атрибут length для указания максимальной длины.