知識庫 / Spring / Spring Boot RSS 訂閱

使用 AsyncAPI 和 Springwolf 文檔 Spring 事件驅動 API

Spring Boot
HongKong
6
11:33 AM · Dec 06 ,2025

1. 概述

記錄API是構建應用程序的必要組成部分。它是一份我們與客户共享的合同,同時詳細記錄了我們的集成點的工作方式。文檔應易於訪問、理解和實施。

在本教程中,我們將探討 Springwolf 用於記錄事件驅動的 Spring Boot 服務。Springwolf 實現了 AsyncAPI 規範,它是 OpenAPI 規範的適應版本,用於事件驅動的 API。

Springwolf 協議無關性,涵蓋 Spring Kafka、RabbitMQ、JMS、AWS SNS & SQS、STOMP (WebSocket) 和 CloudStream 實現。

使用 Spring Kafka 作為我們的事件驅動系統,Springwolf 自動為我們生成 AsyncAPI 文檔。 某些消費者會自動檢測。 其他信息由我們提供。

2. 設置 Springwolf

要開始使用 Springwolf,我們需要添加依賴並進行配置。

2.1. 添加依賴

假設我們已經有一個運行的 Spring 應用,並且使用了 Spring Kafka,則需要在 Maven 項目的 pom.xml 文件中將 springwolf-kafka 添加為依賴:

<dependency>
    <groupId>io.github.springwolf</groupId>
    <artifactId>springwolf-kafka</artifactId>
    <version>1.7.0</version>
</dependency>

最新版本可以在 Maven Central 找到,關於除了 Spring Kafka 之外的其他綁定支持,請參考項目網站。

2.2. application.properties 配置

在最基本的形式下,我們向我們的 application.properties 添加以下 Springwolf 配置:

# Springwolf Configuration
springwolf.docket.base-package=com.baeldung.boot.documentation.springwolf.adapter
springwolf.docket.info.title=${spring.application.name}
springwolf.docket.info.version=1.0.0
springwolf.docket.info.description=Baeldung Tutorial Application to Demonstrate AsyncAPI Documentation using Springwolf

# Springwolf Kafka Configuration
springwolf.docket.servers.kafka-server.protocol=kafka
springwolf.docket.servers.kafka-server.host=${spring.kafka.bootstrap-servers}

第一個塊設置了通用的 Springwolf 配置。 這包括 base-package,Springwolf 使用它來自動檢測監聽器。 此外,我們還設置了在 docket 配置鍵下的通用信息,該信息出現在 AsyncAPI 文檔中。

然後,我們設置了 springwolf-kafka 相關的配置。 同樣,這出現在 AsyncAPI 文檔中。

2.3. 驗證

現在,我們準備好運行我們的應用程序。應用程序啓動後,AsyncAPI 文檔默認在 /springwolf/docs 路徑下可用:

http://localhost:8080/springwolf/docs

3. AsyncAPI 文檔

AsyncAPI 文檔遵循與OpenAPI文檔相似的結構 (參考鏈接)。首先,我們只關注關鍵部分。 規範(規範可在AsyncAPI網站上找到。為了簡便起見,我們只關注一部分屬性。

在後續子章節中,我們將逐步分析AsyncAPI文檔的JSON格式。我們從以下結構開始:

{
    "asyncapi": "3.0.0",
    "info": { ... },
    "servers": { ... },
    "channels": { ... },
    "components": {
        "messages": { ... },
        "schemas": { ... }
    },
    "operations": { ... }
}

3.1. info 部分

info 部分包含有關應用程序本身的資料。 這包括至少以下字段:標題應用程序版本描述

根據我們添加到配置中的信息,創建了以下結構:

"info": {
    "title": "Baeldung Tutorial Springwolf Application",
    "version": "1.0.0",
    "description": "Baeldung Tutorial Application to Demonstrate AsyncAPI Documentation using Springwolf"
}

3.2 服務器部分

同樣,服務器部分包含有關我們的 Kafka 代理的信息,並基於上述的 application.properties 配置:

"servers": {
    "kafka-server": {
        "host": "127.0.0.1:9092",
        "protocol": "kafka"
    }
}

3.3. 通道部分

本部分目前為空,因為我們尚未配置應用程序中的任何消費者或生產者。在稍後部分配置它們之後,我們將看到以下結構:

"channels": {
    "incoming-topic": {
         "address": "incoming-topic",
         "messages": {
            "IncomingPayloadDto": {
                "$ref": "#/components/messages/IncomingPayloadDto"
            }
        }
    }
}

通用術語 channel 指的是 Kafka 術語中的 topic

為了避免在多個渠道和操作中重複相同的 payload 信息,AsyncAPI 使用 $ref 符號來指示 components 部分的引用

3.4. 組件 章節

再次説明,此時該章節為空,但將具有以下結構:

"components": {
    "messages": {
        "IncomingPayloadDto": {
            ...
            "payload": {
                "schema": {
                    "$ref": "#/components/schemas/IncomingPayloadDto"
                }
            },
            "headers": ...
        }
    },
    "schemas": {
        "IncomingPayloadDto": {
            "type": "object",
             "properties": {
                ...
                "someString": {
                    "type": "string"
                }
            },
            "example": {
                "someEnum": "FOO2",
                "someLong": 1,
                "someString": "string"
            }
        }
    }
}

組件部分包含所有 $ref 引用詳情,包括 #/components/messages/IncomingPayloadDto#/components/schemas/IncomingPayloadDto

除了消息之外,payload 模式也位於 components 部分。模式字段包括數據的 type、payload 的 properties 以及 payload 的可選 (JSON) example

3.5. 操作 模塊

一個 操作 對象與一個 通道 (3.3) 以及一個 消息 (3.4) 關聯,該消息可以被髮送或接收。它可能包含額外的信息,例如 描述

"operations": {
    "incoming-topic_receive_consume": {
        "action": "receive",
        "channel": {
            "$ref": "#/channels/incoming-topic"
        },
        "title": "incoming-topic_receive",
        "description": "More details for the incoming topic",
        "messages": [
            {
                "$ref": "#/channels/incoming-topic/messages/IncomingPayloadDto"
            }
        ]
    }
}

類似於 通道操作對象是一個鍵值對映射,其中鍵是操作 ID,值是 操作對象。在此,incoming-topic_receive_consume 是 Springwolf 生成的操作 ID。

4. 文檔消費者

Springwolf 會自動檢測所有@KafkaListener註解,如下所示。 此外,我們使用@AsyncListener註解手動提供更多詳細信息。

4.1. 自動檢測 @KafkaListener 標註

通過在方法上使用 Spring-Kafka 的 @KafkaListener 標註,Springwolf 會自動在 base-package 中找到消費者:

@KafkaListener(topics = TOPIC_NAME)
public void consume(IncomingPayloadDto payload) {
    // ...
}

現在,AsyncAPI 文檔確實包含名為 TOPIC_NAME 的通道,該通道具有 publish 操作和 IncomingPayloadDto 模式,正如我們之前所見。

4.2. 手動記錄消費者 via <em @AsyncListener@ 註解

使用自動檢測和 結合,可能導致重複記錄。為了能夠手動添加更多信息,我們完全禁用 的自動檢測,並將其添加到 文件中:

springwolf.plugin.kafka.scanner.kafka-listener.enabled=false

接下來,我們添加 Springwolf @AsyncListener 註解到同一方法中,併為 AsyncAPI 文檔提供額外信息:

@KafkaListener(topics = TOPIC_NAME)
@AsyncListener(
    operation = @AsyncOperation(
        channelName = TOPIC_NAME,
        description = "More details for the incoming topic"
    )
)
@KafkaAsyncOperationBinding
public void consume(IncomingPayloadDto payload) {
    // ...
}

此外,我們添加了 @KafkaAsyncOperationBinding 註解,以將通用的 @AsyncOperation 註解與 Kafka 代理連接,在 servers 部分進行設置。 通過此註解,還設置了 Kafka 協議特定的信息。

更改後,AsyncAPI 文檔包含更新後的文檔。

5. 記錄生產者

生產者手動記錄使用 Springwolf 的 @AsyncPublisher 註解。

5.1. 手動記錄生產者,通過 <em @AsyncPublisher/> 註解

類似於 <em @AsyncListener/> 註解,我們添加 <em @AsyncPublisher/> 註解,並添加 <em @KafkaAsyncOperationBinding/> 註解

@AsyncPublisher(
    operation = @AsyncOperation(
        channelName = TOPIC_NAME,
        description = "More details for the outgoing topic"
    )
)
@KafkaAsyncOperationBinding
public void publish(OutgoingPayloadDto payload) {
    kafkaTemplate.send(TOPIC_NAME, payload);
}

根據以上信息,Springwolf 為 TOPIC_NAME 頻道添加了 subscribe 操作到 channels 部分,利用上述信息。 payload type 從方法簽名中提取,類似於 @AsyncListener 的提取方式。

6. 提升文檔質量

AsyncAPI 規範涵蓋了我們之前所討論的更多功能。接下來,我們對 Spring Kafka 默認的 __TypeId__ 頭部進行文檔記錄,並改進了有效負載的文檔。

6.1. 添加 Kafka 頭部

當運行原生 Spring Kafka 應用程序時,Spring Kafka 會自動添加 TypeId 頭部,以協助消費者的消息負載解序列化。

我們通過在 @AsyncOperation 字段上設置 headers 字段,將 TypeId 頭部添加到文檔中,該字段位於 @AsyncListener (或 @AsyncPublisher) 註解中。

@AsyncListener(
    operation = @AsyncOperation(
        ...,
        headers = @AsyncOperation.Headers(
            schemaName = "SpringKafkaDefaultHeadersIncomingPayloadDto",
            values = {
                // this header is generated by Spring by default
                @AsyncOperation.Headers.Header(
                    name = DEFAULT_CLASSID_FIELD_NAME,
                    description = "Spring Type Id Header",
                    value = "com.baeldung.boot.documentation.springwolf.dto.IncomingPayloadDto"
                ),
            }
        )
    )
)

現在,AsyncAPI 文檔包含一個新的字段 headers 作為 message 對象的一部分。

6.2. 添加 Payload 詳情

我們使用 Swagger@Schema 標註,以提供關於 Payload 的額外信息。在下面的代碼片段中,我們設置了 description、一個 example 值,以及該字段是否是 required

@Schema(description = "Outgoing payload model")
public class OutgoingPayloadDto {
    @Schema(description = "Foo field", example = "bar", requiredMode = NOT_REQUIRED)
    private String foo;

    @Schema(description = "IncomingPayload field", requiredMode = REQUIRED)
    private IncomingPayloadDto incomingWrapped;
}

基於此,我們看到 AsyncAPI 文檔中增強的 OutgoingPayloadDto 模式:

"OutgoingPayloadDto": {
    "type": "object",
    "description": "Outgoing payload model",
    "properties": {
        "incomingWrapped": {
            "$ref": "#/components/schemas/IncomingPayloadDto"
        },
        "foo": {
            "type": "string",
            "description": "Foo field",
            "example": "bar"
        }
    },
    "required": [
        "incomingWrapped"
    ],
    "example": {
        "incomingWrapped": {
            "someEnum": "FOO2",
            "someLong": 5,
            "someString": "some string value"
         },
         "foo": "bar"
    }
}

我們的應用程序的完整 AsyncAPI 文檔可在鏈接的示例項目中使用。

7. 驗證 API 契約變更

當 AsyncAPI 文檔被提交到代碼倉庫時,我們可以將其與當前代碼生成的 AsyncAPI 文檔進行比較。這使得我們能夠驗證 (a) 添加或遷移的 事件類 與我們預期的 示例 相匹配; (b) 無害的重構確實保持了 API 契約不變

一個簡單的測試就足夠了:

@Test
public void asyncApiArtifactTest() {
    InputStream s = this.getClass().getResourceAsStream("/asyncapi.json");
    String expected = new String(s.readAllBytes(), StandardCharsets.UTF_8).trim();

    String actual = restTemplate.getForObject("/springwolf/docs", String.class);

    JSONAssert.assertEquals(expected, actual, JSONCompareMode.STRICT);
}

8. 使用 Springwolf UI

Springwolf 擁有自己的 UI,儘管任何符合 AsyncAPI 規範的文檔渲染器都可以使用。

8.1. 添加 springwolf-ui 依賴

要使用 springwolf-ui,我們需要將 依賴 添加到我們的 pom.xml 文件中,然後重新構建並重啓應用程序:

<dependency>
    <groupId>io.github.springwolf</groupId> 
    <artifactId>springwolf-ui</artifactId
    <version>1.7.0</version>
</dependency>

8.2. 查看 AsyncAPI 文檔

現在,通過訪問 http://localhost:8080/springwolf/asyncapi-ui.html,在瀏覽器中打開文檔。

該網頁的結構與 AsyncAPI 文檔類似,並顯示關於 應用程序服務器通道模式的信息:

8.3. 發佈消息

Springwolf 允許我們直接從瀏覽器發佈消息。點擊 發佈 按鈕會將消息直接發送到 Kafka。 消息頭和消息內容可以根據需要進行調整:

由於安全考慮,此功能默認已禁用。要啓用發佈功能,請將以下行添加到我們的 application.properties 文件中:

springwolf.plugin.kafka.publishing.enabled=true

9. 結論

在本文中,我們已在現有的 Spring Boot Kafka 應用中設置了 Springwolf。

利用消費者自動檢測功能,自動生成符合 AsyncAPI 文檔。我們進一步通過手動配置來增強文檔。

除了通過提供的 REST 端點下載 AsyncAPI 文檔外,我們還使用 springwolf-ui 在瀏覽器中查看文檔。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.