HTTP 服务客户端增强功能

工程 | Rossen Stoyanchev | 2025年9月23日 | ...

Road to GA 系列的第三篇博客文章中,我们重点介绍了 Spring 组合中针对 将于 11 月发布的下一个主要版本 的主要功能,我们将探讨 HTTP 服务客户端的新功能,这是多个 Spring 项目的协作成果。

引言

Spring Framework 6 引入了通过带有 @HttpExchange 注解方法的 Java 接口定义 HTTP 服务的能力。例如

public interface MilestoneService {

    @GetExchange("/repos/{org}/{repo}/milestones")
    List<Milestone> getMilestones(@PathVariable String org, @PathVariable String repo);

}

在客户端,您可以从接口生成代理,以执行 HTTP 请求,如下所示

// Initialize HTTP client
RestClient restClient = RestClient.create("https://api.github.com");

// Create factory for client proxies
HttpServiceProxyFactory proxyFactory = HttpServiceProxyFactory.builder()
        .exchangeAdapter(RestClientAdapter.create(restClient))
        .build();

// Create client proxy
MilestoneService client = proxyFactory.createClient(MilestoneService.class);

// Use proxy for HTTP requests
List<Milestone> milestones = client.getMilestones(“spring-projects”, “spring-framework”);

在服务器端,如果 HTTP 服务是您的,@Controller 类可以实现相同的接口来处理请求。

HTTP 服务客户端支持功能强大、表达力强且易于使用。它允许一个团队掌握 REST API 的工作方式、哪些部分与客户端应用程序相关、要创建哪些输入和输出类型、需要哪些端点方法签名、需要哪些 Javadoc 等知识。生成的 Java API 可以指导开发人员,并可立即使用。

这些模式长期以来与 Spring Cloud OpenFeign 一起使用,现在已可供所有 Spring Framework 6+ 应用程序与 RestClientRestTemplateWebClient 一起使用。HTTP 服务客户端支持在 6.x 时间线中经历了大量由反馈驱动的演变,但一个主要挑战仍然存在。

配置开销

创建 HttpServiceProxyFactory 并使用它来创建一两个或三个客户端代理是微不足道的,但随着数量的增长,这变得重复且麻烦,尤其因为客户端代理通常声明为 Spring Bean。例如,考虑以下情况:

@Bean
MilestoneService milestoneService(HttpServiceProxyFactory factory) {
    return factory.createClient(MilestoneService.class);
}

@Bean
ReleaseService releaseService(HttpServiceProxyFactory factory) {
    return factory.createClient(ReleaseService.class);
}

// More client beans

@Bean
HttpServiceProxyFactory proxyFactory(RestClient.Builder clientBuilder) {
    RestClient client = clientBuilder.baseUrl("https://api.github.com").build();
    return HttpServiceProxyFactory.builderFor(RestClientAdapter.create(client)).build();
}

REST API 暴露了许多细粒度端点。GitHub API 有数十个,甚至数百个,虽然您肯定不需要所有这些,但根据实际需要,很容易最终得到至少十几个或更多。

此外,与多个 REST API 集成是很常见的,这意味着更多的接口以及更复杂的底层 HTTP 客户端配置。

HTTP 服务注册

为了解决这个挑战,Spring Framework 7HttpServiceProxyFactory 之上引入了一个额外的注册层,提供以下功能:

  • 用于注册 HTTP 接口和初始化 HTTP 客户端基础设施的配置模型
  • 透明地创建和注册客户端代理作为 Spring Bean
  • 通过 HttpServiceProxyRegistry 访问所有客户端代理

在配置模型中,HTTP 服务按组组织,其中组只是一组共享相同 HTTP 客户端配置和结果客户端实例的 HTTP 服务。

目前,有两种声明 HTTP 服务的方式。

声明式注册

声明 HTTP 服务组的一种方式是通过 @ImportHttpServices 注解(Spring Framework 7 中新增)。

您可以使用它按组手动列出 HTTP 服务

@ImportHttpServices(group = "github", types = {MilestoneService.class, … })
@ImportHttpServices(group = "stackoverflow", types = {QuestionService.class, … })
@Configuration
public class DemoConfig {
}

或者让它们在基础包下被检测到

@ImportHttpServices(group = "github", basePackages = “"client.github")
@ImportHttpServices(group = "stackoverflow", basePackages = “client.stackoverflow”)
@Configuration
public class DemoConfig {
}

HTTP 服务组默认配置为 RestClient,但您可以通过注解的 clientType 属性切换到 WebClient

编程式注册

如果您需要对过滤或其他注册逻辑有更多控制,您还可以分两步以编程方式声明 HTTP 服务。

首先,创建一个声明 HTTP 服务组的注册器

public class CustomHttpServiceRegistrar extends AbstractHttpServiceRegistrar { 

    @Override
    protected void registerHttpServices(GroupRegistry registry, AnnotationMetadata metadata) {
        registry.forGroup("github").detectInBasePackages(“client.github);
        // more registrations…
    }
}

然后导入注册器

@Configuration
@Import(CustomHttpServiceRegistrar.class) 
public class ClientConfig {
}

请注意,声明式和编程式注册都依赖于 ImportBeanDefinitionRegistrar,它在 Spring 配置生命周期的早期阶段,即 Bean 定义级别介入。这使得客户端代理 Bean 可用于依赖注入,并有助于避免生命周期问题。

HTTP 客户端初始化

一旦声明了 HTTP 服务组,剩下的就是为每个组配置 HTTP 客户端。您可以使用 HttpServiceGroupConfigurer Bean 方法来完成此操作。例如:

@Bean
RestClientHttpServiceGroupConfigurer groupConfigurer() {
    return groups -> {

        groups.filterByName("github").forEachClient((_, builder) ->
                builder.baseUrl("https://api.github.com"));

        groups.filterByName("stackoverflow").forEachClient((_, builder) ->
                builder.baseUrl("https://api.stackexchange.com?site=stackoverflow"));
    };
}

您可以拥有任意数量的 HTTP 服务组配置器,这些配置器可以是应用程序或框架拥有的。

例如,Spring Boot 4.0 通过其 RestClientWebClient 自动配置透明地为每个组应用 HTTP 客户端构建器的初始化。此外,它还提供了按组配置 HTTP 客户端属性的支持

# Global, applies to all groups
spring.http.client.service.read-timeout=2s

# GitHub group
spring.http.client.service.group.github.base-url=https://api.github.com

# Stackoverflow group
spring.http.client.service.group.stackoverflow.base-url=https://api.stackexchange.com

Spring Cloud 2025.1 为 HTTP 服务组提供了负载均衡断路器的透明支持。

Spring Security 7.0 为 HTTP 服务组提供了 OAuth 支持,它会在 @HttpExchange 方法上检测 @ClientRegistrationId 注解。与 spring-security#17940 相关的额外身份验证支持正在考虑中。

总结

新的 HTTP 服务注册表允许应用程序声明 HTTP 服务并配置底层客户端基础设施,而框架则负责其余部分。它还是一个可扩展的机制,用于提供功能强大、开箱即用的 HTTP 客户端初始化功能。

我们知道在这个主题上存在各种各样的场景和观点,也有着丰富的 OpenFeign 使用经验。与此同时,我们将一项功能引入 Spring Framework,这是一个以全新视角重新审视它并提供更精简和广泛有用的功能的机会。我们需要您尝试我们提出的方案,并告诉我们它在您的具体场景中如何工作。

示例

获取 Spring 新闻通讯

通过 Spring 新闻通讯保持联系

订阅

领先一步

VMware 提供培训和认证,助您加速进步。

了解更多

获得支持

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件,只需一份简单的订阅。

了解更多

即将举行的活动

查看 Spring 社区所有即将举行的活动。

查看所有