From f8df322e22d6f4062f4f2cf2a491794d2837083e Mon Sep 17 00:00:00 2001 From: Zhoujie628 Date: Tue, 8 Sep 2026 18:05:26 +0800 Subject: [PATCH] fix(spring): make server auto-configuration opt-in Signed-off-by: Zhoujie628 --- docs/en/API_REFERENCE.md | 6 ++-- docs/en/INTEGRATION_GUIDE.md | 1 + docs/zh/API_REFERENCE.md | 8 +++-- docs/zh/INTEGRATION_GUIDE.md | 1 + samples/src/main/resources/application.yml | 1 + .../engine/spring/A2AAutoConfiguration.java | 4 +-- .../workflow/engine/spring/A2AProperties.java | 4 +-- .../spring/A2ASlashActionAliasController.java | 7 ++-- .../spring/A2AAutoConfigurationTest.java | 33 ++++++++++++++++++- 9 files changed, 52 insertions(+), 13 deletions(-) diff --git a/docs/en/API_REFERENCE.md b/docs/en/API_REFERENCE.md index 6c7f349a..0992135f 100644 --- a/docs/en/API_REFERENCE.md +++ b/docs/en/API_REFERENCE.md @@ -609,7 +609,7 @@ not add private negotiation-state keys to the wire message. ## spring-boot-starter Module The `spring-boot-starter` module provides Spring Boot auto-configuration for the A2A **server** side (not the -client/workflow side). When on the classpath of a Spring Boot web application, it auto-registers all A2A SDK server +client/workflow side). In a Spring Boot web application, set `a2at.server.enabled=true` to register the A2A SDK server components as Spring beans. ### A2AProperties @@ -618,6 +618,7 @@ Configuration properties prefixed with `a2at.server`: | Property | Default | Description | |----------------------------------------------|----------------------------|------------------------------------------------------------------------| +| `a2at.server.enabled` | `false` | Enable A2A server auto-configuration | | `a2at.server.agent-card` | `classpath:agentcard.json` | Path to the AgentCard JSON file (classpath: or file: prefix supported) | | `a2at.server.path-prefix` | `/a2a/json` | URL path prefix for A2A endpoints | | `a2at.server.slash-action-aliases-enabled` | `false` | Expose slash-style action aliases for gateways that reject colons | @@ -632,6 +633,7 @@ Configuration properties prefixed with `a2at.server`: ```yaml a2at: server: + enabled: true agent-card: classpath:agentcard/my_agent.json path-prefix: /a2a/json slash-action-aliases-enabled: false @@ -658,7 +660,7 @@ Auto-configures the following beans (all `@ConditionalOnMissingBean`, so you can | `requestHandler` | `RequestHandler` | Default request handler | | `restHandler` | `RestHandler` | REST protocol handler | | `a2aController` | `A2AController` | Spring MVC controller for message and task endpoints | -| `a2aSlashActionAliasController` | internal controller | Optional slash-style aliases for action endpoints | +| `a2aSlashActionAliasController` | `A2ASlashActionAliasController` | Optional public controller for slash-style action aliases | ### A2AController diff --git a/docs/en/INTEGRATION_GUIDE.md b/docs/en/INTEGRATION_GUIDE.md index 183f2f09..5aadbff8 100644 --- a/docs/en/INTEGRATION_GUIDE.md +++ b/docs/en/INTEGRATION_GUIDE.md @@ -565,6 +565,7 @@ If the gateway cannot rewrite the backend path, enable the starter's aliases: ```yaml a2at: server: + enabled: true path-prefix: /a2a/json slash-action-aliases-enabled: true ``` diff --git a/docs/zh/API_REFERENCE.md b/docs/zh/API_REFERENCE.md index ede93908..a7839e77 100644 --- a/docs/zh/API_REFERENCE.md +++ b/docs/zh/API_REFERENCE.md @@ -575,8 +575,8 @@ getReceivedMessages() 是保留层级的响应来源,getOutputs() 为便利投 ## spring-boot-starter 模块 -`spring-boot-starter` 模块为 A2A **服务端**(非客户端/工作流侧)提供 Spring Boot 自动配置。当位于 Spring Boot Web 应用的 -classpath 时,自动将所有 A2A SDK 服务端组件注册为 Spring Bean。 +`spring-boot-starter` 模块为 A2A **服务端**(非客户端/工作流侧)提供 Spring Boot 自动配置。在 Spring Boot Web 应用中配置 +`a2at.server.enabled=true` 后,才会将 A2A SDK 服务端组件注册为 Spring Bean。 ### A2AProperties @@ -584,6 +584,7 @@ classpath 时,自动将所有 A2A SDK 服务端组件注册为 Spring Bean。 | 属性 | 默认值 | 说明 | |----------------------------------------------|----------------------------|----------------------------------------------------------| +| `a2at.server.enabled` | `false` | 启用 A2A 服务端自动配置 | | `a2at.server.agent-card` | `classpath:agentcard.json` | AgentCard JSON 文件路径(支持 classpath: 或 file: 前缀) | | `a2at.server.path-prefix` | `/a2a/json` | A2A 端点的 URL 路径前缀 | | `a2at.server.slash-action-aliases-enabled` | `false` | 为拒绝冒号路径的网关暴露斜杠动作别名 | @@ -598,6 +599,7 @@ classpath 时,自动将所有 A2A SDK 服务端组件注册为 Spring Bean。 ```yaml a2at: server: + enabled: true agent-card: classpath:agentcard/my_agent.json path-prefix: /a2a/json slash-action-aliases-enabled: false @@ -624,7 +626,7 @@ a2at: | `requestHandler` | `RequestHandler` | 默认请求处理器 | | `restHandler` | `RestHandler` | REST 协议处理器 | | `a2aController` | `A2AController` | 消息和任务端点的 Spring MVC 控制器 | -| `a2aSlashActionAliasController` | 内部控制器 | 可选的斜杠形式动作端点别名 | +| `a2aSlashActionAliasController` | `A2ASlashActionAliasController` | 可选的斜杠动作别名公共控制器 | ### A2AController diff --git a/docs/zh/INTEGRATION_GUIDE.md b/docs/zh/INTEGRATION_GUIDE.md index 188d04c6..0dde4252 100644 --- a/docs/zh/INTEGRATION_GUIDE.md +++ b/docs/zh/INTEGRATION_GUIDE.md @@ -530,6 +530,7 @@ A2A HTTP+JSON 定义了 `message:send`、`message:stream`、`tasks/{id}:cancel` ```yaml a2at: server: + enabled: true path-prefix: /a2a/json slash-action-aliases-enabled: true ``` diff --git a/samples/src/main/resources/application.yml b/samples/src/main/resources/application.yml index bce52403..282e7aad 100644 --- a/samples/src/main/resources/application.yml +++ b/samples/src/main/resources/application.yml @@ -14,6 +14,7 @@ spring: a2at: server: + enabled: true agent-card: classpath:agentcard/transport_workbench_agent.json path-prefix: /a2a/json diff --git a/spring-boot-starter/src/main/java/dev/openan/workflow/engine/spring/A2AAutoConfiguration.java b/spring-boot-starter/src/main/java/dev/openan/workflow/engine/spring/A2AAutoConfiguration.java index 31bbbf47..61ccef36 100644 --- a/spring-boot-starter/src/main/java/dev/openan/workflow/engine/spring/A2AAutoConfiguration.java +++ b/spring-boot-starter/src/main/java/dev/openan/workflow/engine/spring/A2AAutoConfiguration.java @@ -71,13 +71,13 @@ * * *

The partner only needs to provide an {@link AgentExecutor} implementation as a - * {@code @Component} or {@code @Bean}. Set {@code a2at.server.enabled=false} to disable the whole + * {@code @Component} or {@code @Bean}. Set {@code a2at.server.enabled=true} to enable the * server-side autoconfiguration. */ @AutoConfiguration @ConditionalOnWebApplication @ConditionalOnClass(AgentExecutor.class) -@ConditionalOnProperty(name = "a2at.server.enabled", havingValue = "true", matchIfMissing = true) +@ConditionalOnProperty(name = "a2at.server.enabled", havingValue = "true") @EnableConfigurationProperties(A2AProperties.class) public class A2AAutoConfiguration { diff --git a/spring-boot-starter/src/main/java/dev/openan/workflow/engine/spring/A2AProperties.java b/spring-boot-starter/src/main/java/dev/openan/workflow/engine/spring/A2AProperties.java index af986f1e..116152d2 100644 --- a/spring-boot-starter/src/main/java/dev/openan/workflow/engine/spring/A2AProperties.java +++ b/spring-boot-starter/src/main/java/dev/openan/workflow/engine/spring/A2AProperties.java @@ -30,8 +30,8 @@ @ConfigurationProperties(prefix = "a2at.server") public class A2AProperties { - /** Whether A2A server autoconfiguration is enabled. Set false to disable all A2A beans. */ - private boolean enabled = true; + /** Whether A2A server autoconfiguration is enabled. Set true to create the A2A server beans. */ + private boolean enabled = false; /** Path to the AgentCard JSON file (classpath: or file: prefix supported). */ private String agentCard = "classpath:agentcard.json"; diff --git a/spring-boot-starter/src/main/java/dev/openan/workflow/engine/spring/A2ASlashActionAliasController.java b/spring-boot-starter/src/main/java/dev/openan/workflow/engine/spring/A2ASlashActionAliasController.java index 51d8683a..a5ef9a82 100644 --- a/spring-boot-starter/src/main/java/dev/openan/workflow/engine/spring/A2ASlashActionAliasController.java +++ b/spring-boot-starter/src/main/java/dev/openan/workflow/engine/spring/A2ASlashActionAliasController.java @@ -35,13 +35,14 @@ */ @RestController @ConditionalOnProperty( - name = "a2at.server.slash-action-aliases-enabled", + prefix = "a2at.server", + name = {"enabled", "slash-action-aliases-enabled"}, havingValue = "true") -final class A2ASlashActionAliasController { +public class A2ASlashActionAliasController { private final A2AController delegate; - A2ASlashActionAliasController(A2AController delegate) { + public A2ASlashActionAliasController(A2AController delegate) { this.delegate = delegate; } diff --git a/spring-boot-starter/src/test/java/dev/openan/workflow/engine/spring/A2AAutoConfigurationTest.java b/spring-boot-starter/src/test/java/dev/openan/workflow/engine/spring/A2AAutoConfigurationTest.java index 3fbdb3c4..493953b1 100644 --- a/spring-boot-starter/src/test/java/dev/openan/workflow/engine/spring/A2AAutoConfigurationTest.java +++ b/spring-boot-starter/src/test/java/dev/openan/workflow/engine/spring/A2AAutoConfigurationTest.java @@ -36,6 +36,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.hamcrest.Matchers.containsString; +import java.lang.reflect.Modifier; import java.util.concurrent.ExecutorService; import java.util.concurrent.Flow; import java.util.concurrent.ThreadPoolExecutor; @@ -410,7 +411,8 @@ void slashActionAliasesAreOptIn() { .withBean(AgentExecutor.class, () -> mock(AgentExecutor.class)) .withBean(RequestHandler.class, () -> mock(RequestHandler.class)) .withBean(RestHandler.class, () -> mock(RestHandler.class)) - .withBean(A2AController.class, () -> canonical); + .withBean(A2AController.class, () -> canonical) + .withPropertyValues("a2at.server.enabled=true"); runner.run( context -> assertFalse(context.containsBean("a2aSlashActionAliasController"))); @@ -427,6 +429,17 @@ void slashActionAliasPropertyDefaultsToDisabled() { assertTrue(properties.isSlashActionAliasesEnabled()); } + @Test + void slashActionAliasControllerIsPubliclyAccessible() throws Exception { + assertTrue(Modifier.isPublic(A2ASlashActionAliasController.class.getModifiers())); + assertFalse(Modifier.isFinal(A2ASlashActionAliasController.class.getModifiers())); + assertTrue( + Modifier.isPublic( + A2ASlashActionAliasController.class + .getConstructor(A2AController.class) + .getModifiers())); + } + @Test void blockingTimeoutsComeFromProperties() { A2AProperties properties = new A2AProperties(); @@ -487,6 +500,24 @@ void whenEnabledFalseThenNoA2ABeansCreated() { }); } + @Test + void whenEnabledIsMissingThenNoA2ABeansCreated() { + new WebApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(A2AAutoConfiguration.class)) + .run( + context -> { + assertFalse(context.containsBean("a2aController")); + assertFalse(context.containsBean("agentExecutorPool")); + assertFalse(context.containsBean("eventBus")); + }); + } + + @Test + void serverEnabledPropertyDefaultsToFalse() { + A2AProperties properties = new A2AProperties(); + assertFalse(properties.isEnabled()); + } + @Test void whenEnabledTrueThenA2ABeansCreated() { new WebApplicationContextRunner()