Spring 7 声明式 HttpClient:FeignClient 的最佳替代
Spring 7 引入了原生的声明式 HttpClient,基于 RestClient 和 WebClient 构建。它使用熟悉的注解如 @GetExchange 和 @PostExchange,与控制器中的 @GetMapping 类似。通过接口定义出站 HTTP 请求,无需手动实现。
关键接口示例如下:
public interface UserClient {
@GetExchange("/api/users/{id}")
UserResponse getUser(
@PathVariable("id") Long id,
@RequestParam(name = "includeDetails", defaultValue = "false") boolean includeDetails,
@RequestHeader("Authorization") String authToken,
@RequestHeader("X-Request-Id") String requestId
);
@PostExchange("/api/users")
UserResponse createUser(
@RequestBody @Valid CreateUserRequest request,
@RequestHeader("Authorization") String authToken
);
@PatchExchange("/api/users/{id}")
void updateUserEmail(
@PathVariable("id") Long id,
@RequestParam("email") String email,
@RequestHeader("Authorization") String authToken
);
}
HttpClient 开箱即用支持可观测性,且无需 Spring Cloud——不同于 FeignClient,后者自 2022 年起进入维护模式。
与 OpenAPI Generator 集成
要自动化生成客户端,使用 openapi-generator 结合 spring-http-interface 库。
Maven 插件配置:
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>${openapi-generator.version}</version>
<executions>
<execution>
<id>generate-weather-client</id>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>${project.basedir}/src/main/resources/openapi/external/weather-api.yaml</inputSpec>
<generatorName>spring</generatorName>
<library>spring-http-interface</library>
<output>${project.build.directory}/generated-sources/openapi-client</output>
<apiPackage>ulllie.exchange.openapi.gen.client.api</apiPackage>
<modelPackage>ulllie.exchange.openapi.gen.client.model</modelPackage>
<generateApis>true</generateApis>
<generateModels>true</generateModels>
<generateSupportingFiles>false</generateSupportingFiles>
<configOptions>
<useSpringBoot4>true</useSpringBoot4>
<useJackson3>true</useJackson3>
<interfaceOnly>true</interfaceOnly>
<skipDefaultInterface>true</skipDefaultInterface>
<useBeanValidation>true</useBeanValidation>
<annotationLibrary>none</annotationLibrary>
<serializationLibrary>jackson</serializationLibrary>
<useTags>true</useTags>
</configOptions>
</configuration>
</execution>
</executions>
</plugin>
这会生成类似这样的接口:
public interface OpenMeteoApi {
@HttpExchange(method = "GET", value = "/v1/forecast")
ResponseEntity<ForecastResponse> getForecast(
@RequestParam("latitude") Double latitude,
@RequestParam("longitude") Double longitude
);
}
服务连接与配置
通过 import 配置:
@Configuration @ImportHttpServices(group = "weather", types = OpenMeteoApi.class)
public class WeatherRestClientConfig implements RestClientHttpServiceGroupConfigurer {
@Override
public void configureGroups(Groups<RestClient.Builder> groups) {
groups.filterByName("weather").forEachClient(
($, builder) -> builder.baseUrl("https://api.weather.com")
);
}
}
错误处理通过 defaultStatusHandler 配置:
builder.baseUrl(properties.baseUrl())
.defaultStatusHandler(
status -> status.is4xxClientError() || status.is5xxServerError(),
errorHandler
);
自定义 HttpErrorHandler 可针对 4xx/5xx 状态码定制逻辑,支持 Resilience4j 集成,或使用 @ControllerAdvice。
核心特性:
- 拦截器配置
- Resilience4j 集成
- ResponseSpec 级错误处理
RestClient 与 WebClient 在 Project Loom 下的对比
启用虚拟线程(spring.threads.virtual.enabled=true)后,阻塞式 RestClient 提供 I/O 可扩展性,无需响应式编程。
优势:
- 易调试的命令式代码
- 无响应式管道
- I/O 密集任务性能媲美 WebClient
响应式场景下,选择 WebClient.Builder 生成 Mono/Flux 签名。
验证挑战与解决方案
Bean Validation 不会递归嵌套对象,因为生成器跳过 @Valid。
解决方案:
- OpenAPI 中的 x-field-extra-annotation:
@jakarta.validation.Valid——需修改规范。 - AlwaysTraversableResolver ——全局遍历整个对象树,但易引发循环和性能问题。
- 自定义 Mustache 模板 ——生成时自动添加 @Valid。
额外提示:生成 Kotlin 数据类以确保空安全。
data class ForecastResponse(
val temperature: Double,
val windSpeed: Double?
)
依赖:kotlin-stdlib + jackson-module-kotlin。空值违规时快速失败反序列化。
核心要点
- HttpClient 是 Spring Framework 原生功能,无需 Spring Cloud 依赖。
- OpenAPI generator 结合
spring-http-interface生成即用接口。 - 虚拟线程简化 I/O 处理,无需响应式。
- Kotlin 数据类及早捕获空值问题。
- 错误处理和验证完全可定制。
— Editorial Team
暂无评论。