Spring Boot 4 – komplett guide · Lektion

Skapa egna constraint-annoteringar

Skapa återanvändbara egna validators med ConstraintValidator för domänspecifik valideringslogik.

Lektion 2 av 413 steg

Skapa egna constraint-annoteringar är en gratis lektion i Spring Boot 4 – komplett guide på CoddyKit. Detta är lektion 2 av 4. Du kan läsa vilka 3 lektioner som helst i den här lärvägen kostnadsfritt i sin helhet – därefter låser CoddyKit PRO upp alla lektioner, plus praktisk övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Den ingår i lärvägen för Spring Boot 4 – komplett guide, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i Spring Boot 4 – komplett guide innehåller totalt 4 lektioner.

Varför egna constraints?

Bean Validation levereras med annoteringar som @NotNull, @Size och @Email. Men verkliga applikationer har domänspecifika regler som ingen inbyggd annotering täcker.

  • Ett användarnamn måste vara skrivet med små bokstäver och innehålla 3–20 tecken
  • Ett telefonnummer måste följa formatet i ert land
  • Ett statusfält måste vara ett av en tillåten uppsättning enum-värden

I stället för att skriva manuella kontroller i varje controller eller tjänst kan ni skapa en återanvändbar egen constraint-annotering som kopplas in i samma valideringspipeline som de inbyggda.

En egen constraints två delar

Varje egen constraint i Spring Boot 4 (som använder Jakarta Bean Validation) består av exakt två delar:

  • Annoteringen — det ni skriver på ett fält, till exempel @ValidUsername. Den deklarerar metadata och pekar på en validator.
  • ConstraintValidator — en klass som innehåller den faktiska logiken i isValid().

Metaannoteringen @Constraint kopplar ihop de två. När validering körs instansierar ramverket er validator och anropar isValid() för varje annoterat fält.

Definiera annoteringen

En egen constraint-annotering måste deklarera tre standardattribut: message, groups och payload. @Constraint(validatedBy = ...) kopplar den till en validatorklass.

Observera att importerna kommer från jakarta.validation, inte från det gamla javax.validation.

import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.*;

@Documented
@Constraint(validatedBy = UsernameValidator.class)
@Target({ ElementType.FIELD, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidUsername {
    String message() default "invalid username";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

Förstå @Target och @Retention

Två metaannoteringar styr var och när er constraint gäller:

  • @Target — var annoteringen kan placeras. FIELD för entitetsfält, PARAMETER för metodargument, METHOD för getters och TYPE_USE för generiska typer som List<@ValidUsername String>.
  • @Retention(RUNTIME) — annoteringen måste finnas kvar under körning så att valideringsmotorn kan läsa den via reflection. Detta är obligatoriskt; retention med SOURCE eller CLASS skulle göra constrainten osynlig.

Skriva ConstraintValidator

Validatern implementerar ConstraintValidator<A, T>, där A är er annoteringstyp och T är typen för värdet som valideras (till exempel String).

Metoden isValid() returnerar true om värdet är giltigt. En viktig regel är att behandla null som giltigt och låta @NotNull hantera null-värden separat. Då förblir varje constraint fokuserad på en enda sak.

import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;

public class UsernameValidator
        implements ConstraintValidator<ValidUsername, String> {

    @Override
    public boolean isValid(String value, ConstraintValidatorContext ctx) {
        if (value == null) {
            return true; // let @NotNull handle null
        }
        return value.matches("^[a-z0-9_]{3,20}$");
    }
}

Tillämpa constrainten

När constrainten väl är definierad används den precis som en inbyggd constraint. Placera den på ett DTO-fält och kombinera den med andra constraints. Spring utvärderar alla när objektet valideras.

Använd @NotNull tillsammans med @ValidUsername, eftersom validatern avsiktligt tillåter null.

import jakarta.validation.constraints.NotNull;

public record RegisterRequest(
        @NotNull
        @ValidUsername(message = "username must be 3-20 lowercase chars")
        String username,

        @NotNull
        String password
) {}

Utlösa validering i en controller

För att validera inkommande request bodies annoterar ni parametern med @Valid. Om någon constraint misslyckas kastar Spring en MethodArgumentNotValidException innan metodkroppen körs.

Det message som ni anger i annoteringen blir felmeddelandet som rapporteras tillbaka till klienten.

import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/users")
public class UserController {

    @PostMapping("/register")
    public String register(@Valid @RequestBody RegisterRequest req) {
        return "registered: " + req.username();
    }
}

Anpassa felmeddelandet dynamiskt

Ibland räcker inte ett statiskt meddelande – Ni vill att meddelandet ska återspegla varför valideringen misslyckades. Använd ConstraintValidatorContext för att inaktivera standardmeddelandet och skapa ett anpassat meddelande.

Ni måste först anropa disableDefaultConstraintViolation() och därefter lägga till Er egen validering. Annars visas båda meddelandena.

@Override
public boolean isValid(String value, ConstraintValidatorContext ctx) {
    if (value == null) return true;
    if (value.length() < 3 || value.length() > 20) {
        ctx.disableDefaultConstraintViolation();
        ctx.buildConstraintViolationWithTemplate(
                "username length must be between 3 and 20")
           .addConstraintViolation();
        return false;
    }
    return value.matches("^[a-z0-9_]+$");
}

Skicka parametrar till Er constraint

Gör constraints konfigurerbara genom att lägga till attribut i annoteringen. Till exempel kan @ValidUsername ta emot en minimi- och en maximilängd med min och max.

Validatorn läser in dessa i initialize(), som körs en gång innan anropen till isValid().

public @interface ValidUsername {
    String message() default "invalid username";
    int min() default 3;
    int max() default 20;
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

Läsa parametrar i initialize()

Överskugga initialize() för att spara annoteringens attributvärden i fält. Ramverket anropar metoden en gång per validatorinstans, innan något anrop till isValid() görs.

På så sätt kan en enda validatorklass användas för många olika konfigurationer.

public class UsernameValidator
        implements ConstraintValidator<ValidUsername, String> {

    private int min;
    private int max;

    @Override
    public void initialize(ValidUsername ann) {
        this.min = ann.min();
        this.max = ann.max();
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext ctx) {
        if (value == null) return true;
        return value.length() >= min && value.length() <= max
                && value.matches("^[a-z0-9_]+$");
    }
}

Ren Java: fristående valideringslogik

Den centrala regex- och längdlogiken är vanlig Java som Ni kan testa utan något ramverk. Här uttrycks samma regel som ett körbart program, så att Ni kan verifiera logiken innan Ni kopplar in den i Spring.

Denna isolering gör Era validatorer enkla att enhetstesta.

public class Main {
    static boolean isValidUsername(String value, int min, int max) {
        if (value == null) return true;
        return value.length() >= min && value.length() <= max
                && value.matches("^[a-z0-9_]+$");
    }

    public static void main(String[] args) {
        System.out.println(isValidUsername("alice_99", 3, 20)); // true
        System.out.println(isValidUsername("Al", 3, 20));       // false
        System.out.println(isValidUsername("Bad Name", 3, 20)); // false
        System.out.println(isValidUsername(null, 3, 20));       // true
    }
}

Snabb kontroll

Ni implementerar en anpassad ConstraintValidator för ett String-fält. Fältet är valfritt, och en separat @NotNull hanterar redan null-värden. Vad ska isValid() returnera när värdet är null?

Sammanfattning

Ni har lärt Er att skapa återanvändbara anpassade constraint-annoteringar i Spring Boot 4:

  • En constraint består av två delar: annoteringen (med message, groups och payload) samt en ConstraintValidator.
  • @Constraint(validatedBy = ...) kopplar ihop dem. @Retention(RUNTIME) och @Target styr synlighet och placering.
  • isValid() innehåller logiken och bör behandla null som giltigt genom att överlåta kontrollen till @NotNull.
  • Använd initialize() för att läsa annoteringsparametrar och ConstraintValidatorContext för att skapa dynamiska meddelanden.
  • Använd constrainten på DTO-fält och utlös den med @Valid i controllers.

Med dessa mönster finns domänreglerna på en enda testad plats och kan anslutas sömlöst till Springs valideringspipeline.

Gratis att börja

Lär dig Java med en AI-lärare – gratis

Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.

Kurser
21
Lektioner
84

Vanliga frågor

Är lektionen ”Skapa egna constraint-annoteringar” gratis?

Ja – du kan läsa vilka 3 lektioner som helst i lärvägen Spring Boot 4 – komplett guide, inklusive ”Skapa egna constraint-annoteringar”, kostnadsfritt i sin helhet här på webben. Därefter låser CoddyKit PRO upp alla lektioner, plus interaktiv övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Kursen i Spring Boot 4 – komplett guide innehåller totalt 4 lektioner.

Vad lär jag mig i ”Skapa egna constraint-annoteringar”?

Skapa återanvändbara egna validators med ConstraintValidator för domänspecifik valideringslogik. Ni övar på Spring Boot 4 – komplett guide med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.

Behöver jag någon erfarenhet för att börja lära mig Spring Boot 4 – komplett guide?

Du behöver inga förkunskaper. Utbildningen i Spring Boot 4 – komplett guide på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 2 av 4.

Hur lång tid tar lektionen ”Skapa egna constraint-annoteringar”?

De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.

Kan jag skriva och köra kod i den här Spring Boot 4 – komplett guide-lektionen?

Ja. Varje Spring Boot 4 – komplett guide-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.

Alla lektioner i den här kursen

  1. Bean Validation-begränsningar och constraint-grupper
  2. Skapa egna constraint-annoteringar
  3. Global felhantering med @ControllerAdvice
  4. RFC 7807-svar med problemdetaljer
← Tillbaka till Spring Boot 4 – komplett guide