Автор оригинала: Andrew Shcherbakov.
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, мы бы просто определили интерфейс и заставили классы реализовать этот интерфейс. В контроллерах основная нагрузка на методы связана не с сигнатурами методов, а с аннотациями методов.
Однако в Spring 5.1 появилась новая функция :
Аннотации параметров контроллера также обнаруживаются на интерфейсах: это позволяет выполнять полные контракты сопоставления в интерфейсах контроллера.
Давайте рассмотрим, как мы можем использовать эту функцию.
3. Интерфейс контроллера
3.1. Настройка контекста
Мы проиллюстрируем новую функцию на примере очень простого приложения REST, которое управляет книгами. Он будет состоять всего из одного контроллера с методами, которые позволяют нам извлекать и изменять книги.
В учебнике мы сосредоточимся только на вопросах, связанных с этой функцией. Все вопросы реализации приложения можно найти в нашем репозитории GitHub .
3.2. Интерфейс
Давайте определим обычный интерфейс Java, в котором мы определяем не только сигнатуры методов, но и тип веб-запросов, которые они должны обрабатывать:
@RequestMapping("/default") public interface BookOperations { @GetMapping("/") ListgetAll(); @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 ListgetAll() {...} @Override public Optional getById(int id) {...} @Override public void save(Book book, int id) {...} }
Мы все равно должны добавить аннотацию уровня класса @RestController или @Controller к нашему контроллеру. Определенный таким образом, контроллер наследует все аннотации, связанные с отображением веб-запросов.
Чтобы проверить, что контроллер теперь работает должным образом, давайте запустим приложение и нажмем метод GetAll () , сделав соответствующий запрос:
curl http://localhost:8081/book/
Несмотря на то, что контроллер реализует интерфейс, мы можем дополнительно настроить его, добавив аннотации веб-запросов. Мы можем сделать это так же, как мы сделали это для интерфейса: либо на уровне класса, либо на уровне метода. На самом деле, мы использовали эту возможность при определении контроллера:
@RequestMapping("/book") public class BookController implements BookOperations {...}
Если мы добавим аннотации веб-запросов к контроллеру, они будут иметь приоритет над аннотациями интерфейса. Другими словами, Spring интерпретирует интерфейсы контроллера аналогично тому, как Java работает с наследованием.
Мы определяем все общие свойства веб-запросов в интерфейсе, но в контроллере мы всегда можем их точно настроить.
3.3. Предостережение
Когда у нас есть интерфейс и различные контроллеры, которые его реализуют, мы можем столкнуться с ситуацией, когда веб-запрос может обрабатываться более чем одним методом. Естественно, весна выдаст исключение:
Caused by: java.lang.IllegalStateException: Ambiguous mapping.
Если мы украсим контроллер с помощью @RequestMapping , мы можем снизить риск неоднозначных сопоставлений.
4. Заключение
В этом уроке мы рассмотрели новую функцию, введенную в Spring 5.1. Теперь, когда контроллеры Spring MVC реализуют интерфейс, они делают это не только стандартным способом Java, но и наследуют все функции, связанные с веб-запросами, определенные в интерфейсе.
Как всегда, мы можем найти соответствующие фрагменты кода в нашем репозитории GitHub .