还在为调用第三方接口
而编写冗长的 RestTemplate 或 WebClient 代码吗?
Spring Boot 4 中提供了声明式 HTTP 客户端
它让你能够像定义 Controller 一样定义客户端接口
将调用逻辑从繁琐的模板代码中解放出来
使代码更加简洁、直观和易于维护

假设我们要调用一个创建商品的接口
POST /api/goods
传入一个 Goods 对象
发出请求
一般来说是这样的
// 商品实体
public class Goods {
private Integer id;
private String name;
private Double price;
// getter and setter
}
// 业务类
@Service
public class BizService {
@Autowired
private RestTemplate restTemplate;
private static final String GOODS_API_URL = "http://localhost:8888/api/goods";
public Goods createGoodsSimple(Goods goods) {
return restTemplate.postForObject(GOODS_API_URL, goods, Goods.class);
}
很明显,我们分了好几步
其中隐性的环节
包括:需要手动处理 URL 拼接、请求头设置、请求体封装以及响应转换
有没有更简单的方式
就像调用本地类一样呢?
以前可以使用 Feign 比较接近的实现我们的目标
在4.0里可以尝试一些新方法
新版本中,只需要定义一个 Java 接口
并使用特定的注解来描述 HTTP 请求的细节
Spring Boot 会在运行时自动为这个接口生成一个通过动态代理类
我们只需像使用普通 Bean 一样注入并使用它即可

让我们通过一个例子
解决之前相同的问题
@HttpExchange(url = "http://localhost:8888/api/goods")
public interface GoodsClient {
// 对应 GET /api/goods/{goodsId}
@GetExchange(url = "/{goodsId}")
Goods getById(@PathVariable("goodsId") Integer goodsId);
// 对应 POST /api/goods
@PostExchange
Goods create(@RequestBody Goods goods);
// 商品实体类
record Goods(Integer id, String name, Double price) {}
}
声明后,要告知Spring容器启用他
在启动类增加扫描
@SpringBootApplication
@ImportHttpServices(basePackages = "com.liaobuqi.client") // 扫描指定包下的客户端接口
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
之后就可以像普通 Bean 一样使用
在任何 @Component、@Service 或 @Controller 中
直接注入并使用这个 GoodsClient
@RestController
public class OrderController {
@Autowired
private GoodsClient goodsClient;
@GetMapping("/order/goods/{goodsId}")
public GoodsClient.Goods getGoodsInfo(@PathVariable Integer goodsId) {
// 直接调用,就像调用本地方法一样!
return goodsClient.getById(goodsId);
}
}
在新的写法中
完全感受不出
我调用的是本地类
还是远程服务
整个调用过程没有任何关于 URL 拼接、请求发送和响应解析的样板代码
刚才是个比较简单的示例
在实际工作中
我们可以实现一些更优雅的写法
例如前面客户端接口的注解里
写了网址
其实可以独立配置
spring:
http:
serviceclient:
goods: # 对应客户端接口的类名(首字母小写)
base-url: "http://goods-service" # 统一配置基础 URL
connect-timeout: 3s # 连接超时
read-timeout: 5s # 读取超时
接口上的 @HttpExchange(url = "...") 就可以省略
或者只保留相对路径
声明式客户端提供了灵活的错误处理方式
当收到非 2xx 状态码时
默认会抛出 HttpClientException 或 HttpServerErrorException
可以配置一个全局的 RestClient Bean
并为其添加状态码处理器
@Configuration
public class ClientConfig {
@Bean
public RestClient restClient() {
return RestClient.builder()
.defaultStatusHandler(HttpStatusCode::is4xxClientError, (request, response) -> {
throw new IllegalArgumentException("客户端错误: " + response.getStatusCode());
})
.defaultStatusHandler(HttpStatusCode::is5xxServerError, (request, response) -> {
throw new RuntimeException("服务端错误");
})
.build();
}
}
除此之外
我们经常需要进行统一header等处理
可以建立全局拦截器
@Configuration
public class InterceptorConfig {
@Bean
public RestClient restClient() {
return RestClient.builder()
.requestInterceptor((request, body, execution) -> {
// 请求拦截:添加统一的请求头
request.getHeaders().add("X-Request-ID", java.util.UUID.randomUUID().toString());
return execution.execute(request, body);
})
.build();
}
}
如果需要添加请求头、记录日志、链路追踪等等
都可以用这个方法
Spring Boot 4 的声明式 HTTP 客户端是一个革命性的改进
它将微服务间的调用体验提升到了一个新的高度
原生支持,无需额外依赖
接口即契约,代码清晰易懂
如果你正在使用或计划升级到 Spring Boot 4
可以尝试新体验
能让你的远程调用代码变得前所未有的优雅

