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

SpringBoot --> 使用 Spring WebFlux 的 WebClient 整合第三方 Restful 服务

需求:就是弄两个项目 , 从 端口9090 这个项目,通过 webClient, 去访问 端口8080 的项目,并获取8080项目的数据。


RESTful客户端的两种方式

方式1:

应用基于传统的Spring MVC框架,此时考虑使用RestTemplate来整合第三方RESTful服务。
RestTemplate就属于传统Spring Web的API。

方式2::

应用基于传统的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

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

<?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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_L_J_H_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值