Spring 6.x HTTP interface 使用说明

https://docs.spring.io/spring-framework/reference/6.1/integration/rest-clients.html
http-service-client-enhancements)

简介

Spring Framework 6 引入了通过 Java 接口定义 HTTP 服务的能力,该接口的方法使用 @HttpExchange 注解标记。

它的用法看起来特别像Spring Cloud OpenFeign.

下面可以通过访问API: https://api.github.com/repos/spring-projects/spring-framework/milestones ,来展示HTTP interface 的使用方法。

API response :

定义HTTP interface接口

在接口的方法上带上:@GetExchange 接口即可

java 复制代码
// @HttpExchange  (optional)
public interface MilestoneService {
    @GetExchange("/repos/{org}/{repo}/milestones")
    List<Milestone> getMilestones(@PathVariable String org, @PathVariable String repo);
}

使用RestClient

java 复制代码
// Initialize HTTP client
RestClient restClient = RestClient.create("https://api.github.com");

// Create factory for client proxies
HttpServiceProxyFactory proxyFactory = HttpServiceProxyFactory.builder()
        .exchangeAdapter(RestClientAdapter.create(restClient))
        .build();

// Create client proxy -- 创建MilestoneService
MilestoneService client = proxyFactory.createClient(MilestoneService.class);

// Use proxy for HTTP requests
List<Milestone> milestones = client.getMilestones("spring-projects", "spring-framework");
System.out.println("Found "+milestones.size()+" milestones, [0]:"+ milestones.get(0));

执行结果

复制代码
Found 7 milestones, [0]:Milestone[url=https://api.github.com/repos/spring-projects/spring-framework/milestones/205, htmlUrl=null, labelsUrl=null, id=3960978, nodeId=null, number=205, title=General Backlog, description=Unordered set of issues that may get addressed in a future release., creator=Creator[login=spring-projects-issues, id=16028288, nodeId=null, avatarUrl=null, gravatarId=null, url=https://api.github.com/users/spring-projects-issues, htmlUrl=null, followersUrl=null, followingUrl=null, gistsUrl=null, starredUrl=null, subscriptionsUrl=null, organizationsUrl=null, reposUrl=null, eventsUrl=null, receivedEventsUrl=null, type=User, userViewType=null, siteAdmin=false], openIssues=0, closedIssues=0, state=open, createdAt=null, updatedAt=null, dueOn=null, closedAt=null]

配置

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

java 复制代码
@Bean
MilestoneService milestoneService(HttpServiceProxyFactory factory) {
    return factory.createClient(MilestoneService.class);
}

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

// More client beans

@Bean
HttpServiceProxyFactory proxyFactory(RestClient.Builder clientBuilder) {
    RestClient client = clientBuilder.baseUrl("https://api.github.com").build();
    return HttpServiceProxyFactory.builderFor(RestClientAdapter.create(client)).build();
}

HTTP Service Registry(Spring Framework 7)

为了解决 Spring 框架中手动注册客户端代理的挑战,Spring Framework 7 引入了一个额外的注册层,该注册层位于 HttpServiceProxyFactory 之上,提供以下功能:

  • 配置模型: 用于注册 HTTP 接口并初始化 HTTP 客户端基础设施。
  • 透明创建和注册: 自动将客户端代理创建并注册为 Spring Bean。
  • 统一访问: 通过 HttpServiceProxyRegistry 统一访问所有客户端代理。

这意味着,开发者现在可以通过配置或自动扫描的方式来声明 HTTP 服务,而无需手动编写工厂方法。

在配置模型中,HTTP服务是按group组织的,其中同一group共享相同HTTP客户端配置的HTTP服务,以及由此产生的客户端实例。

声明式-通过@ ImportHttpServices
java 复制代码
//指定classes
@ImportHttpServices(group = "github", types = {MilestoneService.class, ... })
@ImportHttpServices(group = "stackoverflow", types = {QuestionService.class, ... })
@Configuration
public class DemoConfig {
}

//或者指定package
@ImportHttpServices(group = "github", basePackages = ""client.github")
@ImportHttpServices(group = "stackoverflow", basePackages = "client.stackoverflow")
@Configuration
public class DemoConfig {
}

HTTPgroup默认配置RestClient ,可通过注释的clientType 属性切换为WebClient

编程式注册 - AbstractHttpServiceRegistrar
java 复制代码
public class CustomHttpServiceRegistrar extends AbstractHttpServiceRegistrar { 

    @Override
    protected void registerHttpServices(GroupRegistry registry, AnnotationMetadata metadata) {
        registry.forGroup("github").detectInBasePackages("client.github);
        // more registrations...
    }
}

@Configuration
@Import(CustomHttpServiceRegistrar.class) 
public class ClientConfig {
}

HTTP Client Initialization(Spring Framework 7 & spring-boot4)

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

java 复制代码
@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"));
    };
}

spring-boot4可以通过配置自动的为webclient,restclient进行配置

java 复制代码
# Global, applies to all groups
spring.http.client.service.read-timeout=2s

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

# Stackoverflow group
spring.http.client.service.group.stackoverflow.base-url=https://api.stackexchange.com

RestClient 详细用法

参数与注解

java 复制代码
mport org.springframework.web.service.annotation.*;

@HttpExchange(url = "https://api.example.com")
public interface UserService {

    @GetExchange("/users/{id}")
    User getUser(@PathVariable("id") Long id);

    @PostExchange("/users")
    User createUser(@RequestBody User user);

    @PutExchange("/users/{id}")
    void updateUser(@PathVariable("id") Long id, @RequestBody User user);

    @DeleteExchange("/users/{id}")
    void deleteUser(@PathVariable("id") Long id);
}

这就像声明一个 REST API 客户端。Spring 会根据接口定义自动生成代理实现。

认证与授权(Auth)

手动申明Token
java 复制代码
RestClient restClient = RestClient.builder()
        .baseUrl("https://api.example.com")
        .defaultHeaders(headers -> headers.setBasicAuth("user", "password"))
        .build();
拦截器(Interceptor) 自动注入 Token
java 复制代码
RestClient restClient = RestClient.builder()
        .requestInterceptor((req, body, exec) -> {
            req.getHeaders().setBearerAuth(getJwtToken());
            return exec.execute(req, body);
        })
        .build();

异常处理机制

异常类型

Spring 6 的 RestClient 在执行 HTTP 调用时,如果遇到错误(如 4xx、5xx、网络异常等),

会抛出一系列基于 RestClientException 的异常类型。

复制代码
RestClientException (基础异常)
├── HttpStatusCodeException
│     ├── HttpClientErrorException (4xx 客户端错误)
│     └── HttpServerErrorException (5xx 服务端错误)
└── ResourceAccessException (网络或连接错误)
常见使用示例
java 复制代码
try {
    User user = restClient.get()
            .uri("/users/{id}", 1001)
            .retrieve()
            .body(User.class);
} catch (HttpClientErrorException.NotFound ex) {
    log.warn("用户未找到: {}", ex.getMessage());
} catch (HttpServerErrorException ex) {
    log.error("服务器内部错误: {}", ex.getMessage());
} catch (ResourceAccessException ex) {
    log.error("网络异常,请检查连接: {}", ex.getMessage());
} catch (RestClientException ex) {
    log.error("其他 RestClient 异常: {}", ex.getMessage());
}
自定义异常处理

你可以自定义一个 RestClient.ResponseErrorHandler

java 复制代码
RestClient restClient = RestClient.builder()
        .baseUrl("https://api.example.com")
        .defaultStatusHandler(HttpStatusCode::isError, (req, res) -> {
            String body = res.getBody().readAllBytes().toString();
            throw new CustomApiException("API Error: " + body);
        })
        .build();

RestTemplate vs RestClient vs WebClient

阶段 客户端 特点
Spring 3.x RestTemplate 简单易用,但同步阻塞、API 设计陈旧
Spring 5.x WebClient 基于 Reactor 的响应式客户端,异步非阻塞
Spring 6.x RestClient 同步客户端,现代化 API,线程安全,可测试性强
相关推荐
云烟成雨TD14 小时前
Spring AI Alibaba 1.x 系列【6】ReactAgent 同步执行 & 流式执行
java·人工智能·spring
Java成神之路-15 小时前
SpringMVC 响应实战指南:页面、文本、JSON 返回全流程(Spring系列13)
java·spring·json
砍材农夫15 小时前
spring-ai 第六模型介绍-聊天模型
java·人工智能·spring
云烟成雨TD16 小时前
Spring AI Alibaba 1.x 系列【5】ReactAgent 构建器深度源码解析
java·人工智能·spring
Flittly17 小时前
【SpringAIAlibaba新手村系列】(15)MCP Client 调用本地服务
java·笔记·spring·ai·springboot
Flittly17 小时前
【SpringAIAlibaba新手村系列】(14)MCP 本地服务与工具集成
java·spring boot·笔记·spring·ai
mfxcyh18 小时前
基于xml、注解、JavaConfig实现spring的ioc
xml·java·spring
Flittly18 小时前
【SpringAIAlibaba新手村系列】(13)Tool Calling 函数工具调用技术
java·spring boot·spring·ai
xdscode18 小时前
Spring 依赖注入方式全景解析
java·后端·spring