Перейти к основному содержимому

Как реализовать расширение Quarkus

· 7 мин. чтения

1. Обзор

Quarkus — это фреймворк, состоящий из ядра и набора расширений. Ядро основано на внедрении контекста и зависимостей (CDI), а расширения обычно предназначены для интеграции сторонней инфраструктуры путем предоставления их основных компонентов в виде компонентов CDI.

В этом руководстве мы сосредоточимся на том, как написать расширение Quarkus, предполагая базовое понимание Quarkus .

2. Что такое расширение Quakus

Расширение Quarkus — это просто модуль, который может работать поверх приложения Quarkus. Само приложение Quarkus представляет собой базовый модуль с набором других расширений.

Наиболее распространенный вариант использования такого расширения — запуск сторонней платформы поверх приложения Quarkus.

3. Запуск Liquibase в обычном Java-приложении

Давайте попробуем реализовать расширение для интеграции Liquibase, инструмента для управления изменениями базы данных.

Но прежде чем мы углубимся, нам сначала нужно показать, как запустить миграцию Liquibase из основного метода Java. Это значительно облегчит внедрение расширения.

Точкой входа для платформы Liquibase является API Liquibase. Чтобы использовать это, нам нужен файл журнала изменений, ClassLoader для доступа к этому файлу и соединение с базовой базой данных:

Connection c = DriverManager.getConnection("jdbc:h2:mem:testdb", "user", "password");
ResourceAccessor resourceAccessor = new ClassLoaderResourceAccessor();
String changLogFile = "db/liquibase-changelog-master.xml";
Liquibase liquibase = new Liquibase(changLogFile, resourceAccessor, new JdbcConnection(c));

Имея этот экземпляр, мы просто вызываем метод update() , который обновляет базу данных в соответствии с файлом журнала изменений.

liquibase.update(new Contexts());

Цель состоит в том, чтобы представить Liquibase как расширение Quarkus . То есть предоставление конфигурации базы данных и файла журнала изменений через Quarkus Configuration, а затем создание Liquibase API в виде компонента CDI. Это предоставляет средства для записи вызова миграции для последующего выполнения.

4. Как написать расширение Quarkus

С технической точки зрения расширение Quarkus представляет собой многомодульный проект Maven, состоящий из двух модулей. Первый — это модуль времени выполнения, в котором мы реализуем требования. Второй — это модуль развертывания для обработки конфигурации и генерации кода среды выполнения.

Итак, давайте начнем с создания многомодульного проекта Maven под названием quarkus-liquibase-parent , который содержит два подмодуля, runtime и deploy :

<modules>
<module>runtime</module>
<module>deployment</module>
</modules>

5. Реализация модуля времени выполнения

В модуле выполнения мы реализуем:

  • класс конфигурации для захвата файла журнала изменений Liquibase
  • производитель CDI для предоставления Liquibase API
  • и рекордер, который действует как прокси для записи вызовов вызовов

5.1. Зависимости и плагины Maven

Модуль времени выполнения будет зависеть от модуля quarkus-core и, в конечном итоге, от модулей времени выполнения необходимых расширений. Здесь нам нужна зависимость quarkus-agroal , поскольку нашему расширению нужен источник данных. Мы также включим сюда библиотеку Liquibase :

<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-core</artifactId>
<version>${quarkus.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-agroal</artifactId>
<version>${quarkus.version}</version>
</dependency>
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
<version>3.8.1</version>
</dependency>

Также нам может понадобиться добавить quarkus-bootstrap-maven-plugin . Этот подключаемый модуль автоматически генерирует дескриптор расширения Quarkus, вызывая цель дескриптора расширения .

Или мы можем опустить этот плагин и сгенерировать дескриптор вручную.

В любом случае, мы можем обнаружить, что дескриптор расширения находится в META-INF/quarkus-extension.properties :

deployment-artifact=com.foreach.quarkus.liquibase\:deployment\:1.0-SNAPSHOT

5.2. Разоблачение конфигурации

Чтобы предоставить файл журнала изменений, нам нужно реализовать класс конфигурации:

@ConfigRoot(name = "liquibase", phase = ConfigPhase.BUILD_AND_RUN_TIME_FIXED)
public final class LiquibaseConfig {
@ConfigItem
public String changeLog;
}

Мы аннотируем класс @ConfigRoot и свойства @ConfigItem. Таким образом, поле changeLog , которое представляет собой журнал изменений в верблюжьем регистре, будет предоставлено через ключ quarkus.liquibase.change-log в файле application.properties , расположенном в пути к классам приложения Quarkus:

quarkus.liquibase.change-log=db/liquibase-changelog-master.xml

Мы также можем отметить значение ConfigRoot.phase , которое указывает, когда разрешать ключ журнала изменений . В этом случае BUILD_AND_RUN_TIME_FIXED ключ считывается во время развертывания и доступен приложению во время выполнения.

5.3. Предоставление Liquibase API как CDI Bean

Выше мы видели, как запустить миграцию Liquibase из основного метода.

Теперь мы воспроизведем тот же код, но в виде компонента CDI, и для этой цели воспользуемся производителем CDI:

@Produces
public Liquibase produceLiquibase() throws Exception {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
ResourceAccessor resourceAccessor = new ClassLoaderResourceAccessor(classLoader);
DatabaseConnection jdbcConnection = new JdbcConnection(dataSource.getConnection());
Liquibase liquibase = new Liquibase(liquibaseConfig.changeLog, resourceAccessor, jdbcConnection);
return liquibase;
}

5.4. Запись байт-кода

На этом шаге мы напишем класс записи, который действует как прокси для записи байт-кода и настройки логики времени выполнения:

@Recorder
public class LiquibaseRecorder {

public BeanContainerListener setLiquibaseConfig(LiquibaseConfig liquibaseConfig) {
return beanContainer -> {
LiquibaseProducer producer = beanContainer.instance(LiquibaseProducer.class);
producer.setLiquibaseConfig(liquibaseConfig);
};
}

public void migrate(BeanContainer container) throws LiquibaseException {
Liquibase liquibase = container.instance(Liquibase.class);
liquibase.update(new Contexts());
}

}

Здесь мы должны записать два вызова. setLiquibaseConfig для настройки конфигурации и migrate для выполнения миграции. Далее мы рассмотрим, как эти методы записи вызываются процессорами шагов сборки развертывания, которые мы реализуем в модуле развертывания.

Обратите внимание, что когда мы вызываем эти методы записи во время сборки, инструкции не выполняются, а записываются для последующего выполнения во время запуска.

6. Реализация модуля развертывания

Центральными компонентами расширения Quarkus являются процессоры шагов сборки . Это методы, аннотированные как @BuildStep , которые генерируют байт-код с помощью средств записи и выполняются во время сборки с помощью цели сборки quarkus-maven-plugin , настроенной в приложении Quarkus.

@BuildStep упорядочиваются благодаря BuildItem s. Они потребляют элементы сборки, созданные на ранних этапах сборки, и создают элементы сборки для более поздних шагов сборки.

Сгенерированный код всех упорядоченных шагов сборки, найденных в модулях развертывания приложения, на самом деле является кодом времени выполнения.

6.1. Зависимости Maven

Модуль развертывания должен зависеть от соответствующего модуля среды выполнения и, в конечном итоге, от модулей развертывания необходимых расширений:

<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-core-deployment</artifactId>
<version>${quarkus.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc-deployment</artifactId>
<version>${quarkus.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-agroal-deployment</artifactId>
<version>${quarkus.version}</version>
</dependency>

<dependency>
<groupId>com.foreach.quarkus.liquibase</groupId>
<artifactId>runtime</artifactId>
<version>${project.version}</version>
</dependency>

Последняя стабильная версия расширений Quarkus одинакова для модуля среды выполнения.

6.2. Реализация процессоров шагов сборки

Теперь давайте реализуем два процессора шагов сборки для записи байт-кода. Процессор первого шага сборки — это метод build() , который записывает байт-код для выполнения в статическом методе инициализации. Мы настраиваем это через значение STATIC_INIT :

@Record(ExecutionTime.STATIC_INIT)
@BuildStep
void build(BuildProducer<AdditionalBeanBuildItem> additionalBeanProducer,
BuildProducer<FeatureBuildItem> featureProducer,
LiquibaseRecorder recorder,
BuildProducer<BeanContainerListenerBuildItem> containerListenerProducer,
DataSourceInitializedBuildItem dataSourceInitializedBuildItem) {

featureProducer.produce(new FeatureBuildItem("liquibase"));

AdditionalBeanBuildItem beanBuilItem = AdditionalBeanBuildItem.unremovableOf(LiquibaseProducer.class);
additionalBeanProducer.produce(beanBuilItem);

containerListenerProducer.produce(
new BeanContainerListenerBuildItem(recorder.setLiquibaseConfig(liquibaseConfig)));
}

Во- первых, мы создаем FeatureBuildItem , чтобы отметить тип или имя расширения. Затем мы создаем AdditionalBeanBuildItem , чтобы bean- компонент LiquibaseProducer был доступен для контейнера Quarkus .

Наконец, мы создаем BeanContainerListenerBuildItem , чтобы запустить BeanContainerListener после запуска Quarkus BeanContainer . Здесь, в слушателе, мы передаем конфигурацию bean-компоненту Liquibase.

В свою очередь, processMigration() запишет вызов для выполнения в основном методе, поскольку он настроен с использованием параметра RUNTIME_INIT для записи.

@Record(ExecutionTime.RUNTIME_INIT)
@BuildStep
void processMigration(LiquibaseRecorder recorder,
BeanContainerBuildItem beanContainer) throws LiquibaseException {
recorder.migrate(beanContainer.getValue());
}

Здесь, в этом процессоре, мы только что вызвали метод записи migrate() , который, в свою очередь, записывает метод update() Liquibase для последующего выполнения.

7. Тестирование расширения Liquibase

Чтобы протестировать наше расширение, мы сначала начнем с создания приложения Quarkus с помощью quarkus-maven-plugin :

mvn io.quarkus:quarkus-maven-plugin:1.0.0.CR1:create\
-DprojectGroupId=com.foreach.quarkus.app\
-DprojectArtifactId=quarkus-app

Далее мы добавим наше расширение в качестве зависимости в дополнение к расширению Quarkus JDBC, соответствующему нашей базовой базе данных:

<dependency>
<groupId>com.foreach.quarkus.liquibase</groupId>
<artifactId>runtime</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-h2</artifactId>
<version>1.0.0.CR1</version>
</dependency>

Далее нам понадобится плагин quarkus-maven-plugin в нашем файле pom:

<plugin>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-maven-plugin</artifactId>
<version>${quarkus.version}</version>
<executions>
<execution>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
</plugin>

Это особенно полезно для запуска приложения с целью разработки или создания исполняемого файла с целью сборки .

А затем мы предоставим конфигурацию источника данных через файл application.properties , расположенный в src/main/resources :

quarkus.datasource.url=jdbc:h2:mem:testdb
quarkus.datasource.driver=org.h2.Driver
quarkus.datasource.username=user
quarkus.datasource.password=password

Далее мы предоставим конфигурацию журнала изменений для нашего файла журнала изменений:

quarkus.liquibase.change-log=db/liquibase-changelog-master.xml

Наконец, мы можем запустить приложение либо в режиме разработки:

mvn compile quarkus:dev

Или в производственном режиме:

mvn clean package
java -jar target/quarkus-app-1.0-SNAPSHOT-runner.jar

8. Заключение

В этой статье мы реализовали расширение Quarkus. В качестве примера мы продемонстрировали, как запустить Liquibase поверх приложения Quarkus.

Полный исходный код доступен на GitHub .