Зміст
Як вже згадувалося в попередній статті, у цій піде мова про створення власної анотації для перевірки кореектності введених даних. Як приклад буде розглянуто порівняння двох паролів на формі реєстрації.
У цій статті будемо розглядати все той же приклад з двох попередніх частин статті про форми в Spring MVC.
Отже, клас SignupForm тепер буде виглядати наступним чином:
package com.seostella.springcustomconstraints.form;
import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotBlank;
import com.seostella.constraints.FieldEquals;
@FieldEquals( field="password", equalsTo="confirmPassword" )
public class SignupForm {
@NotBlank
@Size(min=3, max=16)
private String username;
@NotBlank
private String password;
@NotBlank
private String confirmPassword;
@Email
private String email;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getConfirmPassword() {
return confirmPassword;
}
public void setConfirmPassword(String confirmPassword) {
this.confirmPassword = confirmPassword;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
Як видно з лістингу, додалася одна анотація до класу:
@FieldEquals( field="password", equalsTo="confirmPassword" )
Нам необхідно створити нову анотацію @FieldEquals для обробки валідності полів форми. Код анотації нижче:
package com.seostella.constraints;
import javax.validation.Constraint;
import javax.validation.Payload;
import com.seostella.constraints.impl.FieldEqualsValidator;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Target(TYPE)
@Retention(RUNTIME)
@Constraint(validatedBy = FieldEqualsValidator.class)
@Documented
public @interface FieldEquals {
public static final String MESSAGE = "fields.notMatches";
String message() default MESSAGE;
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
@Target(TYPE)
@Retention(RUNTIME)
@Documented
@interface List {
FieldEquals[] value();
}
String field();
String equalsTo();
}
Про анотації вже було докладно розказано ранніше (див. статтю "Анотації в Java. Введення" та її продовження), тому в цій статті зупинимося тільки на основних моментах:
@Target(TYPE) - дана анотація може описувати тільки клас, інтерфейс, enum або іншу анотацію.
@Retention(RUNTIME) - анотація доступна під час виконання програми.
@Constraint(validatedBy = FieldEqualsValidator.class) - цієї анотацією вказується, який клас буде перевіряти на правильність дані. Ця анотація необхідна щоб спрацював автоматичний механізм перевірки даних.
@Documented - зазначаємо, що згенерований JavaDoc буде містити дану анотацію.
Майже все тіло анотації FieldEquals є шаблоном, який необхідний за замовчуванням для кожної анотації (з причини того, що для анотацій не передбачений механізм успадкування):
String message() default MESSAGE;
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
@Target(TYPE)
@Retention(RUNTIME)
@Documented
@interface List {
FieldEquals[] value();
}
Два поля, які є унікальними для анотації FieldEquals: field і equalsTo. Це поля, які будуть порівнюватися.
Клас FieldEqualsValidator, який перевіряє правильність даних:
package com.seostella.constraints.impl;
import java.lang.reflect.Method;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import com.seostella.constraints.FieldEquals;
public class FieldEqualsValidator implements
ConstraintValidator<FieldEquals, Object> {
private String field;
private String equalsTo;
private String message = FieldEquals.MESSAGE;
public void initialize(FieldEquals constraintAnnotation) {
this.message = constraintAnnotation.message();
this.field = constraintAnnotation.field();
this.equalsTo = constraintAnnotation.equalsTo();
}
public boolean isValid(Object value, ConstraintValidatorContext context) {
try {
final Object fieldObject = getProperty(value, field, null);
final Object equalsToObject = getProperty(value, equalsTo, null);
if (fieldObject == null && equalsToObject == null) {
return true;
}
boolean matches = (fieldObject != null)
&& fieldObject.equals(equalsToObject);
if (!matches) {
String msg = this.message;
if( this.message == null
|| "".equals( this.message )
|| FieldEquals.MESSAGE.equals( this.message ) ){
msg = field + " is not equal to " + equalsTo;
}
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate( msg )
.addNode(equalsTo).addConstraintViolation();
}
return matches;
} catch (final Exception e) {
e.printStackTrace();
}
return true;
}
private Object getProperty(Object value, String fieldName,
Object defaultValue) {
Class<?> clazz = value.getClass();
String methodName = "get" + Character.toUpperCase(fieldName.charAt(0))
+ fieldName.substring(1);
try {
Method method = clazz.getDeclaredMethod(methodName, new Class[0]);
return method.invoke(value);
} catch (Exception e) {
}
return defaultValue;
}
}
В цьому класі все трохи простіше. Він повинен реалізовувати інтерфейс ConstraintValidator і його два методи: initialize() і isValid().
У метод initialize() параметром передається анотація, в якій міститься необхідна інформація: повідомлення про помилку і два поля:
public void initialize(FieldEquals constraintAnnotation) {
this.message = constraintAnnotation.message();
this.field = constraintAnnotation.field();
this.equalsTo = constraintAnnotation.equalsTo();
}
В методі isValid() відбувається обробка даних. Якщо метод повертає true, дані вважаються правильними, якщо false - дані не вірні і користувачеві буде відображена помилка:
public boolean isValid(Object value, ConstraintValidatorContext context) {
try {
final Object fieldObject = getProperty(value, field, null);
final Object equalsToObject = getProperty(value, equalsTo, null);
if (fieldObject == null && equalsToObject == null) {
return true;
}
boolean matches = (fieldObject != null)
&& fieldObject.equals(equalsToObject);
if (!matches) {
String msg = this.message;
if( this.message == null
|| "".equals( this.message )
|| FieldEquals.MESSAGE.equals( this.message ) ){
msg = field + " is not equal to " + equalsTo;
}
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate( msg )
.addNode(equalsTo).addConstraintViolation();
}
return matches;
} catch (final Exception e) {
e.printStackTrace();
}
return true;
}
Параметр value методу isValid() є значенням, яке необхідно перевірити на правильність даних. В даному випадку, це об'єкт класу SignForm. За допомогою рефлексії в наступному коді отримуємо значення двох властивостей, які необхідно порівняти:
final Object fieldObject = getProperty(value, field, null);
final Object equalsToObject = getProperty(value, equalsTo, null);
Далі відбувається звичайне порівняння двох об'єктів і ще одна цікава річ. Наступним кодом повертається помилка, зібрана з двох полів якщо користувач не перевизначив поле message:
String msg = this.message;
if( this.message == null
|| "".equals( this.message )
|| FieldEquals.MESSAGE.equals( this.message ) ){
msg = field + " is not equal to " + equalsTo;
}
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate( msg )
.addNode(equalsTo).addConstraintViolation();
Ця помилка буде виглядати так:
password is not equal to confirmPassword
Код, який демонструє роботу анотації FieldEquals, Ви можете завантажити за наступним посиланням - Завантажити spring-custom-constraints.zip.
< | Перевірка даних форми за допомогою анотацій (@Size, @Email та ін) в Spring MVC |
12 вересня 2012 р. 09:33
|
Спасибо за подборку статей.
Толково подобрано по тематикам. Ничего лишнего(типа: посмотрите какой я умный). И достаточно свежие темы. Редакторам респект. Просьба сохранять стиль и желание писать. |