Boot4升后HttpServiceClient怎么换

升到 Spring Boot 4 之后,接口上的 @HttpServiceClient("echo") 突然编译不过,或者启动后注入不到 HTTP Service Bean------这不是依赖少了,而是 Framework 7 / Boot 4 已删掉该注解,也不再扫描注册 。本文按官方 rest-client 文档与 issue #47179,把怎么改成 @ImportHttpServices、组级属性怎么配、怎么验 Bean 说清楚。

为什么 Boot4 没有 HttpServiceClient 了?

Spring Boot issue #47179 写得很直:@HttpServiceClient 的支持已在 Spring Framework 7.0 拿掉 ,并在 Boot 侧 #47123 移除;Boot 4.0 不再扫描 HTTP Service 接口客户端。组级 configuration properties 与 group configurer 还在 ,变的是「谁把接口注册成 Bean」------从注解扫描,改成你显式 @ImportHttpServices。

常见现象对照:

现象 大致原因
@HttpServiceClient 编译找不到类型 Framework 7 已删除该注解
升 4.0 后接口 Bean 注入失败 Boot 不再扫描;未写 @ImportHttpServices
接口上还写死 base URL 应迁到 spring.http.serviceclient.<group>.*
多组只改了一处 Import 多组需重复注解,每组各自 group

决策流可以先看一张图:

图:Boot3 扫描注册 → Boot4 删注解 → 显式 ImportHttpServices → 组配置 → 验收。

边界钉死:不是「注解改名还能自动扫」,而是显式导入;故意不 Import,容器里就不应有对应 Bean。

同系列升级里,WebClient starter 模块化见 168121729,TestRestTemplate 换法见 168206634,Undertow 迁移见 168409414;本稿只盯 HTTP Service 接口客户端注解怎么换,不把那些主题再写一遍。

官方硬点以 rest-client 文档「HTTP Service Interface Clients」 与 #47179 为准。Migration Guide wiki 没有单独的 HttpServiceClient 专节,升级时不要指望在那里逐条对照。

ImportHttpServices 怎么写?

包名固定为:org.springframework.web.service.registry.ImportHttpServices(Framework 7.0 起)。通常挂在主类或配置类上,属性常用:

  • group :组名,默认 "default"
  • types / value:直接列出接口类型
  • basePackages / basePackageClasses :按包扫描接口(仍是你声明的扫描范围,不是 Boot 默认全包扫描)
  • clientType :后端客户端类型;默认 UNSPECIFIED → 用 RestClient

单组:列类型

java 复制代码
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.service.registry.ImportHttpServices;

@SpringBootApplication
@ImportHttpServices(group = "echo", types = EchoClient.class)
public class Application {
  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }
}

接口本身继续用 @HttpExchange / @GetExchange 等声明路径,不要 再挂已删除的 @HttpServiceClient:

java 复制代码
import org.springframework.web.service.annotation.GetExchange;
import org.springframework.web.service.annotation.HttpExchange;

@HttpExchange("/api")
public interface EchoClient {
  @GetExchange("/ping")
  String ping();
}

按包导入

接口多、不想在注解里逐个列类型时:

java 复制代码
@ImportHttpServices(group = "echo", basePackages = "com.example.clients.echo")

注意:basePackages 只扫你写出的包;没有「Boot 4 默认全工程扫描 HTTP Service」这回事。漏写 Import,就不会有 Bean。

多组:重复注解

#47179 明确:多组用重复 的 @ImportHttpServices(group=..., types=...) 覆盖原先多个 @HttpServiceClient("group") 的场景:

java 复制代码
@SpringBootApplication
@ImportHttpServices(group = "echo", types = EchoClient.class)
@ImportHttpServices(group = "billing", types = BillingClient.class)
public class Application { }

每组各自对应一份 spring.http.serviceclient.<group>.* 配置,不要共用错组名。

spring.http.serviceclient 配什么?

组级前缀是 spring.http.serviceclient.<group>.*。文档示例方向包括:base URL、默认头、API versioning、redirect、connection/read timeouts、SSL bundles。升级时最常改的是这三项:

yaml 复制代码
spring:
  http:
    # 全局:作用于所有 HTTP clients;组级可覆盖
    clients:
      connect-timeout: 1s
    serviceclient:
      echo:
        base-url: https://echo.example.com
        connect-timeout: 2s
        read-timeout: 2s
      billing:
        base-url: https://billing.example.com
        read-timeout: 5s
      # 未指定 group 时,客户端归入名为 default 的组
      default:
        base-url: https://default.example.com

实践要点:

  1. 接口上去掉写死的绝对 URL ,改走组级 base-url,环境差异用 profile / 配置中心覆盖。
  2. 全局 用 spring.http.clients.*(例如全局 connect-timeout);组级 用 spring.http.serviceclient.<group>.* 覆盖。文档示例就是:全局 1s,echo 组 connect/read 2s。
  3. 未写 group 时,客户端进 "default" 组------配置键也要写成 spring.http.serviceclient.default.*,别找错前缀。

属性与注册的关系可以再压成一张图:

图:Import + serviceclient 属性汇入 Bean;全局 clients 可被组级覆盖;configurer 可选。

默认 RestClient,什么时候才用 WebClient?

Framework 侧:clientType 默认 UNSPECIFIED,使用 RestClient 。要走 WebClient,需显式改成 WEB_CLIENT(并准备好对应栈),例如:

java 复制代码
@ImportHttpServices(
    group = "echo",
    types = EchoClient.class,
    clientType = ImportHttpServices.ClientType.WEB_CLIENT
)

Boot 侧提供两类 group configurer Bean(声明即由 auto-config 应用):

  • RestClientHttpServiceGroupConfigurer ------ RestClient 后端
  • WebClientHttpServiceGroupConfigurer ------ WebClient 后端

Starter 对照(Migration Guide starters 表):spring-boot-starter-restclient(命令式 RestClient/RestTemplate)、spring-boot-starter-webclient(响应式 WebClient)。生产侧 starter 怎么换,见已发文 168121729;不要 编造「classpath 里有 WebFlux 就自动把所有 HTTP Service 切成 WebClient」------默认仍是 RestClient,切 WebClient 要显式 clientType + 对应 starter/configurer。

可选定制(RestClient 后端加默认头,一句示例即可):

java 复制代码
@Bean
RestClientHttpServiceGroupConfigurer echoHeaders() {
  return groups -> groups.filterByName("echo")
      .forEachClient((group, builder) ->
          builder.defaultHeader("X-Client", "boot4-demo"));
}

WebClient 后端同理换 WebClientHttpServiceGroupConfigurer。超时等组级项继续走 spring.http.serviceclient.* / spring.http.clients.*,不要 在本文里往 WebClient.Builder 上堆 responseTimeout / connectTimeout 写法(既有禁写项)。

怎么确认 HTTP Service Bean 注册成功?

建议按「能注入 → 能打到 URL → 故意失败对照」三步验收,比只看启动日志安心:

1. 正常路径:注入并调用

java 复制代码
@RestController
@RequiredArgsConstructor
class PingController {
  private final EchoClient echoClient;

  @GetMapping("/local-ping")
  String localPing() {
    return echoClient.ping();
  }
}

启动后访问本机入口,确认请求落到 spring.http.serviceclient.echo.base-url 指向的地址(可用 mock server / WireMock,或临时改 base-url 看落点)。

2. 负向对照:故意不 Import

临时去掉 @ImportHttpServices(或去掉某一组),应出现 无对应 Bean (NoSuchBeanDefinitionException 一类)。加回后恢复。这一步专门用来钉死「Boot 4 不再扫描」------避免有人以为「放在 component-scan 包下就会自动注册」。

3. 依赖与后端栈

bash 复制代码
# Maven 示例:确认 restclient / webclient 按你的选型出现
mvn -q dependency:tree -Dincludes=org.springframework.boot:spring-boot-starter-restclient
mvn -q dependency:tree -Dincludes=org.springframework.boot:spring-boot-starter-webclient

默认走 RestClient 时,应能看到 restclient 相关坐标;只有显式 WEB_CLIENT 时才去对齐 webclient starter。测试侧客户端换法见 168206634,与本稿生产 HTTP Service 注册是两条线。

4. 一眼检查表

检查项 期望
源码中 @HttpServiceClient 无(已删)
@ImportHttpServices 有;多组则重复注解
spring.http.serviceclient.<group>.base-url 与组名一致
故意不 Import 注入失败
默认后端 RestClient(未改 clientType)

边界与不要踩的坑

  • 不要把 @HttpServiceClient 写成仍可用 ,也不要假设「以后官方会加回同名注解」。#47179 只提到若类似能力有用会收集反馈,不等于承诺加回同名 API。
  • 不要写「扫描默认全开」 。Boot 4 明确不再扫描;必须 @ImportHttpServices(types 或你声明的 basePackages)。
  • Migration Guide 无 HttpServiceClient 专节 ------硬点以 rest-client.html + #47179 为准,别谎称 wiki 专节逐条列出。
  • 禁写 WebClient.Builder 上的 responseTimeout / connectTimeout 迁移教条;超时用文档中的 spring.http.clients.* / spring.http.serviceclient.*。
  • 勿抢主线 :Undertow 没了怎么迁见 168409414;WebClient starter、TestRestTemplate 各有专文。本稿不展开 SSRF / InetAddressFilter 长文,也不写活动名。
  • 多模块工程里,Import 往往挂在「组装 HTTP 客户端的配置模块」而不是每个业务 jar 上;升级时全局搜索旧注解名,比只改主类更不容易漏组。

从 Boot3 迁过来的改法顺序

建议按固定顺序改,避免「注解删了、配置还指着旧组名」或「配置改了、Import 漏了」:

  1. 全仓库搜索 @HttpServiceClient、旧 import。业务模块、内部 starter、示例工程一并扫。
  2. 删掉旧注解 ,在组装客户端的配置类或主类上补 @ImportHttpServices;多组就写多条。
  3. 把写死在接口/注解上的绝对 URL 挪走 ,落到 spring.http.serviceclient.<group>.base-url;路径片段仍留在 @HttpExchange。
  4. 对齐组名 :旧 @HttpServiceClient("echo") 的字符串,就是新注解 group = "echo",也是配置里的 echo 段。改名要三处一起改。
  5. 确认后端栈 :多数工程保持默认 RestClient + spring-boot-starter-restclient;只有明确要响应式客户端时再设 clientType = WEB_CLIENT 并加 webclient starter。
  6. 跑正负向验收:能注入调用;去掉 Import 应失败;改 base-url 看落点。

Boot 3 常见「接口上挂 @HttpServiceClient、指望组件扫描自动注册」在 Boot 4 整条失效。迁移 MR 里不妨把「搜索结果截图 / 旧注解清零」写进描述,减少漏模块。

若你们以前把 URL 写在接口常量或自定义注解里,升级时容易只改一处。更稳妥的是:注册看 Import,地址看 serviceclient 属性,路径看 HttpExchange------三层职责分开,环境差异只动配置。

旧写法 → 新写法对照

Boot 3 方向 Boot 4 对应
接口或配置上的 @HttpServiceClient("echo") 删除;改为 @ImportHttpServices(group="echo", types=...)
依赖组件扫描自动发现接口 必须显式 Import;可用 basePackages 限定范围
多组多个 @HttpServiceClient 多条可重复的 @ImportHttpServices
组 URL / 超时(若已用 serviceclient 属性) 前缀仍为 spring.http.serviceclient.<group>.*(保留并核对)
未分组客户端 归入 default 组;配置键用 ...serviceclient.default.*

这张表可以贴进升级手册:评审时按行勾,比口头说「按文档改」更不容易漏。

配置中心与多环境注意点

spring.http.serviceclient.* 经常不只在仓库 yml 里,还在 Nacos、K8s ConfigMap、启动参数里。只改 Git、不改运行时配置,会出现「本地能调、预发仍打到旧域名」的漂移。建议:

  • 每个环境一份组级 base-url(或占位符 + 环境变量),组名保持一致 ,避免 profile 里组名拼错导致落到空配置的 default。
  • 超时先用文档示例量级做冒烟,再按下游 SLA 调;全局 spring.http.clients.* 适合「全集群统一下限」,组级适合「某个慢下游单独放宽」。
  • 新增一组客户端时,先加 Import,再加配置,最后写注入点------顺序反了,本地用假配置也能「看起来能启动」,一到联调才发现 Bean 都没有。

HTTP Service 这条适合小步 MR:先编译通过并完成 Import,再单独收拢 URL 与超时,别和一个 MR 里混进 Undertow / Jackson(见系列内链)。

FAQ

为什么 Boot4 没有 HttpServiceClient 了?

Framework 7 删除该注解,Boot 4(#47123 / #47179)不再扫描注册。改用 @ImportHttpServices,组配置与 group configurer 仍保留。

ImportHttpServices 怎么写多组?

对每个组写一条 @ImportHttpServices(group=..., types=...)(或 basePackages),注解可重复;组名与 spring.http.serviceclient.<group> 对齐。

spring.http.serviceclient 配什么?

至少配 base-url;常用再加 connect-timeout / read-timeout。全局默认走 spring.http.clients.*,组级覆盖。未指定 group 时组名为 default。

接口上的 URL 还要写死吗?

绝对地址建议去掉,改组级 base-url;路径仍留在 @HttpExchange / @GetExchange 上。

怎么确认 HTTP Service Bean 注册成功?

启动后注入调用;再故意去掉 Import 应无 Bean;改 base-url 看请求落点;依赖树确认 RestClient / WebClient 后端与选型一致。

小结 checklist

  • 删除所有 @HttpServiceClient;改 org.springframework.web.service.registry.ImportHttpServices
  • 单组:group + types 或 basePackages;多组:重复注解
  • 配置:spring.http.serviceclient.<group>.base-url|connect-timeout|read-timeout;全局 spring.http.clients.*
  • 默认 RestClient;WebClient 须显式 clientType + 对应 configurer/starter
  • 验收:能注入;故意不 Import 无 Bean;依赖树与 base-url 落点核对
  • 文档锚点:rest-client.html + #47179(勿依赖 Migration Guide 专节)

按「删旧注解 → 显式 Import → 组级属性 → 正负向验收」改完,这类 Boot4 HTTP Service 注册失败通常一次就能闭环,比在依赖里猜「是不是少了某个自动配置」省事。

相关推荐
ZealSinger4 天前
Boot4升后WebClient starter怎么换
starter·webclient·restclient·boot4
ZealSinger9 天前
Boot4升级后MockBean和MockMvc怎么改
mockmvc·boot4·mockbean·mockitobean