编辑此页面

构建我的第一个扩展

Quarkus 扩展像项目依赖项一样增强您的应用程序。扩展的作用是利用 Quarkus 范例将库无缝集成到 Quarkus 架构中 - 例如,在构建时做更多的事情。这样您就可以使用您经过实战考验的生态系统,并利用 Quarkus 的性能和原生编译。前往 code.quarkus.io 获取受支持的扩展列表。

在本指南中,我们将开发 示例问候扩展。该扩展将公开一个可自定义的 HTTP 端点,该端点仅用于问候访问者。

免责声明
为了确保您清楚,您不需要扩展来向您的应用程序添加 Servlet。本指南是一个简化的示例,用于解释扩展开发的概念,如果您需要更多信息,请参阅完整文档。请记住,它不能代表将事物转移到构建时或简化本机映像构建的强大功能。

先决条件

要完成本指南,您需要

  • 大约 30 分钟

  • 一个 IDE

  • 已安装 JDK 17+ 并正确配置了 JAVA_HOME

  • Apache Maven 3.9.9

  • 如果您想使用它,可以选择 Quarkus CLI

Quarkus 团队没有测试过使用 Java 和 Maven 以外的任何其他方式编写扩展,因此如果您偏离此路径,您的体验可能会有所不同

基本概念

首先,我们需要从一些基本概念开始。

  • JVM 模式 vs 原生模式

    • Quarkus 首先是一个 Java 框架,这意味着您可以开发、打包和运行经典的 JAR 应用程序,这就是我们所说的 JVM 模式

    • 感谢 GraalVM,您可以将您的 Java 应用程序编译为特定于机器的代码(就像您在 Go 或 C++ 中所做的那样),这就是我们所说的 原生模式

将 Java 字节码编译为原生系统特定机器代码的操作称为 提前编译(又名 AoT)。

  • 传统 Java 框架中的构建时 vs 运行时

    • 构建时对应于您应用于 Java 源文件以将其转换为可运行内容(类文件、jar/war、本机映像)的所有操作。通常,此阶段由编译、注释处理、字节码生成等组成。此时,一切都在开发人员的范围和控制之下。

    • 运行时是您执行应用程序时发生的所有操作。它显然侧重于启动面向业务的操作,但它依赖于许多技术操作,例如加载库和配置文件、扫描应用程序的类路径、配置依赖注入、设置您的对象关系映射、实例化您的 REST 控制器等。

通常,Java 框架在实际启动应用程序“面向业务的层”之前,在运行时执行其引导。在引导期间,框架通过扫描类路径来动态收集元数据,以查找配置、实体定义、依赖注入绑定等,以便通过反射实例化适当的对象。主要后果是

  • 延迟您的应用程序的就绪状态:您需要等待几秒钟才能实际服务业务请求。

  • 在引导时达到资源消耗峰值:在受限环境中,您需要根据您的技术引导需求而不是实际的业务需求来调整所需资源的大小。

Quarkus 的理念是通过将这些操作左移并在构建时最终执行它们,从而尽可能地防止缓慢且内存密集型的动态代码执行。Quarkus 扩展是一段 Java 代码,充当您喜欢的库或技术的适配器层。

Quarkus 扩展的描述

Quarkus 扩展由两部分组成

  • 运行时模块,它表示扩展开发人员向应用程序开发人员公开的功能(身份验证过滤器、增强的数据层 API 等)。运行时依赖项是用户将添加作为其应用程序依赖项的依赖项(在 Maven POM 或 Gradle 构建脚本中)。

  • 部署模块,在构建的增强阶段使用,它描述了如何按照 Quarkus 的理念“部署”库。换句话说,它在构建期间将所有 Quarkus 优化应用于您的应用程序。部署模块也是我们为 GraalVM 的原生编译做准备的地方。

用户不应将扩展的部署模块添加为应用程序依赖项。部署依赖项由 Quarkus 在增强阶段从应用程序的运行时依赖项中解析。

此时,您应该已经理解,大多数魔力将在增强构建时通过部署模块发生。

Quarkus 应用程序引导

Quarkus 应用程序有三个不同的引导阶段。

  • 增强。在构建时,Quarkus 扩展将加载并扫描您的应用程序的字节码(包括依赖项)和配置。在此阶段,扩展可以读取配置文件、扫描类以查找特定注释等。收集完所有元数据后,扩展可以预处理库引导操作,例如您的 ORM、DI 或 REST 控制器配置。引导的结果直接记录到字节码中,并将成为您最终应用程序包的一部分。

  • 静态初始化。在运行时,Quarkus 将首先执行一个静态初始化方法,其中包含一些扩展操作/配置。当您进行本机打包时,此静态方法将在构建时进行预处理,并且它生成的对象将被序列化到最终本机可执行文件中,因此初始化代码将不会在本机模式下执行(想象一下,您在此阶段执行斐波那契函数,计算结果将直接记录在本机可执行文件中)。在 JVM 模式下运行应用程序时,此静态初始化阶段在应用程序启动时执行。

  • 运行时初始化。好吧,这里没有什么特别的,我们执行经典的运行时代码执行。因此,您在上述两个阶段运行的代码越多,您的应用程序启动的速度就越快。

现在一切都解释清楚了,我们可以开始编码了!

项目设置

扩展可以使用 Maven 或 Gradle 构建。根据您的构建工具,可以按以下方式进行设置

Gradle 扩展插件仍处于实验阶段,可能缺少 Maven 插件中可用的功能。

Maven 设置

Quarkus 提供了 create-extension Maven Mojo 来初始化您的扩展项目。

它将尝试自动检测其选项

  • quarkus(Quarkus Core)或 quarkus/extensions 目录,它将使用“Quarkus Core”扩展布局和默认值。

  • 使用 -DgroupId=io.quarkiverse.[extensionId],它将使用“Quarkiverse”扩展布局和默认值。

  • 在其他情况下,它将使用“独立”扩展布局和默认值。

  • 我们将来可能会引入其他布局类型。

您可以调用它而不使用任何参数来使用交互模式:mvn io.quarkus.platform:quarkus-maven-plugin:3.24.4:create-extension -N
$ mvn io.quarkus.platform:quarkus-maven-plugin:3.24.4:create-extension -N \
    -DgroupId=org.acme \ (1)
    -DextensionId=greeting-extension \  (2)
    -DwithoutTests (3)

[INFO] --- quarkus-maven-plugin:3.24.4:create-extension (default-cli) @ standalone-pom ---

Detected layout type is 'standalone' (4)
Generated runtime artifactId is 'greeting-extension' (5)


applying codestarts...
🔠 java
🧰 maven
🗃 quarkus-extension
🐒 extension-base

-----------
👍 extension has been successfully generated in:
--> /Users/ia3andy/workspace/redhat/quarkus/demo/greeting-extension
-----------
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  1.659 s
[INFO] Finished at: 2021-01-25T16:17:16+01:00
[INFO] ------------------------------------------------------------------------
1 扩展 groupId
2 扩展 id(非命名空间)。
3 表明我们不想生成任何测试
4 从没有 pom.xml 且没有任何其他选项的目录中,生成器将自动选择“独立”扩展布局
5 使用“独立”布局,namespaceId 默认为空,因此计算出的运行时模块 artifactId 是 extensionId

Maven 生成了一个 greeting-extension 目录,其中包含扩展项目,该项目由父 pom.xmlruntimedeployment 模块组成。

父 pom.xml

您的扩展是一个多模块项目。因此,让我们首先检查 ./greeting-extension/pom.xml 中的父 POM。

<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.acme</groupId>
  <artifactId>greeting-extension-parent</artifactId>
  <version>1.0.0-SNAPSHOT</version>
  <packaging>pom</packaging>
  <name>Greeting Extension - Parent</name>
  <modules>(1)
    <module>deployment</module>
    <module>runtime</module>
  </modules>
  <properties>
    <compiler-plugin.version>3.14.0</compiler-plugin.version>(2)
    <failsafe-plugin.version>${surefire-plugin.version}</failsafe-plugin.version>
    <maven.compiler.release>17</maven.compiler.release>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <quarkus.version>3.24.4</quarkus.version>
    <surefire-plugin.version>3.0.0</surefire-plugin.version>
  </properties>
  <dependencyManagement>
    <dependencies>
      <dependency>(3)
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-bom</artifactId>
        <version>${quarkus.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>
  <build>
    <pluginManagement>
      <plugins>
        <plugin>(4)
          <artifactId>maven-surefire-plugin</artifactId>
          <version>${surefire-plugin.version}</version>
          <configuration>
            <systemPropertyVariables>
              <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
              <maven.home>${maven.home}</maven.home>
              <maven.repo>${settings.localRepository}</maven.repo>
            </systemPropertyVariables>
          </configuration>
        </plugin>
        <plugin>(4)
          <artifactId>maven-failsafe-plugin</artifactId>
          <version>${failsafe-plugin.version}</version>
          <configuration>
            <systemPropertyVariables>
              <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
              <maven.home>${maven.home}</maven.home>
              <maven.repo>${settings.localRepository}</maven.repo>
            </systemPropertyVariables>
          </configuration>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>${compiler-plugin.version}</version>
          <configuration>
            <parameters>true</parameters>
          </configuration>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
</project>
1 您的扩展声明了 2 个子模块 deploymentruntime
2 Quarkus 需要支持 annotationProcessorPaths 配置的最新版本的 Maven 编译器插件。
3 quarkus-bom 使您的依赖项与 Quarkus 在增强阶段使用的依赖项保持一致。
4 Quarkus 需要这些配置才能正确运行测试。

部署模块

让我们看一下部署的 ./greeting-extension/deployment/pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.acme</groupId>
        <artifactId>greeting-extension-parent</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>

    <artifactId>greeting-extension-deployment</artifactId> (1)
    <name>Greeting Extension - Deployment</name>

    <dependencies>
        <dependency>
          <groupId>io.quarkus</groupId>
          <artifactId>quarkus-arc-deployment</artifactId> (2)
        </dependency>
        <dependency>
            <groupId>org.acme</groupId>
            <artifactId>greeting-extension</artifactId> (3)
            <version>${project.version}</version>
        </dependency>
        <dependency>
          <groupId>io.quarkus</groupId>
          <artifactId>quarkus-junit5-internal</artifactId>
          <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>io.quarkus</groupId>
                            <artifactId>quarkus-extension-processor</artifactId>  (4)
                            <version>${quarkus.version}</version>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

关键点是

1 按照惯例,部署模块具有 -deployment 后缀 (greeting-extension-deployment)。
2 部署模块依赖于 quarkus-arc-deployment 工件。稍后我们将看到哪些依赖项适合添加。
3 部署模块还必须依赖于运行时模块。
4 我们将 quarkus-extension-processor 添加到编译器注释处理器。

除了 pom.xml 之外,create-extension 还生成了 org.acme.greeting.extension.deployment.GreetingExtensionProcessor 类。

package org.acme.greeting.extension.deployment;

import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.FeatureBuildItem;

class GreetingExtensionProcessor {

    private static final String FEATURE = "greeting-extension";

    @BuildStep
    FeatureBuildItem feature() {
        return new FeatureBuildItem(FEATURE);
    }

}
FeatureBuildItem 表示扩展提供的功能。功能的名称在应用程序引导期间显示在日志中。一个扩展最多应提供一个功能。

请耐心等待,稍后我们将解释 Build Step Processor 概念和所有扩展部署 API。此时,您只需要了解此类向 Quarkus 解释了如何部署名为 greeting 的功能,该功能是您的扩展。换句话说,您正在增强您的应用程序以使用具有所有 Quarkus 优势(构建时优化、本机支持等)的 greeting 扩展。

运行时模块

最后 ./greeting-extension/runtime/pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemalocation="http://maven.apache.org/pom/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/pom/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/xmlschema-instance">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.acme</groupId>
        <artifactId>greeting-extension-parent</artifactId>
        <version>0.0.1-snapshot</version>
    </parent>

    <artifactId>greeting-extension</artifactId>  (1)
    <name>Greeting Extension - Runtime</name>

    <dependencies>
        <dependency>
          <groupId>io.quarkus</groupId>
          <artifactId>quarkus-arc</artifactId> (2)
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>io.quarkus</groupId>
                <artifactId>quarkus-extension-maven-plugin</artifactId>  (3)
                <version>${quarkus.version}</version>
                <executions>
                    <execution>
                        <phase>compile</phase>
                        <goals>
                            <goal>extension-descriptor</goal>
                        </goals>
                        <configuration>
                            <deployment>${project.groupId}:${project.artifactId}-deployment:${project.version}
                            </deployment>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>io.quarkus</groupId>
                            <artifactId>quarkus-extension-processor</artifactId> (4)
                            <version>${quarkus.version}</version>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

关键点是

1 按照惯例,运行时模块没有后缀 (greeting-extension),因为它是向最终用户公开的工件。
2 运行时模块依赖于 quarkus-arc 工件。
3 我们将 quarkus-extension-maven-plugin 添加到生成包含在运行时工件中的 Quarkus 扩展描述符,该描述符将其与相应的部署工件链接。
4 我们将 quarkus-extension-processor 添加到编译器注释处理器。

Gradle 设置

Quarkus 尚未提供任何初始化扩展的 Gradle 项目的方法。

如前所述,扩展由两个模块组成

  • runtime

  • deployment

我们将创建一个包含这两个模块的 Gradle 多模块项目。这是一个简单的 settings.gradle 示例文件

pluginManagement {
    repositories {
        mavenCentral()
        gradlePluginPortal()
    }
    plugins {
        id 'io.quarkus.extension' version "${quarkus.version}" (1)
    }
}

include 'runtime', 'deployment' (2)

rootProject.name = 'greeting-extension'
1 配置 quarkus 扩展插件版本
2 包括 runtimedeployment 模块

这是根 build.gradle 文件的示例

subprojects {
    apply plugin: 'java-library' (1)
    apply plugin: 'maven-publish' (2)

    group 'org.acme' (3)
    version '1.0-SNAPSHOT'
}
1 java-library 插件应用于所有子模块
2 应用用于发布我们工件的 maven-publish 插件
3 全局设置用于发布的组 id

io.quarkus.extension 插件将用于帮助我们构建扩展。该插件将应用于 runtime 模块。

部署模块

部署模块不需要任何特定的插件。这是 deployment 模块的最小 build.gradle 文件的示例

name = 'greeting-extension-deployment' (1)

dependencies {
    implementation project(':runtime') (2)

    implementation platform("io.quarkus:quarkus-bom:${quarkus.version}")

    testImplementation 'io.quarkus:quarkus-junit5-internal'
}
1 按照惯例,部署模块具有 -deployment 后缀 (greeting-extension-deployment)。
2 部署模块必须依赖于 runtime 模块。

运行时模块

运行时模块应用 io.quarkus.extension 插件。这将

  • quarkus-extension-process 添加为两个模块的注释处理器。

  • 生成扩展描述文件。

这是 runtime 模块的 build.gradle 文件的示例

plugins {
    id 'io.quarkus.extension' (1)
}

name = 'greeting-extension' (2)
description = 'Greeting extension'

dependencies {
    implementation platform("io.quarkus:quarkus-bom:${quarkus.version}")
}
1 应用 io.quarkus.extension 插件。
2 按照惯例,运行时模块没有后缀(因此命名为 greeting-extension),因为它是向最终用户公开的工件。

示例问候扩展的基本版本

实现问候功能

我们的扩展提出的(杀手级)功能是问候用户。为此,我们的扩展将在用户应用程序中部署一个 Servlet,该 Servlet 公开 HTTP 端点 /greeting,该端点以纯文本 Hello 响应 GET 谓词。

runtime 模块是您开发要向用户提出的功能的地方,因此现在是创建我们的 Web Servlet 的时候了。

要在您的应用程序中使用 Servlet,您需要一个 Servlet 容器,例如 Undertow。幸运的是,我们的父 pom.xml 导入的 quarkus-bom 已经包含 Undertow Quarkus 扩展。

我们需要做的就是将 quarkus-undertow 作为依赖项添加到我们的 ./greeting-extension/runtime/pom.xml

    <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-undertow</artifactId>
    </dependency>

对于 Gradle,请将依赖项添加到 ./greeting-extension/runtime/build.gradle 文件中

    implementation 'io.quarkus:quarkus-undertow'
现在可以删除 create-extension mojo 生成的对 quarkus-arc 的依赖项,因为 quarkus-undertow 已经依赖于它。

现在我们可以在 runtime 模块中创建我们的 Servlet org.acme.greeting.extension.GreetingExtensionServlet

mkdir -p ./greeting-extension/runtime/src/main/java/org/acme/greeting/extension
package org.acme.greeting.extension;

import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet
public class GreetingExtensionServlet extends HttpServlet { (1)

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { (2)
        resp.getWriter().write("Hello");
    }
}
1 与往常一样,定义 Servlet 需要扩展 jakarta.servlet.http.HttpServlet
2 由于我们想要响应 HTTP GET 谓词,我们覆盖 doGet 方法并在 Servlet 响应的输出流中写入 Hello

部署问候功能

Quarkus 的魔力依赖于构建时字节码生成,而不是等待运行时代码评估,这就是您的扩展的 deployment 模块的角色。冷静点,我们知道,字节码很难,您不想手动执行。Quarkus 提出了一个高级 API 来让您的生活更轻松。感谢基本概念,您将描述要生成/使用的项目以及相应的步骤,以便生成在部署时生成的字节码。

io.quarkus.builder.item.BuildItem 概念表示您将生成或使用的对象实例(并在某个时候转换为字节码),这要归功于使用 @io.quarkus.deployment.annotations.BuildStep 注释的方法,这些方法描述了您扩展的部署任务。

注意

有关更多信息,请参见核心中 BuildItem 实现的完整列表

返回生成的 org.acme.greeting.extension.deployment.GreetingExtensionProcessor 类。

package org.acme.greeting.extension.deployment;

import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.FeatureBuildItem;

class GreetingExtensionProcessor {

    private static final String FEATURE = "greeting-extension";

    @BuildStep (1)
    FeatureBuildItem feature() {
        return new FeatureBuildItem(FEATURE); (2)
    }

}
1 feature() 方法使用 @BuildStep 注释,这意味着它被识别为 Quarkus 在部署期间必须执行的部署任务。BuildStep 方法在增强时并发运行以增强应用程序。它们使用生产者/消费者模型,其中保证在运行某个步骤之前不会运行,直到其使用的所有项目都已生成。
2 io.quarkus.deployment.builditem.FeatureBuildItemBuildItem 的一个实现,它表示扩展的描述。此 BuildItem 将被 Quarkus 用于在应用程序启动时向用户显示信息。

有很多 BuildItem 实现,每个实现都表示部署过程的一个方面。以下是一些示例

  • ServletBuildItem:描述一个 Servlet(名称、路径等),我们想在部署期间生成。

  • BeanContainerBuildItem:描述一个用于在部署期间存储和检索对象实例的容器。

如果您没有找到您想要实现的 BuildItem,您可以创建自己的实现。请记住,BuildItem 应该尽可能细粒度,表示部署的特定部分。要创建您的 BuildItem,您可以扩展

  • 如果您在部署期间只需要一个项目实例,则可以使用 io.quarkus.builder.item.SimpleBuildItem(例如,BeanContainerBuildItem,您只需要一个容器)。

  • 如果您想要有多个实例,则可以使用 io.quarkus.builder.item.MultiBuildItem(例如,ServletBuildItem,您可以在部署期间生成多个 Servlet)。

现在是声明我们的 HTTP 端点的时候了。为此,我们需要生成一个 ServletBuildItem。此时,我们确信您已经了解,如果 quarkus-undertow 依赖项为我们的 runtime 模块提供了 Servlet 支持,那么我们将在 deployment 模块中需要 quarkus-undertow-deployment 依赖项才能访问 io.quarkus.undertow.deployment.ServletBuildItem

让我们将 quarkus-undertow-deployment 作为依赖项添加到我们的 ./greeting-extension/deployment/pom.xml

    <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-undertow-deployment</artifactId>
    </dependency>
现在可以删除 create-extension mojo 生成的对 quarkus-arc-deployment 的依赖项,因为 quarkus-undertow-deployment 已经依赖于它。

对于 Gradle,请将依赖项添加到 ./greeting-extension/deployment/build.gradle 文件中

    implementation 'io.quarkus:quarkus-undertow-deployment'

我们现在可以更新 org.acme.greeting.extension.deployment.GreetingExtensionProcessor

package org.acme.greeting.extension.deployment;

import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.FeatureBuildItem;
import org.acme.greeting.extension.GreetingExtensionServlet;
import io.quarkus.undertow.deployment.ServletBuildItem;

class GreetingExtensionProcessor {

    private static final String FEATURE = "greeting-extension";

    @BuildStep
    FeatureBuildItem feature() {
        return new FeatureBuildItem(FEATURE);
    }

    @BuildStep
    ServletBuildItem createServlet() { (1)
      ServletBuildItem servletBuildItem = ServletBuildItem.builder("greeting-extension", GreetingExtensionServlet.class.getName())
        .addMapping("/greeting")
        .build(); (2)
      return servletBuildItem;
    }

}
1 我们添加一个返回 ServletBuildItem 并使用 @BuildStep 注释的 createServlet 方法。现在,Quarkus 将处理这个新任务,这将导致在构建时生成 Servlet 注册的字节码。
2 ServletBuildItem 提出了一个流畅的 API 来实例化一个类型为 GreetingExtensionServlet 的名为 greeting-extension 的 Servlet(它是我们的扩展 runtime 模块提供的类),并将其映射到 /greeting 路径。

测试问候扩展功能

在开发 Quarkus 扩展时,您主要想测试您的功能是否已正确部署在应用程序中并按预期工作。这就是为什么测试将托管在 deployment 模块中。

Quarkus 提供了通过 quarkus-junit5-internal 工件(该工件应已在 deployment pom.xml 中)测试扩展的工具,特别是 io.quarkus.test.QuarkusUnitTest 运行程序,该运行程序使用您的扩展启动应用程序。

我们将使用 RestAssured(在 Quarkus 中大量使用)来测试我们的 HTTP 端点。让我们将 rest-assured 依赖项添加到 ./greeting-extension/deployment/pom.xml 中。

    ...
    <dependency>
      <groupId>io.rest-assured</groupId>
      <artifactId>rest-assured</artifactId>
      <scope>test</scope>
    </dependency>

对于 Gradle,请将依赖项添加到 ./greeting-extension/deployment/build.gradle 文件中

    ...
    testImplementation 'io.rest-assured:rest-assured'

create-extension Maven Mojo 可以创建测试和集成测试结构(删除 -DwithoutTests)。在这里,我们将自己创建它

mkdir -p ./greeting-extension/deployment/src/test/java/org/acme/greeting/extension/deployment

要开始测试您的扩展,请创建以下 org.acme.greeting.extension.deployment.GreetingExtensionTest 测试类

package org.acme.greeting.extension.deployment;

import io.quarkus.test.QuarkusUnitTest;
import io.restassured.RestAssured;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import static org.hamcrest.Matchers.containsString;

public class GreetingExtensionTest {

  @RegisterExtension
  static final QuarkusUnitTest config = new QuarkusUnitTest()
    .withEmptyApplication(); (1)

  @Test
  public void testGreeting() {
    RestAssured.when().get("/greeting").then().statusCode(200).body(containsString("Hello")); (2)
  }

}
1 我们注册一个 Junit 扩展,该扩展将使用 Greeting 扩展启动 Quarkus 应用程序。
2 我们验证该应用程序是否具有一个 greeting 端点,该端点以 OK 状态 (200) 和包含 Hello 的纯文本正文响应 HTTP GET 请求

是时候测试并安装到我们的本地 maven 存储库了!

$ mvn clean install
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO]
[INFO] Greeting Extension - Parent                                        [pom]
[INFO] Greeting Extension - Runtime                                       [jar]
[INFO] Greeting Extension - Deployment                                    [jar]
[INFO]
[INFO] -----------------< org.acme:greeting-extension-parent >-----------------
[INFO] Building Greeting Extension - Parent 1.0.0-SNAPSHOT                [1/3]
[INFO] --------------------------------[ pom ]---------------------------------
...
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running org.acme.greeting.extension.deployment.GreetingExtensionTest
2021-01-27 10:24:42,506 INFO  [io.quarkus] (main) Quarkus 3.24.4 on JVM started in 0.470s. Listening on: https://:8081
2021-01-27 10:24:42,508 INFO  [io.quarkus] (main) Profile test activated.
2021-01-27 10:24:42,508 INFO  [io.quarkus] (main) Installed features: [cdi, greeting-extension, servlet]
2021-01-27 10:24:43,764 INFO  [io.quarkus] (main) Quarkus stopped in 0.018s
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 4.799 s - in org.acme.greeting.extension.deployment.GreetingExtensionTest
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ greeting-extension-deployment ---
[INFO] Building jar: /Users/ia3andy/workspace/redhat/quarkus/demo/greeting-extension/deployment/target/greeting-extension-deployment-1.0.0-SNAPSHOT.jar
[INFO]
[INFO] --- maven-install-plugin:2.4:install (default-install) @ greeting-extension-deployment ---
...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary for Greeting Extension - Parent 1.0.0-SNAPSHOT:
[INFO]
[INFO] Greeting Extension - Parent ........................ SUCCESS [  0.303 s]
[INFO] Greeting Extension - Runtime ....................... SUCCESS [  3.345 s]
[INFO] Greeting Extension - Deployment .................... SUCCESS [  7.365 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  11.246 s
[INFO] Finished at: 2021-01-27T10:24:44+01:00
[INFO] ------------------------------------------------------------------------

看起来不错!恭喜您刚刚完成了您的第一个扩展。

调试您的扩展

如果调试是删除错误的过程,那么编程必须是放置它们的过程。 Edsger W. Dijkstra

调试您的应用程序构建

由于您的扩展部署是在应用程序构建期间进行的,因此此过程由您的构建工具触发。这意味着如果您想调试此阶段,您需要启动您的构建工具并启用远程调试模式。

Maven

您可以使用 mvnDebug 激活 Maven 远程调试。您可以使用以下命令行启动您的应用程序

mvnDebug quarkus:dev

默认情况下,Maven 将等待 localhost:8000 上的连接。现在,您可以运行您的 IDE Remote 配置以将其附加到 localhost:8000

Gradle

您可以使用守护程序模式下的标志 org.gradle.debug=trueorg.gradle.daemon.debug=true 激活 Gradle 远程调试。您可以使用以下命令行启动您的应用程序

./gradlew quarkusDev -Dorg.gradle.daemon.debug=true

默认情况下,Gradle 将等待 localhost:5005 上的连接。现在,您可以运行您的 IDE Remote 配置以将其附加到 localhost:5005

调试您的扩展测试

我们一起看到了如何测试您的扩展,有时事情进展并不顺利,您想调试您的测试。这里的原则相同,诀窍是启用 Maven Surefire 远程调试,以便附加 IDE Remote 配置。

cd ./greeting-extension
mvn clean test -Dmaven.surefire.debug

默认情况下,Maven 将等待 localhost:5005 上的连接。

是时候使用您的新扩展了

现在您已经完成了构建您的第一个扩展,您应该渴望在 Quarkus 应用程序中使用它!

经典 Maven 发布

如果在上一步中尚未完成,您应该在您的本地存储库中安装 greeting-extension

cd ./greeting-extension
mvn clean install

然后从另一个目录中,使用我们的工具来创建一个新的 greeting-app Quarkus 应用程序和您的新扩展

mvn io.quarkus.platform:quarkus-maven-plugin:3.24.4:create \
     -DprojectGroupId=org.acme \
     -DprojectArtifactId=greeting-app \
     -Dextensions="org.acme:greeting-extension:1.0.0-SNAPSHOT" \
     -DnoCode

cd 进入 greeting-app

必须在本地 Maven 存储库中安装 greeting-extension 扩展才能在应用程序中使用。

运行应用程序并注意 Installed Features 列表包含 greeting-extension 扩展。

$ mvn quarkus:dev
[INFO] Scanning for projects...
[INFO]
[INFO] -----------------------< org.acme:greeting-app >------------------------
[INFO] Building greeting-app 1.0.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-compiler-plugin:3.14.0:compile (default-compile) @ greeting-app ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- quarkus-maven-plugin:3.24.4:dev (default-cli) @ greeting-app ---
Listening for transport dt_socket at address: 5005
__  ____  __  _____   ___  __ ____  ______
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
2022-11-20 04:25:36,885 INFO  [io.quarkus] (Quarkus Main Thread) greeting-app 1.0.0-SNAPSHOT on JVM (powered by Quarkus 3.24.4) started in 4.591s. Listening on: https://:8080
2022-11-20 04:25:36,911 INFO  [io.quarkus] (Quarkus Main Thread) Profile dev activated. Live Coding activated.
2022-11-20 04:25:36,913 INFO  [io.quarkus] (Quarkus Main Thread) Installed features: [cdi, greetin-extension, resteasy-reactive, smallrye-context-propagation, vertx]

从扩展开发人员的角度来看,Maven 发布策略非常方便和快速,但 Quarkus 还希望更进一步,通过确保生态系统的可靠性来让人们使用扩展。想想看,我们都曾有过维护不善的库、依赖项之间的不兼容性(我们甚至不谈论法律问题)的糟糕的开发者体验。这就是 Quarkus 平台的原因。

Quarkus 平台

Quarkus 平台是一组扩展,它们以 Quarkus 作为开发框架的主要用例为目标,并且可以安全地以任何组合在同一应用程序中使用,而不会产生依赖项冲突。从应用程序开发人员的角度来看,Quarkus 平台表示为一个或多个 Maven BOM,例如 io.quarkus.platform:quarkus-bom:3.24.4io.quarkus.platform:quarkus-camel-bom:3.24.4 等,其依赖项版本约束已全局对齐,以便可以在同一应用程序中以任何顺序导入这些 BOM,而不会引入依赖项冲突。

Quarkiverse Hub

Quarkiverse Hub 是 GitHub 组织,它为社区贡献的 Quarkus 扩展项目提供存储库托管(包括构建、CI 和发布发布设置)。

如果您想知道如何创建新的 Quarkus 扩展并将其添加到 Quarkus 生态系统,以便 Quarkus 社区可以使用 Quarkus 开发工具(包括 Quarkus CLIcode.quarkus.io)发现它,那么 Quarkiverse Hub GitHub 组织将是一个不错的选择。

您可以首先创建一个 扩展请求问题(首先检查是否已提交 此处)并请求领导它。

我们将负责提供一个新的存储库并将其设置为

  • 由我们的工具支持;

  • 将您为您的扩展生成的文档发布到 Quarkiverse 网站;

  • 配置您的扩展以使用 Quarkus 生态系统 CI 针对最新的 Quarkus Core 更改进行构建;

  • 让您自由管理项目并按您喜欢的方式发布到 Maven Central。

托管在 Quarkiverse Hub 中的扩展可能最终会在 Quarkus 平台中,也可能不会。

有关更多信息,请查看 Quarkiverse Wiki此博客文章

结论

创建新扩展起初可能看起来是一项复杂的任务,但是一旦您了解了 Quarkus 改变游戏规则的范例(构建时与运行时),扩展的结构就完全有意义了。

与往常一样,Quarkus 简化了底层的事情(Maven Mojo、字节码生成或测试),使其愉快地开发新功能。

进一步阅读

相关内容