HTML Diff
0 added 0 removed
Original 2026-01-01
Modified 2026-02-26
1 <p>В этом уроке мы разберем основные элементы Spring Boot для работы с HTTP на примере CRUD приложения для создания страниц сайта. Под такими страницами обычно понимают что-то подобное:</p>
1 <p>В этом уроке мы разберем основные элементы Spring Boot для работы с HTTP на примере CRUD приложения для создания страниц сайта. Под такими страницами обычно понимают что-то подобное:</p>
2 <p>Нам понадобится модель страницы. Для начала создадим класс Page с тремя полями:</p>
2 <p>Нам понадобится модель страницы. Для начала создадим класс Page с тремя полями:</p>
3 <ul><li><em>slug</em>- слаг (идентификатор, который используется для построения ссылок)</li>
3 <ul><li><em>slug</em>- слаг (идентификатор, который используется для построения ссылок)</li>
4 <li><em>name</em>- имя страницы</li>
4 <li><em>name</em>- имя страницы</li>
5 <li><em>body</em>- содержимое страницы</li>
5 <li><em>body</em>- содержимое страницы</li>
6 </ul><p>Структура директорий может быть любой, потому что Spring не накладывает ограничений на нее. Мы будем придерживаться общепринятых стандартов и хранить наши файлы так, как принято в реальных проектах. Например, модели часто кладут в директорию<em>model</em>:</p>
6 </ul><p>Структура директорий может быть любой, потому что Spring не накладывает ограничений на нее. Мы будем придерживаться общепринятых стандартов и хранить наши файлы так, как принято в реальных проектах. Например, модели часто кладут в директорию<em>model</em>:</p>
7 <p>Следующий шаг - реализуем пять CRUD-маршрутов:</p>
7 <p>Следующий шаг - реализуем пять CRUD-маршрутов:</p>
8 <p>Для реализации этих маршрутов нужна возможность хранить добавленные данные. В будущих уроках мы научимся делать это через базу данных, а сейчас просто будем сохранять данные в поле класса.</p>
8 <p>Для реализации этих маршрутов нужна возможность хранить добавленные данные. В будущих уроках мы научимся делать это через базу данных, а сейчас просто будем сохранять данные в поле класса.</p>
9 <p>Взаимодействие с этим приложением с использованием консольной утилиты http для выполнения запросов выглядит так:</p>
9 <p>Взаимодействие с этим приложением с использованием консольной утилиты http для выполнения запросов выглядит так:</p>
10 <p>Наше приложение возвращает данные в JSON-формате, причем в зависимости от ситуации:</p>
10 <p>Наше приложение возвращает данные в JSON-формате, причем в зависимости от ситуации:</p>
11 <ul><li>Приложение возвращает объект, если мы работаем с одиночным ресурсом - например, созданием</li>
11 <ul><li>Приложение возвращает объект, если мы работаем с одиночным ресурсом - например, созданием</li>
12 <li>Приложение возвращает массив, если мы работаем с коллекцией - например, списком</li>
12 <li>Приложение возвращает массив, если мы работаем с коллекцией - например, списком</li>
13 </ul><p>Подробнее на эту тему мы поговорим в уроке про REST API.</p>
13 </ul><p>Подробнее на эту тему мы поговорим в уроке про REST API.</p>
14 <p>Рассмотрим пример кода такого приложения:</p>
14 <p>Рассмотрим пример кода такого приложения:</p>
15 <p>Сначала разберем общие концепции, а затем поговорим о каждом обработчике в отдельности.</p>
15 <p>Сначала разберем общие концепции, а затем поговорим о каждом обработчике в отдельности.</p>
16 <p>Над классом приложения добавлена аннотация @RestController. Эта аннотация указывает на классы, которые содержат обработчики маршрутов. Пока мы помещаем обработчики в том же классе, в котором стартует приложение - так проще. В будущем мы будем размещать обработчики в контроллерах.</p>
16 <p>Над классом приложения добавлена аннотация @RestController. Эта аннотация указывает на классы, которые содержат обработчики маршрутов. Пока мы помещаем обработчики в том же классе, в котором стартует приложение - так проще. В будущем мы будем размещать обработчики в контроллерах.</p>
17 <p>Каждый обработчик помечен аннотацией описания маршрута. Эти аннотации отвечают за то, какой маршрут обрабатывает обработчик и какой метод HTTP при этом используется. Это не одна аннотация, а набор аннотаций под каждый HTTP-метод - @GetMapping(), @DeleteMapping и так далее.</p>
17 <p>Каждый обработчик помечен аннотацией описания маршрута. Эти аннотации отвечают за то, какой маршрут обрабатывает обработчик и какой метод HTTP при этом используется. Это не одна аннотация, а набор аннотаций под каждый HTTP-метод - @GetMapping(), @DeleteMapping и так далее.</p>
18 <p>Каждый обработчик возвращает либо коллекцию объектов, либо объект, либо ничего не возвращает. А как Java-сущности превращаются в JSON? Это происходит автоматически с помощью библиотеки Jackson. Она входит в пакет<em>spring-boot-starter-json</em>, который подключается через<em>spring-boot-starter-web</em>.</p>
18 <p>Каждый обработчик возвращает либо коллекцию объектов, либо объект, либо ничего не возвращает. А как Java-сущности превращаются в JSON? Это происходит автоматически с помощью библиотеки Jackson. Она входит в пакет<em>spring-boot-starter-json</em>, который подключается через<em>spring-boot-starter-web</em>.</p>
19 <p>Автоматическая конвертация - это очень удобная штука, которая убирает шаблонный код.</p>
19 <p>Автоматическая конвертация - это очень удобная штука, которая убирает шаблонный код.</p>
20 <p>При работе со Spring Boot имена обработчиков не принципиальны. Намного важнее, какой маршрут они обрабатывают. Здесь мы используем подход к именованию, принятый в большинстве веб-фреймворках.</p>
20 <p>При работе со Spring Boot имена обработчиков не принципиальны. Намного важнее, какой маршрут они обрабатывают. Здесь мы используем подход к именованию, принятый в большинстве веб-фреймворках.</p>
21 <h2>Добавление страницы</h2>
21 <h2>Добавление страницы</h2>
22 <p>Обработчик добавления страницы принимает на вход данные страницы, добавляет их в коллекцию страниц и возвращает эти же данные наружу:</p>
22 <p>Обработчик добавления страницы принимает на вход данные страницы, добавляет их в коллекцию страниц и возвращает эти же данные наружу:</p>
23 <p>Обсудим, как данные из HTTP-тела попадают в код. Это происходит с помощью аннотации @RequestBody, которой помечается переменная с типом Page. Имя переменной не важно. Spring Boot автоматически создает объект этого типа и заполняет его данными, которые были в теле. Для работы этого механизма имена полей в Page должны совпадать с именами в теле запроса. При этом данных может быть отправлено меньше, чем содержится в Page.</p>
23 <p>Обсудим, как данные из HTTP-тела попадают в код. Это происходит с помощью аннотации @RequestBody, которой помечается переменная с типом Page. Имя переменной не важно. Spring Boot автоматически создает объект этого типа и заполняет его данными, которые были в теле. Для работы этого механизма имена полей в Page должны совпадать с именами в теле запроса. При этом данных может быть отправлено меньше, чем содержится в Page.</p>
24 <h2>Показ страницы</h2>
24 <h2>Показ страницы</h2>
25 <p>Обработчик вывода страницы принимает на вход идентификатор, в качестве которого выступает слаг страницы. Затем обработчик ищет эту страницу в списке страниц и возвращает наружу. Страница может не найтись, поэтому возвращается Optional:</p>
25 <p>Обработчик вывода страницы принимает на вход идентификатор, в качестве которого выступает слаг страницы. Затем обработчик ищет эту страницу в списке страниц и возвращает наружу. Страница может не найтись, поэтому возвращается Optional:</p>
26 <p>Здесь мы видим работу с параметрами пути в Spring Boot. В маршруте они указываются через фигурные скобки {id}, а в параметрах метода - извлекаются с помощью аннотации @PathVariable. Имя переменной должно совпадать с именем переменной в шаблоне, потому что происходит сопоставление.</p>
26 <p>Здесь мы видим работу с параметрами пути в Spring Boot. В маршруте они указываются через фигурные скобки {id}, а в параметрах метода - извлекаются с помощью аннотации @PathVariable. Имя переменной должно совпадать с именем переменной в шаблоне, потому что происходит сопоставление.</p>
27 <h2>Удаление страницы</h2>
27 <h2>Удаление страницы</h2>
28 <p>Удаление страницы просто удаляет объект из списка страниц. Сам метод ничего не возвращает:</p>
28 <p>Удаление страницы просто удаляет объект из списка страниц. Сам метод ничего не возвращает:</p>
29 <h2>Список страниц</h2>
29 <h2>Список страниц</h2>
30 <p>Вывод списка возвращает список страниц с возможностью указать лимит по выводу:</p>
30 <p>Вывод списка возвращает список страниц с возможностью указать лимит по выводу:</p>
31 <p>Лимит определяется параметром запроса localhost:8080/pages?limit=1. Параметры запроса попадают в код через параметры обработчика, помеченные аннотацией @RequestParam. У параметра можно задать значение по умолчанию в аннотации с помощью defaultValue. Имя переменной сопоставляется с именем параметра запроса.</p>
31 <p>Лимит определяется параметром запроса localhost:8080/pages?limit=1. Параметры запроса попадают в код через параметры обработчика, помеченные аннотацией @RequestParam. У параметра можно задать значение по умолчанию в аннотации с помощью defaultValue. Имя переменной сопоставляется с именем параметра запроса.</p>
32 <h2>Обновление страницы</h2>
32 <h2>Обновление страницы</h2>
33 <p>Обновление устроено сложнее всего. Этот обработчик принимает на вход идентификатор обновляемой страницы и новые данные. Затем выполняется поиск необходимой страницы в общем списке. Если страница найдена, то выполняется ее обновление:</p>
33 <p>Обновление устроено сложнее всего. Этот обработчик принимает на вход идентификатор обновляемой страницы и новые данные. Затем выполняется поиск необходимой страницы в общем списке. Если страница найдена, то выполняется ее обновление:</p>
34 <p>Здесь используются уже знакомые нам концепции, но они появляются вместе. Параметры в обработчиках могут идти в любом порядке, с любыми именами и допустимыми аннотациями. Технически вызов таких методов работает через механизм Reflection, который позволяет определить сигнатуру метода и правильно вызывать его в рантайме.</p>
34 <p>Здесь используются уже знакомые нам концепции, но они появляются вместе. Параметры в обработчиках могут идти в любом порядке, с любыми именами и допустимыми аннотациями. Технически вызов таких методов работает через механизм Reflection, который позволяет определить сигнатуру метода и правильно вызывать его в рантайме.</p>