В эпоху микросервисов приходится все чаще и чаще писать интеграции для их взаимодействия как между собой, так и со сторонними системами. Кто-то создаёт отдельные библиотеки с интеграцией и переиспользует их в нескольких микросервисах, кто-то захламляет проект огромным количеством POJO классов, некоторые же создают один POJO класс с множеством вложенных классов. В этой статье я хотел бы поделиться подходом, используя который вы сможете спрятать большую часть кода, которая мешает чтению и пониманию проекта.
Для примера мы будем использовать интеграцию с API GitHub и привычные Spring аннотации для описания нашего клиента.
Подключаем зависимости
Добавим feign-spring4 для поддержки генерации клиента на основе spring аннотаций.
<dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-spring4</artifactId> <version>${feign.version}</version> </dependency>
Добавляем feign-jackson для поддержки аннотаций, которые будут в сгенерированных POJO классах.
<dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-jackson</artifactId> <version>${feign.version}</version> </dependency>
Добавляем плагин для генерации POJO классов
В секцию plugins добавляем конфигурацию для jsonschema2pojo-maven-plugin. Указываем, где у нас хранятся json схемы, какое название пакета будет использоваться в сгенерированных классах и отключаем добавление дополнительных полей, чтобы игнорировать те, которые мы не используем.
<build> <plugins> <plugin> <groupId>org.jsonschema2pojo</groupId> <artifactId>jsonschema2pojo-maven-plugin</artifactId> <version>1.2.1</version> <configuration> <sourceDirectory>${basedir}/src/main/resources/schema</sourceDirectory> <targetPackage>com.example.types</targetPackage> <includeAdditionalProperties>false</includeAdditionalProperties> </configuration> <executions> <execution> <goals> <goal>generate</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
Опишем json схему, по которой будут генерироваться наши POJO классы. Для того, чтобы генератор не пропустил классы из definitions, нужно добавить ссылки на них в properties.
{ "$schema": "http://json-schema.org/draft-07/schema#", "description" : "Definition of GitHubApi", "type": "object", "properties" : { "contributor" : { "type" : "object", "$ref" : "#/definitions/Contributor" }, "issue" : { "type" : "object", "$ref" : "#/definitions/Issue" } }, "definitions": { "Contributor" : { "type" : "object", "properties": { "login": { "type": "string" }, "contributions": { "type": "integer" } } }, "Issue" : { "type" : "object", "properties": { "title": { "type": "string" }, "body": { "type": "string" }, "labels": { "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string" } } } }, "user": { "type" : "object", "$ref" : "#/definitions/User" } } }, "User" : { "type" : "object", "properties": { "login": { "type": "string" }, "avatar_url": { "type": "string" } } } } }
Для упрощения создания схемы можно воспользоваться онлайн генератором схемы из json.
Переходим к описанию интеграции
Создадим простой интерфейс для описания конечных URI для получения данных.
interface GitHubApi { @GetMapping("/repos/{owner}/{repo}/contributors") List<Contributor> contributors(@PathVariable("owner") String owner, @PathVariable("repo") String repo); @GetMapping("/repos/{owner}/{repo}/issues") List<Issue> issues(@PathVariable("owner") String owner, @PathVariable("repo") String repo); }
Создаем клиента для обращения к GitHub. Выбираем JacksonDecoder для поддержки аннотаций в POJO классах (таких как @JsonProperty). Добавляем SpringContract для использования spring аннотаций (@GetMapping, @PathVariable).
GitHubApi github = Feign.builder() .decoder(new JacksonDecoder()) .contract(new SpringContract()) .target(GitHubApi.class, "https://api.github.com");
Далее мы сможем использовать этот клиент для обращения к таким методам, как contributors и issues. При необходимости можно провести более глубокую настройку. Например, можно добавить логирование каждого запроса / ответа сервера, количество повторных вызовов в случае ошибок и многое другое.
Заключение
Безусловно, можно использовать множество реализаций генерации кода, таких как OpenAPI. Данный подход дает гибкость при описании POJO и простой настройке клиента.
