• 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
    
    • 1
    • 2
    • 3
    • 4
    • 5

    ★ 使用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()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    ★ 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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    ★ 对客户端和服务器端同时配置:

      两步:
    
     (1)在Spring容器中配置自定义的ReactorResourceFactory(对于Reactor Netty)或JettyResourceFactory(对Jetty生效)。
    
     (2)Spring Boot会自动加载、并应用它们对Reactor Netty或Jetty的资源配置进行重写,这样可同时作用于服务器端和客户端。
    
    • 1
    • 2
    • 3
    • 4
    • 5

    ★ 替换ClientHttpConnector【一般很少这么干,因为这样相当于完全放弃了Spring Boot的自动配置】

    只要在Spring容器中配置自己的ClientHttpConnector,Spring Boot就不会再帮我们自动配置ClientHttpConnector。
    
    这样就使用了自定义的ClientHttpConnector代替了自动配置的ClientHttpConnector。
    
    • 1
    • 2
    • 3

    ★ 定制WebClient(做法完全类似于前面定制RestTemplate)

    定制WebClient提供了两种方式:
    - 局部式:在调用WebClient.Builder的build()方法构建WebCilent之前,
      先调用WebClient.Builder的方法对其定制,通过这种方式设置的WebClient.Builder仅对它构建的WebClient起作用。
    
    - 全局式:使用WebClientCustomizer进行定制,所有实现WebClientCustomizer接口的Bean会被自动应用到
              自动配置的WebClient.Builder中,这种定制方式对整个应用范围的WebClient都起作用。
    
    —— 此处的定制方式与定制RestTemplate几乎是相同的。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    代码演示

    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

    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;
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100

    pom.xml

    <?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 属于 WebFluxAPI , 因此需要导入 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>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
  • 相关阅读:
    win10卸载oracle11g
    初识Java--Java数据类型
    Spring注解驱动之@Resource注解和@Inject注解
    mysql索引最左匹配原则的理解?(绝对牛逼)
    openjudge 1.5.2 财务管理
    第五十九章 IIS 7 或更高版本的替代选项 (Windows) - 替代选项 2:将本机模块与 - 映射 InterSystems IRIS 文件扩展名
    【Java进阶】包装类
    第2章 算法
    关于深拷贝和浅拷贝你需要了解的内容
    C++ 使用bit7z实现压缩与解压缩
  • 原文地址:https://blog.csdn.net/weixin_44411039/article/details/132837670