Какие аннотации могут использоваться на стороне сервера в клиент серверном spring приложении
Сегодняшняя статья рассмотрит основные вопросы про REST в Spring. Она будет особенно полезна для начинающих программистов.
Официальный гид от Pivotal, в котором написано про темы для подготовки.
Spring REST — это часть Spring MVC. Поэтому многое из Spring MVC будет применяться в REST и наоборот. Для более подробного ознакомления со Spring MVC можно прочитать эту статью.
Чтобы понять концепцию REST, нужно разобрать акроним на его составляющие:
- Representational — ресурсы в REST могут быть представлены в любой форме — JSON, XML, текст, или даже HTML — зависит от того, какие данные больше подходят потребителю
- State — при работе с REST вы должны быть сконцентрированы на состоянии ресурса, а не на действиях с ресурсом
- Transfer — REST включает себя передачу ресурсных данных, в любой представленной форме, от одного приложения другому.
REST это передача состояний ресурса между сервером и клиентом.
Ресурс в REST — это все, что может быть передано между клиентом и сервером.
Вот несколько примеров ресурсов:
- Новость
- Температура в Санкт-Петербурге в понедельник в 4 утра
- Зарплата сотрудника
- Выборка из базы данных
- Результат поиска
Самые часто-используемые обозначаются аббревиатурой CRUD:
- Create — POST
- Read — GET
- Update — PUT
- Delete — DELETE
По умолчанию REST не защищен.
Вы можете настроить безопасность с помощью Basic Auth, JWT, OAuth2
Это операции, которые не модифицируют ресурсы. Вот их список:
Что такое идемпотентая операция? Почему идемпотентность важна?Идемпотентые методы — это методы, при каждом вызове которых результат будет одинаковый.
То есть, результат после 1 вызова такого метода будет такой же, как и результат после 10 вызовов этого метода.
Это важно для отказоустойчевого API. Предположим, что клиент хочет обновить ресурс с помощью POST-запроса? Если POST не идемпотентный метод, то при многократном вызове возникнут непредвиденные обновления ресурса. Используя идемпотентные методы, вы ограждаете себя от многих ошибок.
Да. REST хорошо масштабируется потому что он не хранит состояние.
Это значит что он не хранит информацию о пользовательских сессиях на сервере.
Информация о клиенте не должна хранится на стороне сервера, а должна передаваться каждый раз туда, где она нужна. Вот что значит ST в REST, State Transfer. Вы передаете состояние, а не храните его на сервере.
REST также интероперабельный — это значит, что на нем могут взаимодействовать разные программы написанные на разных языках. Это исходит из 2ух факторов:
Spring имеет несколько реализаций этого интерфейса, а вы можете создать свою.
В этом случае DispatcherServlet не использует Model и View.
@RestController ставится на класс-контроллер вместо @Controller . Она указывает, что этот класс оперирует не моделями, а данными. Она состоит из аннотаций @Controller и @RequestBody .
Аннотация @ResponseBody ставится на методы, которые работают с данными, а не с моделями. Ее не требуется указывать явно, если используется @RestController .
Теперь она используется только для указания URI до класса-контроллера.
Что за аннотации @GetMapping, @PostMapping, @DeleteMapping и прочие?- @GetMapping — Обрабатывает get-запросы
- @PostMapping — Обрабатывает post-запросы
- @DeleteMapping — Обрабатывает delete-запросы
- @PutMapping — Обрабатывает put-запросы
- @PatchMapping — Обрабатывает patch-запросы
Все написанное ниже характерно также и для других аннотаций.
Аннотация @GetMapping — это просто аннотация которая содержит @RequestMapping(method = RequestMethod.GET).
Она также позволяет более глубоко настроить метод-обработчик.
Ее параметры(они конвертируются в аналогичные параметры @RequestMapping):
- path — URI
- headers — заголовки
- name — имя обработчика
- params — параметры
- produces — тип возвращаемых данных(JSON, XML, текст). Используется в REST
consumes — тип принимаемых данных. Используется в REST
По умолчанию аннотация принимает путь до метода.
@GetMapping("managers") = @GetMapping(path = "managers")
Эта аннотация получает определенную часть из URI.
Следующий код поместит в переменную id значение 23 .
POST — 200 OK, 201 Created, 204 No Content
PUT — 200 OK, 201 Created, 204 No Content
DELETE — 204 No Content, 202 Accepted
Она позволяет устанавливать код ответа. Обычно Spring сам устанавливает нужный код ответа, но бывают моменты, когда это нужно переопределить.
Вместо использования аннотации можно возвращать ResponseEntity и вручную устанавливать код ответа.
Не рекомендуется использовать ResponseEntity и @ReponseStatus вместе.
Он является универсальным типом, и можно использовать любой объект в качестве тела:
Вы можете использовать аннотацию @RequestBody на параметре метода, для того чтобы тело запроса конвертировалось в этот параметр.
Вы можете использовать @Validated вместе с @RequestBody , для проверки пришедшего запроса.
Что такое RestTemplate? Какие у него преимущества?Среди растущего числа распределенных облачных сервисов, где сервера постоянно появляются и исчезают, конечные точки сервисов оказываются динамичны и их сложно знать наперед. По этой причине, прежде чем отправлять запрос сервису, REST-клиенту необходимо интегрироваться в реестр сервисов и находить там его конечную точку.
Помимо этого, нужно обработать сериализацию запрос-ответ, а также реализовать балансировку нагрузки, правильно распределив ее между серверами. Плюсом же к этому нужно организовать систему отказоустойчивости. Все перечисленное представляет собой сквозную функциональность распределенных систем.
В текущей статье вы узнаете:
Создание Maven-проекта
Для генерации Maven-проекта со Spring Boot 2.x можно использовать Spring Initializr. Добавьте в этот проект зависимости Spring Web, OpenFeign и Ribbon.
Если вы начинаете Maven-проект с нуля, то импортируйте Spring Cloud Dependencies POM, чтобы он наследовал все версии артефактов семейства Spring CLoud.
Далее добавьте к зависимостям проекта модули Spring Boot Starter Web, Spring Cloud Starter OpenFeign и Spring Cloud Starter Netflix Ribbon.
В качестве альтернативы можете загрузить весь проект с GitHub.
Создание класса Application для запуска
@SpringBootApplication активирует в приложении сканирование компонентов и автоматическую настройку. Аннотацию @EnableFeignClients мы добавляем, чтобы включить сканирование компонентов для интерфейсов, аннотированных с @FeignClient .
Создание интерфейса REST-клиента
Создайте интерфейс PostmanEchoClient , добавьте к нему аннотацию @FeinClient и назовите ее postman-echo . Spring автоматически просканирует наш интерфейс и создаст реализацию для REST-клиента в среде выполнения.
Spring использует имя postman-echo , сопровождаемое аннотацией @FeignClient в качестве идентификатора, чтобы создать RibbonClient для клиентского балансировщика нагрузки.
Есть несколько способов предоставить RibbonClient конечную точку сервера для балансировки нагрузки, например использовать настройки Java, свойства приложения Spring или интеграцию с Eureka Server для поиска этой конечной точки.
Чтобы перейти от жестко закодированного URL конечной точки сервера к решению с балансировкой нагрузки, давайте настроим Ribbon со статическим listOfServers . В разделе src/main/resources файла application.yaml добавьте следующие свойства:
Postman Echo — это сервис, который можно использовать для тестирования REST-клиентов и совершения пробных вызовов API. Он предоставляет конечные точки для GET , POST и PUT с различными механизмами аутентификации.
Добавление клиентского метода GET-запроса
Далее добавьте в интерфейсе клиента Postman Echo клиентский метод getEcho , который принимает параметры запроса String foo и String bar , возвращая объект EchoGetResponse . Прикрепите к этому GET-запросу path / get при помощи аннотации GetMapping . При вызове этот метод будет вызывать конечную точку GET-запроса Postman Echo.
Конечная точка GET-запроса Postman Echo будет повторять все переданные параметры запроса в теле ответа в элементе args . Документация по GET-запросу Postman Echo лежит здесь.
Создайте новый класс EchoGetResponse , который будет представлять ответ JSON. EchoGetResponse — это класс простого Java-объекта (POJO). Ответ JSON будет десериализован в нашем методе GET-запроса Postman Echo как EchoGetResponse .
Тестирование метода GET-запроса с помощью ‘SpringBootTest’
Мы тестируем вызов клиентского метода GET-запроса к удаленной конечной точке с помощью SpringBootTest . Для этого нужно создать класс PostmanEchoClientTests . Установите для теста случайный порт с помощью аннотации @SpringBootTest .
Выполните автоматическое внедрение (autowire) созданного Spring bean-компонента PostmanEchoClient в класс модульного теста. Bean-компонент должен использовать настроенный список серверов Ribbon для балансировки нагрузки на клиентской стороне.
Создайте метод getEcho с аннотацией @Test , использующий внедренный bean-компонент PostmanEchoClient для вызова конечной точки GET-запроса Postman Echo.
После вызова конечной точки проверьте, совпадает ли возвращаемый ответ EchoGetResponse с переданными аргументами запроса, убедившись, что он правильно десериализован из ожидаемого содержимого JSON.
Добавление клиентского метода POST-запроса
Теперь, протестировав GET, давайте перейдем к запросу POST. Добавьте в интерфейс PostmanEchoClient клиентский метод postEcho , принимающий параметры запроса String foo и String bar . У него должно быть тело запроса объекта EchoPostRequest , а возвращаться им должен объект EchoPostResponse .
Добавьте к этому POST-запросу path / post при помощи аннотации @PostMapping . При вызове этот клиентский метод будет вызывать конечную точку POST-запроса Postman Echo.
Конечная точка будет повторять переданные параметры и body запроса в виде ответа в элементах args и data .
Создайте другой класс EchoPostResponse , который будет представлять ответ JSON. Этот класс содержит args со свойствами foo и bar , а также data со свойством message . Ответ JSON будет десериализован как EchoPostResponse в нашем клиентском методе Post-запроса Postman Echo.
Тестирование метода POST-запроса с помощью ‘SpringBootTest’
Снова включаем метод теста postEcho в ранее добавленный класс PostmanEchoClientTests для только что добавленного клиентского метода postEcho . Как и ранее используйте для вызова конечной точки POST-запроса Postman Echo автоматическое внедрение PostmanEchoClient .
После вызова удаленной точки убедитесь, что возвращаемый ответ EchoPostResponse совпадает с переданными аргументами и телом запроса, подтверждающими его верную десериализацию из ожидаемого формата JSON.
Интеграция с Eureka Server
С помощью простой аннотации и некоторой настройки можно быстро активировать в сервисе Spring REST клиента Netflix Eureka Discovery.
Далее добавьте в класс application аннотацию @EnableDiscoveryClient , чтобы активировать реализацию клиента Netflix Eureka Discovery. Затем он зарегистрируется в реестре сервисов Netflix Eureka Server и будет использовать абстракцию Spring Cloud DiscoveryClient для запроса метаданных, содержащих конечные точки сервисов, которые клиент Ribbon будет использовать для балансировки нагрузки.
И наконец, укажите имя приложения и URL конечной точки Eureka Server в application.yaml .
Клиент Eureka использует имя приложения для регистрации на Eureka Server. Если это имя не указать, ваш сервис будет отображаться на Eureka Server как неизвестный.
Проделав эти несколько шагов, вы интегрировали сервис Spring REST в Eureka Server.
Теперь, когда ваш REST-сервис готов к подключению к Eureka Server, нужно удалить свойство listOfServers для Ribbon из application.yaml или закомментировать его.
Создание Eureka Server
Создайте с помощью Spring Initializr другой проект — на этот раз для Eureka Server, добавив в него зависимость Eureka Server.
Если вы начинаете новый Maven-проект, импортируйте Spring Cloud Dependencies POM и добавьте зависимость Spring Cloud Starter Netflix Eureka Server.
В качестве альтернативы можно загрузить проект Eureka Server с GitHub.
Вам нужно создать стандартный класс точки входа с аннотацией @SpringBootApplication . Для активации реализации Eureka Server также добавьте в него аннотацию @EnableEurekaServer .
Далее измените порт сервера на 8761 .
Наконец, деактивируйте в application.yaml функции саморегистрации и запроса реестра.
Теперь Eureka Server готов, можете запускать!
Поскольку Postman Echo является внешним сервисом, нужно вручную зарегистрировать его на Eureka Server. Список REST-операций, поддерживаемых Eureka, можно найти здесь. Этот API понадобится вам только для регистрации нового приложения.
Отправьте с помощью Postman запрос на Eureka Server.
URL для регистрации Postman Echo на Eureka Server следующий:
Запросите содержимое для регистрации Postman Echo на Eureka Server так:
Нужно зарегистрировать приложение как POSTMAN-ECHO , потому что ранее мы назвали @FeignClient как postman-echo .
В результате вы должны получить успешный ответ без содержимого.
Откройте панель Eureka, где должна отразиться регистрация POSTMAN-ECHO .
Еще раз запустите PostmanRestClientTests , чтобы убедиться, что все работает как надо.
Заключение
Сегодня я покажу Вам как создать java веб приложение используя возможности фреймворка Spring Boot. Будем писать и бекенд и фронтенд с нуля. Для тех, кто хотел начать изучать Spring, данная статья может послужить очень хорошим началом. В ней я даю ссылки на другие статьи где определенные технологии описаны более детально.
Тем, кто уже знаком со Spring, статья поможет собрать все знания воедино, закрепить их, и написать простое приложение в копилку к портфолио.
Для тех, кому лень читать: видео в конце страницы; код в описании к видео.
Технологии, которые мы будем использовать:
Создание простого Spring Boot приложения
Дальше нужно открыть проект в любом удобном java редакторе. В этой статье я использую intellij idea. Как я пишу в каждой статье: выбирайте такой редактор, который удобен именно Вам.
После вышеперечисленных шагов у Вас должен быть открыт проект с одним единственным классом SpringCrudApplication. Название этого класса спринг берет частично с названия самого проекта. Поэтому у Вас он может иметь другое название. Это не принципиально.
Структура Spring Boot приложения
Данный класс имеет main метод, который является точкой входа в приложение. Аннотация SpringBootApplication указывает что приложение будет запускаться как Spring Boot приложение. Она также указывает спрингу, что для всего что у нас есть в файле зависимостей pom.xml нужно подключить авто конфигурацию. Если Вам нужно кастомизировать конфигурацию под определенные зависимости, то автоконфигурацию можно легко отключить, указав в списке exclude внутри аннотации SpringBootApplication:
Все настройки приложение по умолчанию берет из файла application.properties. Чтобы указать спринг как ему получить доступ к базе данных, нужно добавить в данный файл путь к базе данных, имя пользователя и пароль с такими ключами:
Spring Boot возьмет вышеуказанные данные и на их основании сделает пул соединений (стандартный).
Теперь, приложение должно запускаться. По умолчанию оно использует порт 8080, который тоже можно поменять указав нужный в конфигурационном файле (application.properties).
Я создал простую страницу с приветствием:
Дальше, когда все приготовления с базой готовы, нужно написать слой доступа к базе данных. Создаем сперва класс отображения сущности. Если у нас таблица users_table нужно создать класс который будет ее отображением в java. Сначала создадим пакет entity, в который и поместим наш класс сущность. Назовем его Users. В данном классе создадим поля, которые есть в базе и навешаем на наш класс и поля аннотации java persistence: Table, Entity, Column, Id. Если для Вас эти термины новы, советую почитать статью по хибернейт и персистенс. Чтобы избежать написание геттеров, сеттеров, equals, hashCode, toString, я воспользуюсь аннотациями библиотеки Lombok.
В результате всех манипуляций, мой класс Users имеет вид:
Дальше на очереди JPA репозиторий. Подробнее о Spring Data JPA я писал раньше. Теперь, пришло время все это соединить в один проект.
Создадим пакет repository в который поместим интерфейс UsersRepository. Унаследуем этот интерфейс от JpaRepository указав в треугольных скобках нашу сущность с которой будет работать репозиторий и тип первичного ключа (Users, Integer). Предлагаю сразу добавить в репозиторий метод findByLogin. Чтобы можно было искать пользователя по логину. Напоминаю, что CRUD методы по работе с сущностью уже встроенны в JpaRepository и писать их самому не нужно. Такие методы как save(), saveAll(), delete(), findById() готовы к использованию. Использовать их мы будем в слое сервиса. Наш репозиторий выглядит следующим образом:
После работы с jdbc этот код выглядит как магия.
Именно в этот класс мы будем превращать нашу сущность юзер когда достанем данные из базы. Также этот класс будет нам служить трансфером между клиентом, контроллером и сервисом.
На очереди слой сервисов. Сначала создаем пакет service. В него добавляем интерфейс UsersService. Далее в интерфейсе добавляем методы которые нам нужны для работы:
В данном примере проверим чтобы объект UsersDto не был пустым и чтобы поле логин было заполнено. В идеале же еще нужно проверять чтобы емейл был в правильном формате. Для учебных целей я упустил все эти моменты, но, на реальных проектах, Вы должны стараться провалидировать входные данные по максимум перед тем как сохранять их в базу.
Я вынес валидацию в отдельный метод, чтобы было легче читать код:
После того как UsersDto пройдет валидацию, переконвертируем его в Users:
Для того, чтобы не писать весь код в одном классе я вынесу конвертацию из класса сервиса в другой класс. Назову его UsersConverter. В этот класс я и помещу метод fromUserDtoToUser. Предлагаю сразу написать конвертацию из UsersDto в Users. И, для демонстрации Вам еще одной фишки библиотеки Lombok я воспользусь встроенным в ломбок паттерном билдер. Для начала я добавлю в класс UsersDto аннотацию @Builder. Она реализовывает в данном классе паттерн билдер. Теперь я могу создавать объекты UsersDto через builder. Это достаточно популярный паттерн проектирования. Советую ознакомиться с ним.
Мой метод по конвертации из Users в UsersDto будет выглядеть так:
Если не нравиться паттерн builder можно делать по аналогии с методом fromUserDtoToUser.
Для того, чтобы спринг создал бин моего класса UsersConverter я навешу на него аннотацию @Component. Это позволит мне использовать внедрение зависимостей (dependency injection) и подлючить этот класс в мой класс DefaultUsersService используя Spring Dependency Injection.
Теперь, вернемся в наш класс DefaultUsersService и подключим к нему UsersRepository и UsersConverter. Я будут использовать внедрение зависимостей через конструктор. А Lombok поможет мне с конструктором. Для начала я объявляю переменные классов:
Мой компилятор ругается что нужно проинициализировать данные переменные используя конструктор. И правда: ведь мои переменные имеют final модификатор, что говорит о том что они должны быть проинициализированы. Компилятор предлагает инициализировать их в констукторе. Так я и сделаю. Только чтобы не писать его руками я воспользуюсь аннотацией @AllArgsConstructor из библиотеки ломбок. Заодно и навешаю на свой класс аннотацию @Service чтобы потом внедрить его в контроллер.
Дальше приступаю за оформление saveUser:
- вызываю validateUserDto для валидации входящих данных;
- конвертирую в юзера Users convertedUser = usersConverter.fromUserDtoToUser(usersDto);
- сохраняю в базу данных Users savedUser = usersRepository.save(convertedUser);
- конвертирую сохраненного юзера обратно в дто и возвращаю return usersConverter.fromUserToUserDto(savedUser);
В методе deleteUser я просто удаляю пользователя по айди:
Метод findAll похож на findByLogin с той лишь разницей что мы винимаем всех пользователей из базы и потом конвертируем их в список дто. Для обхода списка и конвертации я воспользовался стримами java. Но Вы может пользоваться простым циклом если сложно по началу разобраться со stream.
Наш класс DefaultUsersService после всех преобразований имеет такой вид:
На очереди слой контроллер. Создадим новый пакет controller в который поместим новый класс UsersController. Данный класс будет отвечать за обработку запросов по работе с сущностью User. Чтобы вызывать методы по созданию, удалению и выборке пользователей подключим в наш контроллер только что созданный UsersService таким же образом как мы подключали репозиторий и конвертор компонент: внедрение зависимостей через конструктор.
Чтобы наш класс стал контроллером простого названия мало. Нужно навешать на него аннотацию @RestController которая укажет спринг что данный класс предоставляет REST API.
В контроллере не должно содержаться никакой логики по работе с сущностями. Вся логика сосредоточена в сервисном слое. Поэтому, в контроллере мы просто вызываем методы из сервиса и перебрасываем ответы на клиент:
На этом разработка со стороны бекенда закончена. Нужно еще добавить юнит и интеграционных тестов но это уже тема другой статьи. Чтобы проверить работу приложения можно воспользоваться инструментом Postman. Он позволяет делать запросы на сервер и получать ответ.
Тестирование API используя Postman
Я просмотрел постманом все урлы моего приложения и убедился что они работают. На скрине выше пример запроса:
- выбираем метод запроса (POST);
- выбираем тело метода, тело метода будет иметь формат JSON;
- отправляем запрос и смотрим ответ.
Мне пришел ответ с моим сохраненным пользователем. У него появился айди, а это значит что он записался в базу данных и ему присвоился идентификатор. Для уверенности захожу в базу и проверяю все ли в порядке:
Сохраненный пользователь в базе данных
Теперь, когда я проверил что мой код работает так как я и ожидал, можно приступить к фронтенду.
Дальше нужно сделать запрос на сервер используя технологию AJAX (Asynchronous JavaScript and XML). Данная технология позволяет делать запросы без перезагрузки веб страницы. Это значительно приятней пользователям и смотрится современней.
Есть множество реализация AJAX на разных фреймворках. Но в данной статье мы разберем пример на Javascript не используя сторонние библиотеки и фреймворки.
После того как метод open вызван, нужно вызвать send метод, который может принимать тело запроса. Например наш запрос на создание пользователя будет выглядеть следующим образом:
После того как мы получим положительный ответ от нашего сервера (200 статус), то сможем отобразить полученные данные так как мы пожелаем.
Получилась не пара строк После цикла мы берем элемент у которого айди usersList и помещаем в него нашу сформированую таблицу. Только не забудьте в коде добавить сам элемент таблицы с нужным айди:
Чтобы функция loadUsers отработала ее нужно запустить. Просто вызовите ее после всех функций loadUsers();
Дальше добавим логику для создания нового пользователя. Для этого нам нужна простая html форма:
При нажатии на кнопку Create user будет работать функция createUser, которую мы еще должны сооздать:
Берем значения полей формы, формируем запрос JSON, делаем запрос на сервер. После этого вызываем функцию loadUsers чтобы наш вновь созданный пользователь отобразился на странице.
Подобно до сохранения делаем удаление пользователя. И также как и загрузка всех пользователей делаем поиск по логину.
Вот такой вот финальный файл получился после всех манипуляций:
Я советую вынести Javascript и CSS в отдельные файлы, чтобы Ваш код приобрел читабельный вид.
Наше простое веб приложение на Spring Boot готово.
Результат работы приложения
Статья получилась довольно обширной и местами сложной. В ней использовано очень много технологий которые сейчас популярны среди java разработчиков. Берете ее за основу, добавляете немного логики, больше функционала и помимо знаний, на выходе получаете отличный проект для своего портфолио.
Сейчас открыт набор на java mentor где мы по похожему сценарию только под моим руководством пишем немного усложненные варианты подобных проектов.
В дополнение к статье есть еще видео, где можно посмотреть разработку. В нем есть еще пункт с написанием юнит тестов с помощью mockito. Но этот момент я оставил для следующих статтей.
Для одного Spring Boot приложения нормально хранить настройки в локальном application.properties. Но если приложений несколько (микросервисов), и они используют общие настройки, то неплохо бы их вынести в одно общее место. И это можно сделать.
Благодаря Configuration Server настройки можно хранить в Git-репозитории. И к тому же менять их прямо во время работы микросервисов без их перезапуска.
Введение
Продолжим совершенствовать пример с двумя микросервисами Zoo и Random Animal. Мы уже внедрили Eureka, API Gateway. А теперь вынесем конфигурацию на GitHub.
Создание Spring Cloud Config Server
Создадим новое Spring Boot приложение и добавим в него зависимость Config Server:
Central management for configuration via Git, SVN, or HashiCorp Vault.У нас будет Git.
Аннотация главного класса
Необходимо также аннотировать главный класс приложения аннотацией @EnableConfigServer:
Указание пути до репозитория Git
Репозиторий можно создать на локальном ПК или на GitHub. У нас будет репозиторий на GitHub:
В нем лежит файл application.properties с содержимым:
Чтобы Configuration Server знал, откуда брать настройки, путь до репозитория с этим файлом надо указать в файле настроек нашего Configuration Server:
Запуск Configuration Server
Теперь можно запустить приложение и убедиться, что настройки берутся из репозитория. Только еще укажем стандартный для Configuration Server порт 8888 (8080 будет занят другим микросервисом):
Запустим приложение и откроем в браузере страницу:
На экран будет выведена настройка greeting, взятая с GitHub:
Настройки корректно извлекаются
Итак, Configuration Server подтягивает настройки и выдает их правильно. Теперь сделаем там, чтобы и другие микросервисы могли использовать эти настройки. Сделаем их клиентами сервера конфигурации.
Создание Spring Cloud Config Client
Чтобы сделать микросервис Zoo клиентом, добавим в него зависимость:
Клиент должен знать, где находится сервер, с которого ему брать настройки. Так что зададим адрес сервера в файле application.properties клиента (микросервиса Zoo):
И с конца 2020, чтобы все заработало, нужно еще добавить в клиент зависимость :
Теперь Zoo будет брать настройки с Configuration Server. Чтобы в этом убедиться, добавим в локальный файл application.properties микросервиса Zoo аналогичное свойство greeting, но с другим значением (не тем, что в файле application.properties на GitHub):
И исправим контроллер: добавим вывод свойства greeting рядом с именем животного:
Теперь все запустим и убедимся, что свойство берется из удаленного репозитория, а не из локального файла application.properties:
Основное сделано, но рассмотрим еще один момент.
Разные значения настройки для разных микросервисов
Мы вынесли настройки для всех микросервисов в общий репозиторий. Но что, если разным микросервисам требуются разные значения одного свойства (например, порта)? Такую настройку тоже можно вынести в репозиторий, но только положить в отдельный файл с именем конкретного микросервиса.
Например, настройки для микросервиса Zoo нужно положить в zoo.properties.
И отправим новый файл в репозиторий.
Теперь приветствие берется из нового файла:
Изменение настроек: обновление на Config-сервере и на клиенте
С остальными микросервисами ситуация другая. Они созданы для других задач и не обязаны постоянно опрашивать сервер на предмет изменений. Чтобы настройка отобразилась на микросервисе-клиенте, его надо перезапустить.
Но можно это сделать и без перезапуска, как объявлялось в самом начале статьи. Для этого придумана конечная точка в Spring Actuator. Когда мы хотим, чтоб клиент обновил свои настройки, мы делаем POST-запрос на эту точку, и клиент обновляет настройки.
Добавление Spring Actuator
Открыть точку /actuator/refresh
Важный момент: надо открыть конечную точку refresh, чтобы на нее можно было делать запросы. Для этого создадим файл bootstrap.properties и пропишем в нем:
Аннотация @RefreshScope
Но это не все: нужно аннотировать тот класс, чьи настройки мы хотим обновлять по POST-запросу. Добавим аннотацию @RefreshScope в ZooController:
POST-запрос на точку /actuator/refresh
И сделаем POST-запрос. Zoo запущен на порту 8081, так что запрос делаем по адресу:
Запрос сделаем с помощью Postman:
В ответе есть обновленное свойство. Теперь обновим страницу клиента:
Как видите, с помощью Post-запроса мы уведомили клиент, что пора обновить настройки. И он их обновил.
Читайте также: