В листинге 3.7 мы могли бы агрегировать уже имеющиеся ограничения, например @NotNull. Но основное различие между объединением ограничений и созданием обобщенного ограничения заключается в применении класса реализации, объявляемого в атрибуте validatedBy (здесь он ссылается на класс URLValidator.class).
В листинге 3.8 показан класс реализации URLValidator. Как видите, он реализует интерфейс ConstraintValidator и, следовательно, методы initialize и isValid. Здесь важно отметить, что класс URLValidator имеет три атрибута, определенные в аннотации (protocol, host и port), и инициализирует их в методе initialize(URL url). Этот метод вызывается на этапе инстанцирования валидатора. В качестве параметра он получает ограничивающую аннотацию (здесь URL), поэтому может извлекать значения и использовать их при валидации. Так можно поступить с атрибутом itemURL protocol, который в листинге 3.6 имеет строковое значение "http".
public class URLValidator implements ConstraintValidator
··private String protocol;
··private String host;
··private int port;
··public void initialize(URL url) {
····this.protocol = url.protocol();
····this.host = url.host();
····this.port = url.port();
··}
··public boolean isValid(String value, ConstraintValidatorContext context) {
····if (value == null || value.length() == 0) {
····return true;
··}
··java.net.URL url;
··try {
····// Преобразуем URL в java.net.URL для проверки того,
····// имеет ли URL допустимый формат
····url = new java.net.URL(value);
··} catch (MalformedURLException e) {
····return false;
··}
··// Проверяет, имеет ли атрибут протокола допустимое значение
··if (protocol!= null && protocol.length() > 0 &&
!url.getProtocol(). equals(protocol)) {
····return false;
··}
··if (host!= null && host.length() > 0 &&!url.getHost(). startsWith(host)) {
····return false;
··}
··if (port!= -1 && url.getPort()!= port) {
····return false;
··}
··return true;
··}
}
Метод isValid реализует алгоритм валидации URL, показанный в листинге 3.8. Параметр value содержит значение объекта, который требуется валидировать (например, file://www.cdbookstore.com/item/123). Параметр context инкапсулирует информацию о контексте, в котором осуществляется валидация (подробнее об этом ниже). Возвращаемое значение является логическим и указывает, успешно ли прошла валидация.
Основная задача валидационного алгоритма в листинге 3.8 — привести переданное значение к java.net.URL и проверить, правильно ли оформлен URL. После этого алгоритм также проверяет валидность атрибутов protocol, host и port. Если хотя бы один из них окажется невалидным, то метод вернет false. Как будет показано далее в разделе «Валидация ограничений», поставщик валидации компонентов задействует это логическое значение при создании списка ConstraintViolation.
Обратите внимание: метод isValid расценивает нуль как валидное значение (if (value == null… return true)). Спецификация Bean Validation считает такую практику рекомендуемой. Так удается не дублировать код ограничения @NotNull. Пришлось бы одновременно использовать ограничения @URL и @NotNull, чтобы указать, что вы хотите представить валидный ненулевой URL (такой как атрибут itemURL в листинге 3.6).