这节的要点:
就是弄两个项目 , 从 端口9090 这个项目,通过 webClient, 去访问 端口8080 的项目,并获取8080项目的数据。
★ RESTful客户端的两种方式
- 应用基于传统的Spring MVC框架,此时考虑使用RestTemplate来整合第三方RESTful服务。
RestTemplate就属于传统Spring Web的API。
- 应用基于传统的Web Flux框架,此时考虑使用WebClient来整合第三方RESTful服务。
WebClient本身就是属于WebFlux API
★ 使用WebClient调用(整合)第三方RESTful服务
如果应用本身使用的WebFlux这种反应式API,使用WebClient来整合第三饭RESTful服务会更好一些。
与RestTemplate的区别在于:
它采用的函数式的编程方式,且它返回的数据都是Flux或Mono------它是面向消息发布来编程
Flux(要返回多条数据用这个 Flux 返回类型)
Mono(只返回一条数据用这个 Mono 返回类型)
同样使用的反应式、非阻塞的API。
使用:
(1)通过预配置的WebClient.Builder对象创建WebClient。
不要自己去new一个WebClient
(2)调用WebClient对象的如下方法来指定发送请求:
delete()|get()|head() |method(HttpMethod method) |patch()|post() |put()
调用如下方法来设置请求头和请求体。
uri() | header() | accept() | body()
调用如下两个方法获取响应:
exchange()| retrieve()
★ WebClient的底层配置
WebClient底层需要依赖自动配置的ClientHttpConnector(HTTP连接器)
▲ Spring Boot会根据类加载路径里的类库自动检测使用哪个ClientHttpConnector来驱动WebClient,
Spring Boot内置支持Netty的ReactorClientHttpConnector和JettyClientHttpConnector两个实现类。
▲ Spring Boot默认会选择ReactorClientHttpConnector作为实现类(它底层依赖于Reactor Netty),
Reactor Netty可以同时提供服务器和客户端的实现;
- 只要你添加WebFlux的依赖库(spring-boot-starter-webflux),Netty既能提供服务器端的支持,
也能提供WebClient所需要的ClientHttpConnector。
一句话,你只需要添加spring-boot-starter-webflux,剩下的一切都搞定。
- 如果要选择Jetty作为WebFlux应用的服务器(它只能提供服务器端的支持),
如果需要WebClient的客户端支持,那就还需要添加Jetty Reactive HTTPClient的客户端JAR包。
★ 对客户端和服务器端同时配置:
两步:
(1)在Spring容器中配置自定义的ReactorResourceFactory(对于Reactor Netty)或JettyResourceFactory(对Jetty生效)。
(2)Spring Boot会自动加载、并应用它们对Reactor Netty或Jetty的资源配置进行重写,这样可同时作用于服务器端和客户端。
★ 替换ClientHttpConnector【一般很少这么干,因为这样相当于完全放弃了Spring Boot的自动配置】
只要在Spring容器中配置自己的ClientHttpConnector,Spring Boot就不会再帮我们自动配置ClientHttpConnector。
这样就使用了自定义的ClientHttpConnector代替了自动配置的ClientHttpConnector。
★ 定制WebClient(做法完全类似于前面定制RestTemplate)
定制WebClient提供了两种方式:
- 局部式:在调用WebClient.Builder的build()方法构建WebCilent之前,
先调用WebClient.Builder的方法对其定制,通过这种方式设置的WebClient.Builder仅对它构建的WebClient起作用。
- 全局式:使用WebClientCustomizer进行定制,所有实现WebClientCustomizer接口的Bean会被自动应用到
自动配置的WebClient.Builder中,这种定制方式对整个应用范围的WebClient都起作用。
------ 此处的定制方式与定制RestTemplate几乎是相同的。
代码演示
RESTful_XML 8080 项目代表 restful 服务的 服务端,生成json响应的,
MyWebClient9090 项目代表 restful 服务的 客户端,发起请求的
这个 RESTful_XML 就是第三方RESTful 服务,MyWebClient 项目通过 WebClient 来整合它
其他代码可以在这篇获取,都是一样的
其他代码是基于这篇---SpringBoot 使用RestTemplate 整合第三方 RESTful 服务 -- 延伸的
需求:两个项目 , 从 9090 这个项目,通过 WebClient 去访问 8080 的项目,并获取数据。
WebClient 和 RestTemplate 的区别
WebClient 属于 WebFlux 的 API , 因此需要导入 WebFlux 的依赖库
先导入依赖:
区别:
WebClient 和 RestTemplate 的依赖注入的区别
WebClient 和 RestTemplate 的查看所有图书区别
根据id查看图书
根据id删除图书
webClient可以把被删除的对象返回回来
查看测试结果
根据id修改图书数据
测试结果
完整代码
其他代码可以在这篇获取,都是一样的
其他代码是基于这篇---SpringBoot 使用RestTemplate 整合第三方 RESTful 服务 -- 延伸的
pom.xml
ClientController
java
package cn.ljh.app.controller;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.Map;
@RestController
@RequestMapping("/mybooks")
public class ClientController
{
private final WebClient webClient;
public ClientController(WebClient.Builder builder)
{
/*
* 此处的 WebClient.Builder 是来自于Spring 容器的注入,
* 因此它所构建的 webClient 已经接收了spring 容器的默认设置
* 如果直接创建 WebClient , 那就相当于完全没有利用Spring容器的依赖注入,
* 因此完全不能接受spring容器的默认配置,这样后面所介绍的配置 webClient 完全不可能实现配置了
*/
this.webClient = builder
//此处本身就是对 webClient 的定制
.baseUrl("http://192.168.43.189:8080/") //webClient 指定基路径
//此处还可以对 webClient 进行更多的定制
//............
.build();
}
//查看所有图书
@GetMapping("/viewBooks")
public Flux<Map> viewBooks()
{
Flux<Map> mapFlux = webClient.get()
.uri("/books/viewBooks") //访问路径
.accept(MediaType.APPLICATION_JSON) //访问该方法,希望接收响应类型
.retrieve() //获取响应数据
//将响应数据转换成 Flux(响应集合数据用Flux) 或这 Mono(单个响应数据用 Mono)
.bodyToFlux(Map.class);
return mapFlux;
}
//根据id查看图书
@GetMapping("/{id}")
public Mono<Map> getBookById(@PathVariable Integer id)
{
Mono<Map> mapMono = webClient.get()
.uri("/books/"+id ) //由于地址是静态的,所以可以把id拼接上去
.accept(MediaType.APPLICATION_JSON)
.retrieve()//获取响应,这里的响应不是真正的数据
//将响应数据转换成 Flux(响应集合数据用Flux) 或这 Mono(单个响应数据用 Mono)
.bodyToMono(Map.class); //获取的属于是消息发布者,或者说是一个消息通道
return mapMono;
}
//根据id删除图书
@DeleteMapping("/{id}")
public Mono<Map> deleteBookById(@PathVariable Integer id)
{
//webClient可以把被删除的对象返回回来
Mono<Map> mapMono = webClient.delete()
.uri("/books/" + id)
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(Map.class);
return mapMono;
}
//根据id修改图书
@PutMapping("/{id}")
public Mono<Map> updateById(@PathVariable Integer id,
@RequestBody Map requestData)
{
Mono<Map> mapMono = webClient.put()
.uri("/books/" + id)
.accept(MediaType.APPLICATION_JSON)
//参数1:看源码,需要是消息发布者,,因为请求参数只有一个数据,不是集合,所以可以把请求参数包装成Mono,安全一些
//参数2:指定参数数据的类型
.body(Mono.justOrEmpty(requestData), Map.class)
//.header() //如果有需要,可以这样指定请求头
.retrieve() //获取响应数据
.bodyToMono(Map.class);
return mapMono;
}
}
pom.xml
java
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5</version>
</parent>
<groupId>cn.ljh</groupId>
<artifactId>MyWebClient</artifactId>
<version>1.0.0</version>
<name>MyWebClient</name>
<properties>
<java.version>11</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-web</artifactId>-->
<!-- </dependency>-->
<!-- WebClient 属于 WebFlux 的 API , 因此需要导入 WebFlux 的依赖库 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>