Interface gestuurde controllers in het voorjaar

1. Inleiding

In deze tutorial beschouwen we een nieuwe functie van Spring MVC waarmee we de webverzoeken kunnen specificeren met behulp van gebruikelijke Java-interfaces.

2. Overzicht

Gewoonlijk versieren we bij het definiëren van een controller in Spring MVC de methoden met verschillende annotaties die het verzoek specificeren: de URL van het eindpunt, de HTTP-verzoekmethode, de padvariabelen, enzovoort.

We kunnen bijvoorbeeld de / save / {id} eindpunt met behulp van genoemde annotaties op een verder eenvoudige methode:

@PostMapping ("/ save / {id}") @ResponseBody openbaar boek opslaan (@RequestBody Book-boek, @PathVariable int id) {// implementatie}

Dit is natuurlijk helemaal geen probleem als we maar één controller hebben die de verzoeken afhandelt. De situatie verandert een beetje als we verschillende controllers hebben met dezelfde methodehandtekeningen.

We kunnen bijvoorbeeld twee verschillende versies van de controller hebben - vanwege migratie of iets soortgelijks - die dezelfde methodehandtekeningen hebben. In dat geval zouden we een aanzienlijk aantal gedupliceerde annotaties hebben die bij de methodedefinities horen. Het zou duidelijk in strijd zijn met de DROGE (herhaal jezelf niet) principe.

Als deze situatie zich zou voordoen voor pure Java-klassen, zouden we eenvoudig een interface definiëren en de klassen deze interface laten implementeren. In de controllers, de belangrijkste belasting van de methoden is niet te wijten aan de methodehandtekeningen, maar aan de annotaties van de methode.

Spring 5.1 introduceerde echter een nieuwe functie:

Annotaties van controllerparameters worden ook gedetecteerd op interfaces: volledige toewijzingscontracten in controllerinterfaces.

Laten we eens kijken hoe we deze functie kunnen gebruiken.

3. Interface van de controller

3.1. Context instellen

We illustreren de nieuwe functie aan de hand van een voorbeeld van een zeer eenvoudige REST-applicatie die boeken beheert. Het zal bestaan ​​uit slechts één controller met methoden waarmee we de boeken kunnen ophalen en wijzigen.

In de zelfstudie concentreren we ons alleen op de problemen die verband houden met de functie. Alle implementatieproblemen van de applicatie zijn te vinden in onze GitHub-repository.

3.2. Koppel

Laten we een gebruikelijke Java-interface definiëren waarin we niet alleen de handtekeningen van de methoden definiëren, maar ook het type webverzoeken dat ze moeten behandelen:

@RequestMapping ("/ default") openbare interface BookOperations {@GetMapping ("/") Lijst getAll (); @GetMapping ("/ {id}") Optioneel getById (@PathVariable int id); @PostMapping ("/ save / {id}") public void save (@RequestBody Book book, @PathVariable int id); }

Merk op dat we zowel een annotatie op klassenniveau als op een methode-niveau kunnen hebben. Nu kunnen we een controller maken die deze interface implementeert:

@RestController @RequestMapping ("/ book") public class BookController implementeert BookOperations {@Override public List getAll () {...} @Override public Optioneel getById (int id) {...} @Override public void save (Book book , int id) {...}}

We moeten nog steeds de annotatie op klasniveau toevoegen @RestController of @Controller aan onze controller. Op deze manier gedefinieerd, neemt de controller alle annotaties over die verband houden met het in kaart brengen van de webverzoeken.

Om te controleren of de controller nu werkt zoals verwacht, laten we de applicatie starten en op de alles krijgen() methode door het overeenkomstige verzoek te doen:

curl // localhost: 8081 / book /

Hoewel de controller de interface implementeert, kunnen we deze verder verfijnen door annotaties voor webverzoeken toe te voegen. We kunnen dat doen op een manier zoals we het deden voor de interface: op klassenniveau of op methodeniveau. We hebben deze mogelijkheid zelfs gebruikt bij het definiëren van de controller:

@RequestMapping ("/ book") openbare klasse BookController implementeert BookOperations {...}

Als we annotaties voor webverzoeken aan de controller toevoegen, hebben deze voorrang op die van de interface. Met andere woorden, Spring interpreteert de controllerinterfaces op een manier die vergelijkbaar is met hoe Java omgaat met overerving.

We definiëren alle algemene eigenschappen van webverzoeken in de interface, maar in de controller kunnen we ze altijd verfijnen.

3.3. Let op Opmerking

Als we een interface hebben en verschillende controllers die deze implementeren, kunnen we in een situatie terechtkomen waarin een webverzoek op meer dan één manier kan worden afgehandeld. Uiteraard gooit de lente een uitzondering:

Veroorzaakt door: java.lang.IllegalStateException: dubbelzinnige mapping.

Als we de controller versieren met @RequestMapping, kunnen we het risico van dubbelzinnige toewijzingen verminderen.

4. Conclusie

In deze tutorial hebben we een nieuwe functie besproken die in Spring 5.1 is geïntroduceerd. Wanneer Spring MVC-controllers nu een interface implementeren, doen ze dit niet alleen op de standaard Java-manier, maar nemen ze ook alle webverzoekgerelateerde functionaliteit over die in de interface is gedefinieerd.

Zoals altijd kunnen we de bijbehorende codefragmenten vinden in onze GitHub-repository.