Quarkus Extension for Spring Web API

Prerequisites

如要完成本指南,您需要:

如要完成本指南,您需要:

  • Roughly 15 minutes

  • An IDE

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

  • Apache Maven ${proposed-maven-version}

  • 如果你想使用 Quarkus CLI, 则可以选择使用

  • 如果你想构建一个本机可执行文件(或如果你使用本机容器构建,则使用 Docker),则可以选择安装 Mandrel 或 GraalVM 以及 configured appropriately

Solution

我们建议您遵循接下来的部分中的说明,按部就班地创建应用程序。然而,您可以直接跳到完成的示例。

克隆 Git 存储库: git clone $${quickstarts-base-url}.git,或下载 $${quickstarts-base-url}/archive/main.zip[存档]。

解决方案位于 spring-web-quickstart directory 中。

Creating the Maven project

首先,我们需要一个新项目。使用以下命令创建一个新项目:

CLI
quarkus create app {create-app-group-id}:{create-app-artifact-id} \
    --no-code
cd {create-app-artifact-id}

要创建一个 Gradle 项目,添加 --gradle--gradle-kotlin-dsl 选项。 有关如何安装和使用 Quarkus CLI 的详细信息,请参见 Quarkus CLI 指南。

Maven
mvn {quarkus-platform-groupid}:quarkus-maven-plugin:{quarkus-version}:create \
    -DprojectGroupId={create-app-group-id} \
    -DprojectArtifactId={create-app-artifact-id} \
    -DnoCode
cd {create-app-artifact-id}

要创建一个 Gradle 项目,添加 -DbuildTool=gradle-DbuildTool=gradle-kotlin-dsl 选项。

适用于 Windows 用户:

  • 如果使用 cmd,(不要使用反斜杠 \ ,并将所有内容放在同一行上)

  • 如果使用 Powershell,将 -D 参数用双引号引起来,例如 "-DprojectArtifactId={create-app-artifact-id}"

此命令会生成一个导入 spring-web 扩展的项目。

如果您已配置好 Quarkus 项目,则可以通过在项目基本目录中运行以下命令来将 spring-web 扩展添加到项目中:

CLI
quarkus extension add {add-extension-extensions}
Maven
./mvnw quarkus:add-extension -Dextensions='{add-extension-extensions}'
Gradle
./gradlew addExtension --extensions='{add-extension-extensions}'

这会将以下内容添加到构建文件中:

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-spring-web</artifactId>
</dependency>
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-rest-jackson</artifactId>
</dependency>
build.gradle
implementation("io.quarkus:quarkus-spring-web")
implementation("io.quarkus:quarkus-rest-jackson")

quarkus-spring-web 需要与 quarkus-rest-jacksonquarkus-resteasy-jackson 结合使用才能起作用。

GreetingController

创建 src/main/java/org/acme/spring/web/GreetingController.java 文件,一个包含 Spring Web 注解以定义我们的 REST 端点的控制器,如下所示:

package org.acme.spring.web;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/greeting")
public class GreetingController {

    @GetMapping
    public String hello() {
        return "hello";
    }
}

GreetingControllerTest

请注意,还创建了该控制器的测试:

package org.acme.spring.web;

import io.quarkus.test.junit.QuarkusTest;
import org.junit.jupiter.api.Test;

import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.is;

@QuarkusTest
public class GreetingControllerTest {

    @Test
    public void testHelloEndpoint() {
        given()
          .when().get("/greeting")
          .then()
             .statusCode(200)
             .body(is("hello"));
    }

}

Package and run the application

使用以下内容运行应用程序:

CLI
quarkus dev
Maven
./mvnw quarkus:dev
Gradle
./gradlew --console=plain quarkusDev

用浏览器打开 [role="bare"][role="bare"]http://localhost:8080/greeting.

结果应为: {"message": "hello"}.

Run the application as a native executable

可以使用以下命令生成本机可执行文件:

CLI
quarkus build --native
Maven
./mvnw install -Dnative
Gradle
./gradlew build -Dquarkus.native.enabled=true

Going further with an endpoint returning JSON

上面的 GreetingController 是一个非常简单的端点的示例。然而,在许多情况下都需要返回 JSON 内容,以下示例说明了如何使用 Spring RestController 来实现该目的:

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/greeting")
public class GreetingController {

    @GetMapping("/{name}")
    public Greeting hello(@PathVariable(name = "name") String name) {
        return new Greeting("hello " + name);
    }

    public static class Greeting {
        private final String message;

        public Greeting(String message) {
            this.message = message;
        }

        public String getMessage(){
            return message;
        }
    }
}

对应的测试可能如下所示:

package org.acme.spring.web;

import io.quarkus.test.junit.QuarkusTest;
import org.junit.jupiter.api.Test;

import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.is;

@QuarkusTest
public class GreetingControllerTest {

    @Test
    public void testHelloEndpoint() {
        given()
          .when().get("/greeting/quarkus")
          .then()
            .statusCode(200)
            .body("message", is("hello quarkus"));
    }

}

应当注意,在 Quarkus 中使用 Spring Web 支持时, Jackson 会自动添加到类路径中并得到正确设置。

Adding OpenAPI and Swagger-UI

通过使用 quarkus-smallrye-openapi 扩展,可以添加对 OpenAPISwagger-UI 的支持。

通过运行此命令添加扩展:

./mvnw quarkus:add-extension -Dextensions="io.quarkus:quarkus-smallrye-openapi"

这会将以下内容添加到您的 pom.xml

<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-smallrye-openapi</artifactId>
</dependency>

这足以从你的 REST 端点生成基本 OpenAPI 架构文档:

curl http://localhost:8080/q/openapi

你将看到生成的 OpenAPI 模式文档:

---
openapi: 3.0.1
info:
  title: Generated API
  version: "1.0"
paths:
  /greeting:
    get:
      responses:
        "200":
          description: OK
          content:
            '*/*':
              schema:
                type: string
  /greeting/{name}:
    get:
      parameters:
      - name: name
        in: path
        required: true
        schema:
          type: string
      responses:
        "200":
          description: OK
          content:
            'application/json':
              schema:
                $ref: '#/components/schemas/Greeting'
components:
  schemas:
    Greeting:
      type: object
      properties:
        message:
          type: string

另请参阅 the OpenAPI Guide

Adding MicroProfile OpenAPI Annotations

可以使用 MicroProfile OpenAPI 更好地记录你的架构,例如将以下内容添加到 GreetingController 的类级别:

@OpenAPIDefinition(
    info = @Info(
        title="Greeting API",
        version = "1.0.1",
        contact = @Contact(
            name = "Greeting API Support",
            url = "http://exampleurl.com/contact",
            email = "techsupport@example.com"),
        license = @License(
            name = "Apache 2.0",
            url = "https://www.apache.org/licenses/LICENSE-2.0.html"))
)

你的端点将会如下所示:

@Tag(name = "Hello", description = "Just say hello")
@GetMapping(produces=MediaType.TEXT_PLAIN_VALUE)
public String hello() {
    return "hello";
}

@GetMapping(value = "/{name}", produces=MediaType.APPLICATION_JSON_VALUE)
@Tag(name = "Hello to someone", description = "Just say hello to someone")
public Greeting hello(@PathVariable(name = "name") String name) {
    return new Greeting("hello " + name);
}

将生成此 OpenAPI 架构:

---
openapi: 3.0.1
info:
  title: Greeting API
  contact:
    name: Greeting API Support
    url: http://exampleurl.com/contact
    email: techsupport@example.com
  license:
    name: Apache 2.0
    url: https://www.apache.org/licenses/LICENSE-2.0.html
  version: 1.0.1
tags:
- name: Hello
  description: Just say hello
- name: Hello to someone
  description: Just say hello to someone
paths:
  /greeting:
    get:
      tags:
      - Hello
      responses:
        "200":
          description: OK
          content:
            '*/*':
              schema:
                type: string
  /greeting/{name}:
    get:
      tags:
      - Hello to someone
      parameters:
      - name: name
        in: path
        required: true
        schema:
          type: string
      responses:
        "200":
          description: OK
          content:
            '*/*':
              schema:
                $ref: '#/components/schemas/Greeting'
components:
  schemas:
    Greeting:
      type: object
      properties:
        message:
          type: string

Using Swagger UI

DevTest 模式下运行时,默认情况下会包含 Swagger UI,并且可以选择将其添加到 Prod 模式中,请参阅 the Swagger UI 指南了解更多详情。

导航至 localhost:8080/q/swagger-ui/,你将看到 Swagger UI 屏幕:

spring web guide screenshot01

Supported Spring Web functionalities

Quarkus 目前支持 Spring Web 提供的部分功能。更具体地说,Quarkus 支持 Spring Web 的 REST 相关功能(考虑使用 @RestController 代替 @Controller)。

Annotations

下表总结了受支持的注释:

Table 1. Supported Spring Web annotation
Name Comments

@RestController

@RequestMapping

@GetMapping

@PostMapping

@PutMapping

@DeleteMapping

@PatchMapping

@RequestParam

@RequestHeader

@MatrixVariable

@PathVariable

@CookieValue

@RequestBody

@ResponseStatus

@ExceptionHandler

只能在 @RestControllerAdvice 类中使用,不能基于每个控制器使用

@RestControllerAdvice

只支持 @ExceptionHandler 功能

Controller method return types

支持以下方法返回类型:

  • Primitive types

  • String(将用作文本,不提供 Spring MVC 视图支持)

  • 将通过 JSON 序列化的 POJO 类

  • org.springframework.http.ResponseEntity

Controller method parameter types

除了可以用前一表格中的适当 Spring Web 注释进行注释的方法参数之外,jakarta.servlet.http.HttpServletRequestjakarta.servlet.http.HttpServletResponse 也受支持。不过,为了实现此功能,用户需要添加 quarkus-undertow 依赖项。

Exception handler method return types

支持以下方法返回类型:

  • org.springframework.http.ResponseEntity

  • java.util.Map

Spring ExceptionHandler javadoc 中提到的其他返回类型不受支持。

Exception handler method parameter types

以下参数类型受支持,顺序任意:

  • 异常参数:声明为通用 Exception 或更具体的异常。如果注释本身未通过其 value() 缩小异常类型,这也作为映射提示。

  • 请求和/或响应对象(通常来自 Servlet API)。你可以选择任何特定请求/响应类型,例如 ServletRequest/HttpServletRequest。要使用 Servlet API,需要添加 quarkus-undertow 依赖项。

Spring ExceptionHandler javadoc 中提到的其他参数类型不受支持。

Important Technical Note

请注意,Quarkus 中的 Spring 支持不会启动 Spring 应用程序上下文,也不会运行任何 Spring 基础设施类。Spring 类和注释仅用于读取元数据和/或用作用户代码方法返回类型或参数类型。这对最终用户意味着,添加任意 Spring 库不会产生任何影响。此外,Spring 基础设施类(例如 org.springframework.beans.factory.config.BeanPostProcessor)不会被执行。

Conversion Table

下表显示了 Spring Web 注释如何转换为 Jakarta REST 注释。

Spring Jakarta REST Comments

@RestController

Jakarta REST 中没有等效项。使用 @Path 对类进行注释就足够了

@RequestMapping(path="/api")

@Path("/api")

@RequestMapping(consumes="application/json")

@Consumes("application/json")

@RequestMapping(produces="application/json")

@Produces("application/json")

@RequestParam

@QueryParam

@PathVariable

@PathParam

@RequestBody

Jakarta REST 中没有等效项。请求正文对应的办法参数在 Jakarta REST 中进行处理,无需任何注释

@RestControllerAdvice

Jakarta REST 中没有等效项

@ResponseStatus

Jakarta REST 中没有等效项

@ExceptionHandler

Jakarta REST 中没有等效注释。异常通过实现 jakarta.ws.rs.ext.ExceptionMapper 来处理