提示:这是SpringBoot3以上的新特性。
远程访问@HttpExchange
远程访问
是开发的常用技术,一个应用能够访问其他应用的功能
。Spring Boot 提供了多种远程访问技术。基于 HTTP 协议的远程访问是支持最广泛的。Spring Boot3 提供了新的 HTTP 访问能力,通过接口简化 HTTP 远程访问
,类似 Feign 功能。Spring 包装了底层 HTTP 客户的访问细节
。- Spring Boot 中
定义接口提供 HTTP 服务
。生成的代理对象实现此接口
,代理对象实现 HTTP 的远程访问。
一、webClient
- 我们想要调用其他系统提供的 HTTP 服务,通常可以使用 Spring 提供的
RestTemplate
来访问,RestTemplate
是 Spring3 中引入的同步阻塞式
HTTP 客户端,因此存在一定性能瓶颈。Spring 官方在 Spring5 中引入了WebClient
作为异步非阻塞式
HTTP 客户端。有如下的特点:- 非阻塞,异步请求
- 它的响应式编程是基于 Reactor
- 高并发,硬件资源少
- 支持 Java8 lambda 函数式编程
什么是同步和异步
- 异步和同步
针对调用者
,调用者发送请求,如果等待对方回应之后才去做其他事情,就是同步,如果发送请求之后不等待对方回应就去做其他事情就是异步。
- 异步和同步
什么是阻塞和非阻塞
- 阻塞和非阻塞
针对被调度者
,被调度者收到请求后,做完请求任务之后才给出反馈就是阻塞,收到请求之后马上给出反馈然后去做其他事情,就是非阻塞。
- 阻塞和非阻塞
二、Http 服务接口的方法定义
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
@Reflective({HttpExchangeReflectiveProcessor.class})
public @interface HttpExchange {
@AliasFor("url")
String value() default "";
@AliasFor("value")
String url() default "";
String method() default "";
String contentType() default "";
String[] accept() default {};
}
- @HttpExchange 注解用于声明接口作为 HTTP 远程服务。在方法、类级别使用。通过注解属性以及方法的参数设置 HTTP 请求的细节。
- @GetExchange 用于 HTTP GET 请求。一般用于获取数据。
- @PostExchange 用于 HTTP POST 请求。一般用于提交数据。
- @PutExchange 用于 HTTP PUT 请求。一般用于修改数据。
- @PatchExchange 用于 HTTP PATCH 请求。
- @DelectExchange 用于 HTTP DELETE 请求。一般用于删除数据。
- 作为 HTTP 服务接口中的方法允许使用的参数
参数 说明 URI 设置请求的url,覆盖注解的url属性 HttpMethod 请求方式,覆盖注解的method属性 @RequestHeader 添加到请求中header。 参数类型可以为Map, MultiValueMap,单个值或者Collection @PathVariable 添加一个变量以扩展请求 URL 中的占位符。参数可为Map<String, ?>带有多个变量的,也可以是单个值。非字符串值支持类型转换 @RequestBody 请求体,参数是对象 @RequestParam 请求参数,单个值或Map, MultiValueMap,Collection @RequestPart 发送文件时使用 @CookieValue 向请求中添加cookie - 接口中方法返回值
返回值类型 说明 void 执行请求,无需解析应答 HttpHeaders 存储 response 应答的 header 信息 对象 解析应答结果,转为声明的类型对象 ResponseEntity<void> 执行给定的请求,释放响应内容,并返回 ResponseEntity带有状态和标头的 a。 ResonseEntity<T> 执行给定的请求,将响应内容解码为声明的返回类型,并返回 ResponseEntity 带有状态、标头和解码正文的 a。
三、声明式 HTTP 远程服务
声明式 HTTP 接口可以让你像定义Java接口那样定义HTTP服务,用法和你平时写Controller中方法完全一致。
- 使用一个免费的、24小时在线的 Rest Http 服务。===> https://jsonplaceholder.typicode.com/,其提供的 comments 服务。基于 RESTful 风格,添加新的 comments,修改 comments,查询某个具体的 comments。
1.组合使用注解
- @HttpExchange,@GetExchange等注解可以组合使用。
- 类级别的 url 和方法级别的 url 组合在一起就是最后请求的 url 地址。
- ① 引入依赖
- 声明性 HTTP 接口功能是spring-web依赖项的一部分,使用前必须引入如下依赖包:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency>
- ② 编写 comment 数据类
public class Comment { private Integer postId; private Integer id; private String name; private String email; private String body; //构造方法、setter和getter方法、toString方法 }
- ③ 声明HTTP服务接口
//其中的一个方法就是一个远程服务调用 @HttpExchange(url = "https://jsonplaceholder.typicode.com") public interface CommonService { @GetExchange("/comments/{id}") //设置请求的URI Comment getCommentById(@PathVariable("id") Integer id); }
- ④ 创建 HTTP 服务代理对象
@Configuration(proxyBeanMethods = false) public class HttpConfiguration { @Bean public CommonService requestService(){ WebClient webClient =WebClient.builder().build(); return HttpServiceProxyFactory.builderFor(WebClientAdapter.create(webClient)).build().createClient(CommonService.class); } }
- ⑤ 编写测试代码
@SpringBootTest class HttpExchangeApplicationTests { @Autowired private CommonService commonService; @Test public void test(){ Comment comment = commonService.getCommentById(1); System.out.println(comment); } }
- ⑥ 结果展示
2.使用单个注解
-
① 引入依赖
-
② 编写 comment 数据类
-
③ 声明服务接口
//其中的一个方法就是一个远程服务调用 public interface CommonService { @PutExchange("/comments/{id}") ResponseEntity<Comment> modifyCommentById(@PathVariable("id") Integer modifyId, @RequestBody Comment comment); }
-
④ 创建 HTTP 服务代理对象
@Configuration(proxyBeanMethods = false) public class HttpConfiguration { @Bean public CommonService requestService(){ WebClient webClient = WebClient.create("https://jsonplaceholder.typicode.com"); //WebClient webClient = WebClient.builder().baseUrl("https://jsonplaceholder.typicode.com").build(); //也可以使用这种方式 HttpServiceProxyFactory proxyFactory = HttpServiceProxyFactory.builderFor(WebClientAdapter.create(webClient)).build(); return proxyFactory.createClient(CommonService.class); } }
-
⑤ 编写测试代码
@SpringBootTest class HttpExchangeApplicationTests { @Autowired private CommonService commonService; @Test public void test(){ Comment comment = new Comment(123, 1, "gdb", "aaaaa", "bbbbbb"); ResponseEntity<Comment> result = commonService.modifyCommentById(1, comment); System.out.println(result.getStatusCode()); System.out.println(result.getHeaders()); System.out.println(result.getBody()); } }
-
⑥ 结果展示
3.定制 HTTP 请求服务
四、总结
1.部分方法过时
- 动力节点王鹤Spring Boot3中代码的写法如下
- 由于新版的更新原来的方法已经过时了,大家可以按照如下的方式写
2.过时的方法详解
- Builder builder(HttpClientAdapter clientAdapter) ===> Builder builderFor(HttpExchangeAdapter)
- forCreate(WebClient webClient) ===> create(WebClient webClient)