Niestandardowe adnotacje ograniczeń
Proszę utworzyć niestandardową adnotację @UniqueEmail wraz z implementacją ConstraintValidator.
Niestandardowe adnotacje ograniczeń to bezpłatna lekcja Java Academy na CoddyKit. To lekcja 2 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Java Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Java Academy zawiera 4 lekcji w sumie.
Gdy standardowe ograniczenia nie wystarczają
Standardowe adnotacje Bean Validation obejmują typowe przypadki. W przypadku reguł specyficznych dla domeny (np. unikalnego adresu e-mail, poprawnego numeru IBAN lub siły hasła) należy utworzyć niestandardowe adnotacje ograniczeń.
Definiowanie adnotacji ograniczenia
Należy utworzyć adnotację za pomocą @Constraint(validatedBy = ...), wymaganych elementów message, groups i payload oraz odpowiednich ustawień retencji i celu zastosowania.
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
@Documented
@Constraint(validatedBy = UniqueEmailValidator.class)
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface UniqueEmail {
String message() default "Email already registered";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}Implementowanie ConstraintValidator
Należy zaimplementować ConstraintValidator<UniqueEmail, String>. Metoda initialize odczytuje elementy adnotacji, a isValid wykonuje właściwe sprawdzenie.
@Component
public class UniqueEmailValidator implements ConstraintValidator<UniqueEmail, String> {
@Autowired private UserRepository repo;
@Override
public boolean isValid(String email, ConstraintValidatorContext ctx) {
if (email == null) return true; // let @NotBlank handle null
return !repo.existsByEmail(email);
}
}Korzystanie z niestandardowego ograniczenia
Adnotację należy stosować tak jak każde standardowe ograniczenie: na polu, parametrze lub wartości zwracanej.
public record CreateUserRequest(
@NotBlank
String name,
@NotBlank @Email @UniqueEmail
String email
) {}Niestandardowy komunikat z interpolacją
Elementy adnotacji należy wskazywać w komunikacie za pomocą {elementName}. Metoda ctx.buildConstraintViolationWithTemplate() pozwala dodawać niestandardowe komunikaty zawierające dane dynamiczne.
@Constraint(validatedBy = RangeValidator.class)
public @interface InRange {
int min() default 0;
int max() default 100;
String message() default "Must be between {min} and {max}";
// ...
}Ograniczenie na poziomie klasy
Ograniczenie można zastosować na poziomie klasy, aby wspólnie walidować wiele pól — jest to przydatne w przypadku reguł między polami, takich jak potwierdzenie hasła.
@Documented
@Constraint(validatedBy = PasswordMatchValidator.class)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface PasswordMatch {
String message() default "Passwords do not match";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}Implementacja walidatora na poziomie klasy
Walidator otrzymuje cały obiekt. Może uzyskać dostęp do wielu pól, aby wykonać walidację między polami.
public class PasswordMatchValidator implements ConstraintValidator<PasswordMatch, ChangePasswordRequest> {
public boolean isValid(ChangePasswordRequest req, ConstraintValidatorContext ctx) {
if (req.getNewPassword() == null) return true;
boolean match = req.getNewPassword().equals(req.getConfirmPassword());
if (!match) {
ctx.disableDefaultConstraintViolation();
ctx.buildConstraintViolationWithTemplate("Passwords do not match")
.addPropertyNode("confirmPassword").addConstraintViolation();
}
return match;
}
}Komponowanie ograniczeń
Metawłasne ograniczenie można utworzyć, oznaczając niestandardową adnotację istniejącymi ograniczeniami. Oba ograniczenia zostaną zastosowane automatycznie.
@NotBlank
@Email
@Size(max = 255)
@Constraint(validatedBy = {})
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidEmail {
String message() default "Invalid email";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}Zwracanie wielu naruszeń
Należy użyć ctx.disableDefaultConstraintViolation() i dodać osobny komunikat naruszenia dla każdego problemu wykrytego podczas jednego przebiegu walidatora.
public boolean isValid(String password, ConstraintValidatorContext ctx) {
List<String> issues = new ArrayList<>();
if (password.length() < 8) issues.add("Too short");
if (!password.matches(".*\\d.*")) issues.add("Must contain a digit");
if (issues.isEmpty()) return true;
ctx.disableDefaultConstraintViolation();
issues.forEach(msg -> ctx.buildConstraintViolationWithTemplate(msg).addConstraintViolation());
return false;
}Walidatory zarządzane przez Spring
Ponieważ UniqueEmailValidator jest oznaczony adnotacją @Component, Spring wstrzykuje do niego repozytoria. Działa to tylko wtedy, gdy walidator jest inicjalizowany za pośrednictwem walidatora Spring, który Spring Boot konfiguruje automatycznie.
Testowanie niestandardowych walidatorów
Walidatory należy testować jednostkowo bezpośrednio — w przypadku czystej logiki walidacji kontekst Spring nie jest potrzebny. Do testów integracyjnych należy użyć Validation.buildDefaultValidatorFactory().
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
CreateUserRequest req = new CreateUserRequest("", "bad-email", null);
Set<ConstraintViolation<CreateUserRequest>> violations = validator.validate(req);
System.out.println(violations.size()); // 3Szybkie sprawdzenie
Jaki interfejs implementuje niestandardowy walidator ograniczenia?
Podsumowanie
Należy utworzyć @interface z @Constraint(validatedBy=...). Następnie należy zaimplementować ConstraintValidator. Do wstrzykiwania zależności przez Spring należy użyć @Component. Ograniczenia na poziomie klasy umożliwiają wspólną walidację wielu pól. Komponowanie istniejących ograniczeń pomaga uniknąć duplikacji.
Często zadawane pytania
Czy lekcja „Niestandardowe adnotacje ograniczeń” jest bezpłatna?
Tak — pełny tekst „Niestandardowe adnotacje ograniczeń” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Java Academy, przejdź na CoddyKit PRO. Kurs Java Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Niestandardowe adnotacje ograniczeń”?
Proszę utworzyć niestandardową adnotację @UniqueEmail wraz z implementacją ConstraintValidator. Ćwiczysz Java Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć Java Academy?
Nie wymagamy żadnego doświadczenia. Java Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 4.
Ile czasu zajmuje lekcja „Niestandardowe adnotacje ograniczeń”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji Java Academy?
Tak. Każda lekcja Java Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Walidacja beanów: @NotNull, @Size, @Pattern
- Niestandardowe adnotacje ograniczeń
- Globalna obsługa wyjątków za pomocą @ControllerAdvice
- Szczegóły problemu RFC 7807 i spójne odpowiedzi błędów