«1. Обзор
В этой статье мы сосредоточимся на создании микросервиса на основе Eclipse MicroProfile.
Мы рассмотрим, как написать веб-приложение RESTful с использованием JAX-RS, CDI и JSON-P API.
2. Архитектура микросервисов
Проще говоря, микросервисы — это стиль архитектуры программного обеспечения, который формирует законченную систему как набор нескольких независимых сервисов.
Каждый из них фокусируется на одном функциональном периметре и взаимодействует с другими с помощью независимого от языка протокола, такого как REST.
3. Eclipse MicroProfile
Eclipse MicroProfile — это инициатива, направленная на оптимизацию Enterprise Java для архитектуры микросервисов. Он основан на подмножестве API Jakarta EE WebProfile, поэтому мы можем создавать приложения MicroProfile так же, как мы создаем приложения Jakarta EE.
Цель MicroProfile — определить стандартные API для создания микросервисов и предоставления переносимых приложений в нескольких средах выполнения MicroProfile.
4. Зависимости Maven
Все зависимости, необходимые для создания приложения Eclipse MicroProfile, предоставляются этой зависимостью BOM (Bill Of Materials):
<dependency>
<groupId>org.eclipse.microprofile</groupId>
<artifactId>microprofile</artifactId>
<version>1.2</version>
<type>pom</type>
<scope>provided</scope>
</dependency>
Область действия задана, поскольку среда выполнения MicroProfile уже включает API и реализация.
5. Модель представления
Начнем с создания класса быстрого ресурса:
public class Book {
private String id;
private String name;
private String author;
private Integer pages;
// ...
}
Как мы видим, в этом классе Book нет аннотации.
6. Использование CDI
Проще говоря, CDI — это API, обеспечивающий внедрение зависимостей и управление жизненным циклом. Это упрощает использование Enterprise bean-компонентов в веб-приложениях.
Давайте теперь создадим управляемый компонент CDI в качестве хранилища для представления книги:
@ApplicationScoped
public class BookManager {
private ConcurrentMap<String, Book> inMemoryStore
= new ConcurrentHashMap<>();
public String add(Book book) {
// ...
}
public Book get(String id) {
// ...
}
public List getAll() {
// ...
}
}
Мы аннотируем этот класс с помощью @ApplicationScoped, потому что нам нужен только один экземпляр, состояние которого совместно используется всеми клиентами. Для этого мы использовали ConcurrentMap как типобезопасное хранилище данных в памяти. Затем мы добавили методы для операций CRUD.
Теперь наш bean-компонент готов к CDI и может быть внедрен в bean-компонент BookEndpoint с помощью аннотации @Inject.
7. JAX-RS API
Чтобы создать REST-приложение с JAX-RS, нам нужно создать класс Application с аннотацией @ApplicationPath и ресурс с аннотацией @Path.
7.1. Приложение JAX RS
Приложение JAX-RS идентифицирует базовый URI, под которым мы предоставляем ресурс в веб-приложении.
Давайте создадим следующее приложение JAX-RS:
@ApplicationPath("/library")
public class LibraryApplication extends Application {
}
В этом примере все классы ресурсов JAX-RS в веб-приложении связаны с приложением LibraryApplication, что делает их одним и тем же путем к библиотеке, это значение Аннотация пути к приложению.
Этот аннотированный класс сообщает среде выполнения JAX RS, что она должна автоматически находить ресурсы и предоставлять их.
7.2. Конечная точка JAX RS
Класс конечной точки, также называемый классом ресурсов, должен определять один ресурс, хотя технически возможно множество одинаковых типов.
Каждый класс Java с аннотацией @Path или хотя бы один метод с аннотацией @Path или @HttpMethod является конечной точкой.
Теперь мы создадим конечную точку JAX-RS, которая предоставляет это представление:
@Path("books")
@RequestScoped
public class BookEndpoint {
@Inject
private BookManager bookManager;
@GET
@Path("{id}")
@Produces(MediaType.APPLICATION_JSON)
public Response getBook(@PathParam("id") String id) {
return Response.ok(bookManager.get(id)).build();
}
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getAllBooks() {
return Response.ok(bookManager.getAll()).build();
}
@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response add(Book book) {
String bookId = bookManager.add(book);
return Response.created(
UriBuilder.fromResource(this.getClass())
.path(bookId).build())
.build();
}
}
На этом этапе мы можем получить доступ к ресурсу BookEndpoint по пути /library/books в веб-приложении.
7.3. JAX RS JSON Media Type
JAX RS поддерживает многие типы носителей для связи с клиентами REST, но Eclipse MicroProfile ограничивает использование JSON, поскольку указывает использование JSOP-P API. Таким образом, нам нужно аннотировать наши методы с помощью @Consumes(MediaType.APPLICATION_JSON) и @Produces(MediaType.APPLICATION_JSON).
Аннотация @Consumes ограничивает допустимые форматы — в этом примере принимается только формат данных JSON. Заголовок HTTP-запроса Content-Type должен быть application/json.
Та же идея лежит в основе аннотации @Produces. Среда выполнения JAX RS должна маршалировать ответ в формате JSON. Заголовок HTTP-запроса Accept должен быть application/json.
8. JSON-P
JAX RS Runtime поддерживает JSON-P из коробки, поэтому мы можем использовать JsonObject в качестве входного параметра метода или возвращаемого типа.
«Но в реальном мире мы часто работаем с классами POJO. Итак, нам нужен способ сделать сопоставление между JsonObject и POJO. Вот где поставщик сущностей JAX RS начинает играть.
Для маршалинга входного потока JSON в POJO Book, который вызывает метод ресурса с параметром типа Book, нам нужно создать класс BookMessageBodyReader:
@Provider
@Consumes(MediaType.APPLICATION_JSON)
public class BookMessageBodyReader implements MessageBodyReader<Book> {
@Override
public boolean isReadable(
Class<?> type, Type genericType,
Annotation[] annotations,
MediaType mediaType) {
return type.equals(Book.class);
}
@Override
public Book readFrom(
Class type, Type genericType,
Annotation[] annotations,
MediaType mediaType,
MultivaluedMap<String, String> httpHeaders,
InputStream entityStream) throws IOException, WebApplicationException {
return BookMapper.map(entityStream);
}
}
Мы делаем тот же процесс, чтобы демаршалировать Book в JSON поток вывода, вызывающий метод ресурса, тип возвращаемого значения которого — Book, путем создания BookMessageBodyWriter:
@Provider
@Produces(MediaType.APPLICATION_JSON)
public class BookMessageBodyWriter
implements MessageBodyWriter<Book> {
@Override
public boolean isWriteable(
Class<?> type, Type genericType,
Annotation[] annotations,
MediaType mediaType) {
return type.equals(Book.class);
}
// ...
@Override
public void writeTo(
Book book, Class<?> type,
Type genericType,
Annotation[] annotations,
MediaType mediaType,
MultivaluedMap<String, Object> httpHeaders,
OutputStream entityStream) throws IOException, WebApplicationException {
JsonWriter jsonWriter = Json.createWriter(entityStream);
JsonObject jsonObject = BookMapper.map(book);
jsonWriter.writeObject(jsonObject);
jsonWriter.close();
}
}
Поскольку BookMessageBodyReader и BookMessageBodyWriter аннотируются с помощью @Provider, они автоматически регистрируются средой выполнения JAX RS.
9. Сборка и запуск приложения
Приложение MicroProfile является переносимым и должно работать в любой совместимой среде выполнения MicroProfile. Мы объясним, как создать и запустить наше приложение в Open Liberty, но мы можем использовать любой совместимый Eclipse MicroProfile.
Мы настраиваем среду выполнения Open Liberty через конфигурационный файл server.xml:
<server description="OpenLiberty MicroProfile server">
<featureManager>
<feature>jaxrs-2.0</feature>
<feature>cdi-1.2</feature>
<feature>jsonp-1.0</feature>
</featureManager>
<httpEndpoint httpPort="${default.http.port}" httpsPort="${default.https.port}"
id="defaultHttpEndpoint" host="*"/>
<applicationManager autoExpand="true"/>
<webApplication context-root="${app.context.root}" location="${app.location}"/>
</server>
Давайте добавим плагин Liberty-maven-plugin в наш pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<plugin>
<groupId>net.wasdev.wlp.maven.plugins</groupId>
<artifactId>liberty-maven-plugin</artifactId>
<version>2.1.2</version>
<configuration>
<assemblyArtifact>
<groupId>io.openliberty</groupId>
<artifactId>openliberty-runtime</artifactId>
<version>17.0.0.4</version>
<type>zip</type>
</assemblyArtifact>
<configFile>${basedir}/src/main/liberty/config/server.xml</configFile>
<packageFile>${package.file}</packageFile>
<include>${packaging.type}</include>
<looseApplication>false</looseApplication>
<installAppPackages>project</installAppPackages>
<bootstrapProperties>
<app.context.root>/</app.context.root>
<app.location>${project.artifactId}-${project.version}.war</app.location>
<default.http.port>9080</default.http.port>
<default.https.port>9443</default.https.port>
</bootstrapProperties>
</configuration>
<executions>
<execution>
<id>install-server</id>
<phase>prepare-package</phase>
<goals>
<goal>install-server</goal>
<goal>create-server</goal>
<goal>install-feature</goal>
</goals>
</execution>
<execution>
<id>package-server-with-apps</id>
<phase>package</phase>
<goals>
<goal>install-apps</goal>
<goal>package-server</goal>
</goals>
</execution>
</executions>
</plugin>
Этот плагин можно настраивать, бросая набор свойств:
<properties>
<!--...-->
<app.name>library</app.name>
<package.file>${project.build.directory}/${app.name}-service.jar</package.file>
<packaging.type>runnable</packaging.type>
</properties>
Вышеупомянутая цель exec создает исполняемый файл jar, так что наше приложение будет независимым микросервисом, который можно развернуть и запустить изолированно. Мы также можем развернуть его как образ Docker.
Чтобы создать исполняемый файл jar, выполните следующую команду:
mvn package
А для запуска нашего микросервиса мы используем эту команду:
java -jar target/library-service.jar
Это запустит среду выполнения Open Liberty и развернет наш сервис. Мы можем получить доступ к нашей конечной точке и получить все книги по этому URL-адресу:
curl http://localhost:9080/library/books
Результатом является JSON:
[
{
"id": "0001-201802",
"isbn": "1",
"name": "Building Microservice With Eclipse MicroProfile",
"author": "baeldung",
"pages": 420
}
]
Чтобы получить одну книгу, мы запрашиваем этот URL-адрес:
curl http://localhost:9080/library/books/0001-201802
И результат JSON:
{
"id": "0001-201802",
"isbn": "1",
"name": "Building Microservice With Eclipse MicroProfile",
"author": "baeldung",
"pages": 420
}
Теперь мы добавим новую книгу, взаимодействуя с API:
curl
-H "Content-Type: application/json"
-X POST
-d '{"isbn": "22", "name": "Gradle in Action","author": "baeldung","pages": 420}'
http://localhost:9080/library/books
Как мы видим, статус ответа 201, что указывает на то, что книга была успешно создан, а Location — это URI, по которому мы можем получить к нему доступ:
< HTTP/1.1 201 Created
< Location: http://localhost:9080/library/books/0009-201802
10. Заключение
В этой статье показано, как создать простой микросервис на основе Eclipse MicroProfile, обсуждая JAX RS, JSON-P и CDI. .
Код доступен на Github; это проект на основе Maven, поэтому его должно быть просто импортировать и запускать как есть.