В эпоху микросервисов приходится все чаще и чаще писать интеграции для их взаимодействия как между собой, так и со сторонними системами. Кто-то создаёт отдельные библиотеки с интеграцией и переиспользует их в нескольких микросервисах, кто-то захламляет проект огромным количеством 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 и простой настройке клиента.