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