0Pricing
Java Academy · Lekcja

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()); // 3

Szybkie 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

  1. Walidacja beanów: @NotNull, @Size, @Pattern
  2. Niestandardowe adnotacje ograniczeń
  3. Globalna obsługa wyjątków za pomocą @ControllerAdvice
  4. Szczegóły problemu RFC 7807 i spójne odpowiedzi błędów
← Powrót do Java Academy