Spring框架7的HTTP服务客户端增强功能解析

本文详细介绍了Spring Framework 7中HTTP服务客户端的重大改进,包括新的服务注册机制、配置简化方法以及与Spring Boot、Spring Cloud的深度集成,帮助开发者更高效地构建HTTP服务客户端。

HTTP服务客户端增强功能

在通往GA系列的第三篇博客文章中,我们将重点介绍Spring产品组合中将在11月发布的下一个主要版本中的主要功能,特别是HTTP服务客户端的新功能,这是多个Spring项目协作的成果。

介绍

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

1
2
3
4
5
6
public interface MilestoneService {

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

}

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// 初始化HTTP客户端
RestClient restClient = RestClient.create("https://api.github.com");

// 创建客户端代理的工厂
HttpServiceProxyFactory proxyFactory = HttpServiceProxyFactory.builder()
        .exchangeAdapter(RestClientAdapter.create(restClient))
        .build();

// 创建客户端代理
MilestoneService client = proxyFactory.createClient(MilestoneService.class);

// 使用代理进行HTTP请求
List<Milestone> milestones = client.getMilestones("spring-projects", "spring-framework");

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

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

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

配置开销

创建HttpServiceProxyFactory并使用它创建一个、两个或三个客户端代理很简单,但随着数量的增加,这变得重复和繁琐,特别是因为客户端代理通常声明为Spring bean。例如考虑:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
@Bean
MilestoneService milestoneService(HttpServiceProxyFactory factory) {
    return factory.createClient(MilestoneService.class);
}

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

// 更多客户端bean

@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 7在HttpServiceProxyFactory之上引入了一个额外的注册表层,提供以下功能:

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

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

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

声明式注册

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

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

1
2
3
4
5
@ImportHttpServices(group = "github", types = {MilestoneService.class,  })
@ImportHttpServices(group = "stackoverflow", types = {QuestionService.class,  })
@Configuration
public class DemoConfig {
}

或者在基础包下检测它们:

1
2
3
4
5
@ImportHttpServices(group = "github", basePackages = "client.github")
@ImportHttpServices(group = "stackoverflow", basePackages = "client.stackoverflow")
@Configuration
public class DemoConfig {
}

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

编程式注册

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

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

1
2
3
4
5
6
7
8
public class CustomHttpServiceRegistrar extends AbstractHttpServiceRegistrar { 

    @Override
    protected void registerHttpServices(GroupRegistry registry, AnnotationMetadata metadata) {
        registry.forGroup("github").detectInBasePackages("client.github");
        // 更多注册...
    }
}

然后导入注册器:

1
2
3
4
@Configuration
@Import(CustomHttpServiceRegistrar.class) 
public class ClientConfig {
}

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

HTTP客户端初始化

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@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通过其RestClient和WebClient自动配置,透明地应用每个组的HTTP客户端构建器初始化。此外,它还提供按组的HTTP客户端属性支持:

1
2
3
4
5
6
7
8
# 全局,适用于所有组
spring.http.client.service.read-timeout=2s

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

# Stackoverflow组
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时,这是一个以新视角重新审视它,并提供更最小化和广泛有用的东西的机会。我们需要您尝试我们提出的方案,并让我们知道它在您的具体场景中如何工作。

示例

  • GitHub和StackOverflow客户端
  • Spring Cloud
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计