Интерфейсни контролери през пролетта

1. Въведение

В този урок разглеждаме нова функция на Spring MVC, която ни позволява да задаваме уеб заявките, използвайки обичайните интерфейси на Java.

2. Общ преглед

Обикновено, когато дефинираме контролер в Spring MVC, ние украсяваме неговите методи с различни анотации, които уточняват заявката: URL адреса на крайната точка, метода на HTTP заявка, променливите на пътя и т.н.

Можем например да въведем / save / {id} крайната точка, като използваме споменатите анотации по иначе обикновен метод:

@PostMapping("/save/{id}") @ResponseBody public Book save(@RequestBody Book book, @PathVariable int id) { // implementation }

Естествено, това изобщо не е проблем, когато имаме само един контролер, който обработва заявките. Ситуацията се променя малко, когато имаме различни контролери със същите подписи на методите.

Например, може да имаме две различни версии на контролера - поради миграция или подобни -, които имат едни и същи подписи на метода. В този случай ще имаме значително количество дублирани анотации, които придружават дефинициите на метода. Очевидно това би нарушило принципа СУХО ( не се повтаряйте ).

Ако тази ситуация се случи за чисто Java класове, ние просто ще дефинираме интерфейс и ще накараме класовете да внедрят този интерфейс. В контролерите основната тежест върху методите не се дължи на подписите на метода, а поради анотациите на метода.

Пролет 5.1 обаче представи нова функция:

Анотациите на параметрите на контролера се откриват и на интерфейсите: Позволява пълни договори за картографиране в интерфейсите на контролера.

Нека да проучим как можем да използваме тази функция.

3. Интерфейс на контролера

3.1. Контекстна настройка

Ние илюстрираме новата функция, като използваме пример за много просто приложение REST, което управлява книги. Той ще се състои само от един контролер с методи, които ни позволяват да извличаме и модифицираме книгите.

В урока ние се концентрираме само върху проблемите, свързани с функцията. Всички проблеми с изпълнението на приложението могат да бъдат намерени в нашето хранилище на GitHub.

3.2. Интерфейс

Нека дефинираме обичайния интерфейс на Java, в който дефинираме не само подписите на методите, но и вида на уеб заявките, които те трябва да обработват:

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

Забележете, че може да имаме анотации на ниво клас, както и такива на ниво метод. Сега можем да създадем контролер, който реализира този интерфейс:

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

Все пак трябва да добавим анотация на ниво клас @RestController или @Controller към нашия контролер. Дефиниран по този начин, контролерът наследява всички анотации, свързани с картографирането на уеб заявките.

За да проверим дали контролерът вече работи както се очаква, нека стартираме приложението и натиснем метода getAll () , като направим съответната заявка:

curl //localhost:8081/book/

Въпреки че контролерът изпълнява интерфейса, ние можем допълнително да го настроим чрез добавяне на анотации в уеб заявки. Можем да направим това по начин, по който го направихме за интерфейса: или на ниво клас, или на ниво метод. Всъщност използвахме тази възможност, когато дефинирахме контролера:

@RequestMapping("/book") public class BookController implements BookOperations {...}

Ако добавим анотации за уеб заявки към контролера, те ще имат предимство пред тези на интерфейса. С други думи, Spring интерпретира интерфейсите на контролера по начин, подобен на начина, по който Java се справя с наследяването.

Ние дефинираме всички често срещани свойства на уеб заявката в интерфейса, но в контролера винаги можем да ги прецизираме.

3.3. Внимание

Когато разполагаме с интерфейс и различни контролери, които го прилагат, може да стигнем до ситуация, при която дадена уеб заявка може да бъде обработена от повече от един метод. Естествено, Spring ще направи изключение:

Caused by: java.lang.IllegalStateException: Ambiguous mapping.

Ако декорираме контролера с @RequestMapping , може да намалим риска от двусмислени картографирания.

4. Заключение

В този урок разгледахме нова функция, въведена през пролетта 5.1. Сега, когато Spring MVC контролерите реализират интерфейс, те правят това не само по стандартния начин на Java, но и наследяват цялата свързана с уеб заявката функционалност, дефинирана в интерфейса.

Както винаги, може да намерим съответните кодови фрагменти в нашето хранилище GitHub.