Spring Framework HTTP服务客户端详解 Spring Framework HTTP 服务客户端HTTP Service Client/HTTP Interface最初引入自Spring Framework 6.0。基础声明式HTTP接口能力同时支持RestClient/WebClient/RestTemplate三种底层客户端从Spring Framework 7.0开始支持自动Bean注册、服务分组管理。本文详细介绍Spring Framework HTTP服务客户端用法。1. 定义接口可以定义带有HttpExchange注解方法的 Java 接口通过HttpServiceProxyFactory生成动态代理客户端底层可适配RestClient/WebClient/RestTemplate三种客户端。服务端 Controller 也可实现同一套接口统一接口定义前后端对齐。publicinterfaceRepositoryService{GetExchange(/repos/{owner}/{repo})RepositorygetRepository(PathVariableStringowner,PathVariableStringrepo);}接口级别统一配置注解HttpExchange(url/repos/{owner}/{repo},acceptapplication/vnd.waylau.v3json)publicinterfaceRepositoryService{GetExchangeRepositorygetRepository(PathVariableStringowner,PathVariableStringrepo);PatchExchange(contentTypeMediaType.APPLICATION_FORM_URLENCODED_VALUE)voidupdateRepository(PathVariableStringowner,PathVariableStringrepo,RequestParamStringname,RequestParamStringdescription,RequestParamStringhomepage);}2. 创建代理工厂基于 RestClientRestClientrestClientRestClient.create(https://api.waylau.com);RestClientAdapteradapterRestClientAdapter.create(restClient);HttpServiceProxyFactoryfactoryHttpServiceProxyFactory.builderFor(adapter).build();基于 WebClientWebClientwebClientWebClient.create(https://api.waylau.com);WebClientAdapteradapterWebClientAdapter.create(webClient);HttpServiceProxyFactoryfactoryHttpServiceProxyFactory.builderFor(adapter).build();基于 RestTemplateRestTemplaterestTemplatenewRestTemplate();RestTemplateAdapteradapterRestTemplateAdapter.create(restTemplate);HttpServiceProxyFactoryfactoryHttpServiceProxyFactory.builderFor(adapter).build();3. 获取代理客户端并调用RepositoryServiceservicefactory.createClient(RepositoryService.class);Repositoryreposervice.getRepository(spring-projects,spring-framework);4. 方法参数注解支持参数类型/注解说明URI动态覆盖注解上定义的请求地址UriBuilderFactory自定义 URI 模板解析工厂覆盖客户端默认配置HttpMethod动态修改请求方法覆盖注解定义RequestHeader添加请求头支持单值、集合、Map、多值MapPathVariable路径占位变量支持单个值或 Map 批量传入RequestAttribute请求属性仅 RestClient/WebClient 支持RequestBody请求体普通实体或响应式 Publisher(Mono/Flux)RequestParamURL 查询参数表单编码时放入请求体RequestPart多部件上传普通字段、文件Resource、JSON实体、自定义头部件MultipartFileSpring MVC 文件上传对象CookieValue请求 Cookie支持单值、Map、集合参数默认不允许 null除非注解设置requiredfalse或参数标记为可选。5. 自定义参数解析器可实现HttpServiceArgumentResolver自定义参数转换逻辑例如自定义 Search 对象自动拆解为查询参数自定义解析器staticclassSearchQueryArgumentResolverimplementsHttpServiceArgumentResolver{Overridepublicbooleanresolve(Objectargument,MethodParameterparameter,HttpRequestValues.BuilderrequestValues){if(parameter.getParameterType().equals(Search.class)){Searchsearch(Search)argument;requestValues.addRequestParameter(owner,search.owner());requestValues.addRequestParameter(language,search.language());requestValues.addRequestParameter(query,search.query());returntrue;}returnfalse;}}注册解析器并使用RestClientrestClientRestClient.builder().baseUrl(https://api.waylau.com/).build();RestClientAdapteradapterRestClientAdapter.create(restClient);HttpServiceProxyFactoryfactoryHttpServiceProxyFactory.builderFor(adapter).customArgumentResolver(newSearchQueryArgumentResolver()).build();RepositoryServicerepositoryServicefactory.createClient(RepositoryService.class);SearchsearchSearch.create().owner(spring-projects).language(java).query(rest).build();ListRepositoryrepositoriesrepositoryService.searchRepository(search);6. 返回值类型同步客户端RestClient / RestTemplate支持返回值返回类型说明void仅发起请求忽略响应HttpHeaders返回响应头丢弃响应体T直接返回解析后的实体对象ResponseEntity仅状态码响应头无响应体ResponseEntity完整响应状态、头、解析后实体响应式客户端WebClient额外支持响应式类型返回类型说明Mono异步请求丢弃响应体Mono异步获取响应头Mono异步返回单个实体Flux流式返回多个对象MonoResponseEntityFlux流式响应完整包装RestClientAdapter 额外支持InputStream/ResponseEntityInputStream可读取原始响应流。7. 异常处理代理客户端默认 4xx/5xx 都会抛出异常可在底层客户端统一配置全局状态处理器// RestClient 全局异常处理RestClientrestClientRestClient.builder().defaultStatusHandler(HttpStatusCode::isError,(request,response)-{// 统一错误逻辑}).build();RestClientAdapteradapterRestClientAdapter.create(restClient);HttpServiceProxyFactoryfactoryHttpServiceProxyFactory.builderFor(adapter).build();8. 适配器装饰器可通过exchangeAdapterDecorator包装适配器统一拦截所有代理请求例如内置装饰器实现「404 不抛异常返回空实体」HttpServiceProxyFactoryfactoryHttpServiceProxyFactory.builderFor(restClientAdapter).exchangeAdapterDecorator(NotFoundRestClientAdapterDecorator::new).build();9. HTTP 服务分组大量接口代理时每个接口单独配置 Bean 会产生大量重复代码。Spring 提供「服务分组」能力同一分组下的接口共用一套客户端与代理工厂配置自动注册为 Spring Bean。使用注解声明分组Configuration// 分组echo手动指定接口类ImportHttpServices(groupecho,types{EchoServiceA.class,EchoServiceB.class})// 分组greeting扫描包下所有接口ImportHttpServices(groupgreeting,basePackageClassesGreetServiceA.class)publicclassClientConfig{}编程式注册分组publicclassMyHttpServiceRegistrarextendsAbstractHttpServiceRegistrar{OverrideprotectedvoidregisterHttpServices(GroupRegistryregistry,AnnotationMetadatametadata){registry.forGroup(echo).register(EchoServiceA.class,EchoServiceB.class);registry.forGroup(greeting).detectInBasePackages(GreetServiceA.class);}}ConfigurationImport(MyHttpServiceRegistrar.class)publicclassClientConfig{}统一配置分组客户端通过RestClientHttpServiceGroupConfigurer批量修改分组配置BeanpublicRestClientHttpServiceGroupConfigurergroupConfigurer(){returngroups-{// 单独配置echo分组groups.filterByName(echo).forEachClient((group,clientBuilder)-{clientBuilder.defaultHeader(group,echo);});// 所有分组统一配置groups.forEachClient((group,clientBuilder)-{clientBuilder.requestInterceptor(logInterceptor);});};}注入使用RestControllerpublicclassEchoController{privatefinalEchoServiceechoService;// 直接自动注入publicEchoController(EchoServiceechoService){this.echoServiceechoService;}}多分组同接口场景同一接口存在多个分组时无法直接按类型注入可通过HttpServiceProxyRegistry根据分组名获取RestControllerpublicclassEchoController{privatefinalEchoServiceecho1;privatefinalEchoServiceecho2;publicEchoController(HttpServiceProxyRegistryregistry){this.echo1registry.getClient(echo1,EchoService.class);this.echo2registry.getClient(echo2,EchoService.class);}}Spring Boot、Spring Security、Spring Cloud 均基于该分组机制实现自动配置、负载均衡、OAuth 认证等能力。参考引用以下是Spring生态开发常用教程。《Spring Boot 企业级应用开发实战》北京大学出版社)《Spring Cloud 微服务架构开发实战》北京大学出版社)《Spring 5 开发大全》北京大学出版社《大型互联网应用轻量级架构实战》北京大学出版社《跟老卫学Spring Cloud Stream开发》开源免费教程 https://github.com/waylau/spring-cloud-stream-tutorial/《Vue.jsSpring Boot全栈开发实战》人民邮电出版社JavaAI全栈工程师 https://class.imooc.com/sale/javaaifullstack