Methodebeperkingen met Bean Validation 2.0

1. Overzicht

In dit artikel bespreken we hoe we methodebeperkingen kunnen definiëren en valideren met Bean Validation 2.0 (JSR-380).

In het vorige artikel hebben we JSR-380 met zijn ingebouwde annotaties besproken en hoe eigenschapvalidatie kan worden geïmplementeerd.

Hier zullen we ons concentreren op de verschillende soorten methodebeperkingen, zoals:

  • beperkingen met één parameter
  • kruisparameter
  • retourbeperkingen

We zullen ook bekijken hoe we de beperkingen handmatig en automatisch kunnen valideren met Spring Validator.

Voor de volgende voorbeelden hebben we exact dezelfde afhankelijkheden nodig als in Java Bean Validation Basics.

2. Verklaring van methodebeperkingen

Starten, we zullen eerst bespreken hoe beperkingen voor methodeparameters kunnen worden verklaard en waarden van methoden kunnen worden geretourneerd.

Zoals eerder vermeld, kunnen we annotaties gebruiken van javax.validation.constraints, maar we kunnen ook custom constraints specificeren (bijv. voor custom constraints of cross-parameter constraints).

2.1. Beperkingen met één parameter

Het definiëren van beperkingen op enkele parameters is eenvoudig. We hoeven alleen maar annotaties aan elke parameter toe te voegen, zoals vereist:

public void createReservation (@NotNull @Future LocalDate begin, @Min (1) int duur, @NotNull Customer customer) {// ...}

Evenzo kunnen we dezelfde aanpak gebruiken voor constructeurs:

openbare klasse Klant {openbare klant (@Size (min = 5, max = 200) @NotNull String firstName, @Size (min = 5, max = 200) @NotNull String lastName) {this.firstName = firstName; this.lastName = achternaam; } // eigenschappen, getters en setters}

2.2. Cross-parameter beperkingen gebruiken

In sommige gevallen moeten we mogelijk meerdere waarden tegelijk valideren, bijvoorbeeld als twee numerieke bedragen de ene groter zijn dan de andere.

Voor deze scenario's kunnen we aangepaste parameteroverschrijdende beperkingen definiëren, die mogelijk afhankelijk zijn van twee of meer parameters.

Parameteroverschrijdende beperkingen kunnen worden beschouwd als de methodevalidatie die equivalent is aan beperkingen op klassenniveau. We zouden beide kunnen gebruiken om validatie te implementeren op basis van verschillende eigenschappen.

Laten we eens kijken naar een eenvoudig voorbeeld: een variatie op de createReservation () methode uit de vorige sectie heeft twee parameters van het type nodig Lokale datum: een begindatum en een einddatum.

Daarom willen we ervoor zorgen dat beginnen is in de toekomst, en einde is na beginnen. In tegenstelling tot het vorige voorbeeld, kunnen we dit niet definiëren met behulp van enkele parameterbeperkingen.

In plaats daarvan hebben we een parameteroverschrijdende beperking nodig.

In tegenstelling tot enkele parameterbeperkingen, cross-parameter beperkingen worden gedeclareerd op de methode of constructor:

@ConsistentDateParameters public void createReservation (LocalDate begin, LocalDate end, Customer customer) {// ...}

2.3. Cross-parameter beperkingen creëren

Om het @ConsistentDateParameters beperking, we hebben twee stappen nodig.

Ten eerste moeten we definieer de annotatie van de beperking:

@Constraint (validatedBy = ConsistentDateParameterValidator.class) @Target ({METHOD, CONSTRUCTOR}) @Retention (RUNTIME) @Documented public @interface ConsistentDateParameters {String message () default "Einddatum moet na de begindatum liggen en beide moeten in de toekomst liggen "; Klasse [] groepen () standaard {}; Klasse [] payload () standaard {}; }

Hier zijn deze drie eigenschappen verplicht voor annotaties met beperkingen:

  • bericht - geeft de standaardsleutel terug voor het maken van foutmeldingen, dit stelt ons in staat om berichtinterpolatie te gebruiken
  • groepen - stelt ons in staat om validatiegroepen voor onze beperkingen te specificeren
  • laadvermogen - kan worden gebruikt door klanten van de Bean Validation API om aangepaste payload-objecten aan een beperking toe te wijzen

Raadpleeg de officiële documentatie voor details over het definiëren van een aangepaste beperking.

Daarna kunnen we de validatorklasse definiëren:

@SupportedValidationTarget (ValidationTarget.PARAMETERS) openbare klasse ConsistentDateParameterValidator implementeert ConstraintValidator {@Override openbare boolean isValid (Object [] waarde, ConstraintValidatorContext context) {if (waarde [0] == null || waarde [1 retour == null) {1 retour == null) ; } if (! (waarde [0] instantie van LocalDate) ||! (waarde [1] instantie van LocalDate)) {throw nieuwe IllegalArgumentException ("Illegale methodehandtekening, twee parameters verwacht van het type LocalDate."); } return ((LocalDate) waarde [0]). isAfter (LocalDate.now ()) && ((LocalDate) waarde [0]). isBefore ((LocalDate) waarde [1]); }}

Zoals we kunnen zien, is de is geldig() methode bevat de feitelijke validatielogica. Ten eerste zorgen we ervoor dat we twee type parameters krijgen LocalDate. Daarna kijken we of beide in de toekomst liggen en einde is na beginnen.

Het is ook belangrijk op te merken dat de @SupportedValidationTarget (ValidationTarget.PARAMETERS) annotatie op de ConsistentDateParameterValidator klasse is vereist. De reden hiervoor is omdat @ConsistentDateParameter wordt ingesteld op method-niveau, maar de beperkingen zullen worden toegepast op de methodeparameters (en niet op de geretourneerde waarde van de methode, zoals we in de volgende sectie zullen bespreken).

Opmerking: de Bean Validation-specificatie raadt aan om te overwegen nul-waarden als geldig. Als nul is geen geldige waarde, de @Niet nul-annotatie moet in plaats daarvan worden gebruikt.

2.4. Retourwaardebeperkingen

Soms moeten we een object valideren omdat het door een methode wordt geretourneerd. Hiervoor kunnen we retourwaardebeperkingen gebruiken.

In het volgende voorbeeld worden ingebouwde beperkingen gebruikt:

openbare klasse ReservationManagement {@NotNull @Size (min = 1) openbare lijst getAllCustomers () {return null; }}

Voor getAllCustomers ()gelden de volgende beperkingen:

  • Ten eerste mag de geretourneerde lijst dat niet zijn nul en moet minimaal één vermelding hebben
  • Bovendien mag de lijst geen nul inzendingen

2.5. Retourwaarde aangepaste beperkingen

In sommige gevallen moeten we mogelijk ook complexe objecten valideren:

openbare klasse ReservationManagement {@ValidReservation openbare reservering getReservationsById (int id) {return null; }}

In dit voorbeeld is een geretourneerd Reservering object moet voldoen aan de beperkingen die zijn gedefinieerd door @ValidReservation, die we hierna zullen definiëren.

Opnieuw, we moeten eerst de constraint-annotatie definiëren:

@Constraint (validatedBy = ValidReservationValidator.class) @Target ({METHOD, CONSTRUCTOR}) @Retention (RUNTIME) @Documented public @interface ValidReservation {String message () standaard "Einddatum moet na begindatum" + "zijn en beide moeten in de toekomst moet het kamernummer groter zijn dan 0 "; Klasse [] groepen () standaard {}; Klasse [] payload () standaard {}; }

Daarna definiëren we de validatorklasse:

openbare klasse ValidReservationValidator implementeert ConstraintValidator {@Override openbare boolean isValid (Reserveringsreservering, ConstraintValidatorContext-context) {if (reservering == null) {return true; } if (! (reserveringsinstantie van reservering)) {throw nieuwe IllegalArgumentException ("Illegale methodehandtekening," + "verwachte parameter van het type Reservering."); } if (reservering.getBegin () == null || reservering.getEnd () == null || reservering.getCustomer () == null) {return false; } terug (reservering.getBegin (). isAfter (LocalDate.now ()) && reservering.getBegin (). isBefore (reservering.getEnd ()) && reservering.getRoom ()> 0); }}

2.6. Retourwaarde in constructeurs

Zoals we hebben gedefinieerd METHODE en CONSTRUCTEUR net zo doelwit binnen ons Geldige reservering interface voor, we kunnen ook de constructor van Reservering om geconstrueerde instanties te valideren:

openbare klasreservering {@ValidReservation openbare reservering (LocalDate begin, LocalDate end, Customer customer, int room) {this.begin = begin; this.end = end; this.customer = klant; this.room = kamer; } // eigenschappen, getters en setters}

2.7. Trapsgewijze validatie

Ten slotte stelt de Bean Validation API ons in staat om niet alleen afzonderlijke objecten te valideren, maar ook objectgrafieken, met behulp van de zogenaamde cascaded validation.

Daarom kunnen we gebruiken @Geldig voor een trapsgewijze validatie, als we complexe objecten willen valideren. Dit werkt zowel voor methodeparameters als voor retourwaarden.

Laten we aannemen dat we een Klant klasse met enkele eigenschapsbeperkingen:

openbare klasse Klant {@Size (min = 5, max = 200) private String firstName; @Size (min = 5, max = 200) private String lastName; // constructeur, getters en setters}

EEN Reservering klasse kan een Klant eigenschap, evenals andere eigenschappen met beperkingen:

openbare klasse Reservering {@ Geldige particuliere klantklant; @Positive privé int kamer; // verdere eigenschappen, constructor, getters en setters}

Als we nu verwijzen Reservering als methodeparameter, we kunnen de recursieve validatie van alle eigenschappen afdwingen:

public void createNewCustomer (@Valid Reservation-reservering) {// ...}

Zoals we kunnen zien, gebruiken we @Geldig op twee plaatsen:

  • Op de reservering-parameter: het activeert de validatie van de Reservering-object, wanneer createNewCustomer () wordt genoemd
  • Omdat we hier een geneste objectgrafiek hebben, moeten we ook een @Geldig op de klant-attribuut: daardoor activeert het de validatie van deze geneste eigenschap

Dit werkt ook voor methoden die een object van het type retourneren Reservering:

@ Geldige openbare reservering getReservationById (int id) {return null; }

3. Methodebeperkingen valideren

Na de verklaring van beperkingen in de vorige sectie, kunnen we nu doorgaan met het daadwerkelijk valideren van deze beperkingen. Daarvoor hebben we meerdere benaderingen.

3.1. Automatische validatie met lente

Spring Validation biedt een integratie met Hibernate Validator.

Opmerking: Spring Validation is gebaseerd op AOP en gebruikt Spring AOP als de standaardimplementatie. Daarom werkt validatie alleen voor methoden, maar niet voor constructeurs.

Als we nu willen dat Spring onze beperkingen automatisch valideert, moeten we twee dingen doen:

Ten eerste moeten we de bonen, die zullen worden gevalideerd, annoteren met @Gevalideerd:

@Validated public class ReservationManagement {public void createReservation (@NotNull @Future LocalDate begin, @Min (1) int duration, @NotNull Customer customer) {// ...} @NotNull @Size (min = 1) openbare lijst getAllCustomers ( ) {retourneer null; }}

Ten tweede moeten we een MethodValidationPostProcessor Boon:

@Configuration @ComponentScan ({"org.baeldung.javaxval.methodvalidation.model"}) openbare klasse MethodValidationConfig {@Bean openbare MethodValidationPostProcessor methodValidationPostProcessor () {retourneer nieuwe MethodValidationPostProcessor (); }}

De container zal nu een javax.validation.ConstraintViolationException, als een beperking wordt geschonden.

Als we Spring Boot gebruiken, registreert de container een MethodValidationPostProcessor boon voor ons zolang slaapstand-validator staat in het klassenpad.

3.2. Automatische validatie met CDI (JSR-365)

Vanaf versie 1.1 werkt Bean Validation met CDI (Contexts and Dependency Injection for Jakarta EE).

Als onze applicatie wordt uitgevoerd in een Jakarta EE-container, valideert de container automatisch methodebeperkingen op het moment van aanroep.

3.3. Programmatische validatie

Voor handmatige methode validatie in een zelfstandige Java-applicatie, kunnen we de javax.validation.executable.ExecutableValidator koppel.

We kunnen een instantie ophalen met behulp van de volgende code:

ValidatorFactory factory = Validation.buildDefaultValidatorFactory (); ExecutableValidator executableValidator = factory.getValidator (). ForExecutables ();

ExecutableValidator biedt vier methoden:

  • validateParameters () en validateReturnValue () voor methodevalidatie
  • validateConstructorParameters () en validateConstructorReturnValue () voor constructor validatie

Validatie van de parameters van onze eerste methode createReservation () zou er als volgt uitzien:

ReservationManagement-object = nieuw ReservationManagement (); Method method = ReservationManagement.class .getMethod ("createReservation", LocalDate.class, int.class, Customer.class); Object [] parameterValues ​​= {LocalDate.now (), 0, null}; Set violations = executableValidator.validateParameters (object, methode, parameterValues);

Opmerking: de officiële documentatie raadt af om deze interface rechtstreeks vanuit de applicatiecode aan te roepen, maar om deze te gebruiken via een methode voor onderscheppingstechnologie, zoals AOP of proxy's.

Als u geïnteresseerd bent in het gebruik van de Uitvoerbare validator interface, kunt u de officiële documentatie bekijken.

4. Conclusie

In deze tutorial hebben we snel gekeken naar het gebruik van methodebeperkingen met Hibernate Validator, ook hebben we enkele nieuwe functies van JSR-380 besproken.

Eerst hebben we besproken hoe we verschillende soorten beperkingen kunnen declareren:

  • Beperkingen met één parameter
  • Kruisparameter
  • Retourwaardebeperkingen

We hebben ook gekeken hoe we de beperkingen handmatig en automatisch konden valideren met Spring Validator.

Zoals altijd is de volledige broncode van de voorbeelden beschikbaar op GitHub.