64、使用 Spring WebFlux 的 WebClient 整合第三方Restful服务

这节的要点:

就是弄两个项目 , 从 端口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>
相关推荐
NiNg_1_2343 分钟前
Java中的多线程
java·开发语言
丁总学Java10 分钟前
nohup java -jar supporterSys.jar --spring.profiles.active=prod &
java·spring·jar
呆呆小雅12 分钟前
C# 结构体
android·java·c#
谢尔登12 分钟前
使用 Maven 创建 jar / war 项目
java·maven·jar
理想不理想v25 分钟前
前端开发工程师需要学什么?
java·前端·vue.js·webpack·node.js
赶路人儿26 分钟前
IntelliJ IDEA配置(mac版本)
java·macos·intellij-idea
jjw_zyfx26 分钟前
docker 的各种操作
java·docker·eureka
生财27 分钟前
获取字 short WORD 上指定的位是否有效
java·服务器·c#
hummhumm38 分钟前
第 36 章 - Go语言 服务网格
java·运维·前端·后端·python·golang·java-ee
YuanLiu_2271 小时前
代码随想录算法训练营第十三天(递归遍历;迭代遍历;统一迭代;层序遍历)
java·数据结构·笔记·算法·leetcode