Spring中WebClient的创建和使用详解

 更新时间:2023年11月18日 09:06:29   作者:morris131  
这篇文章主要介绍了Spring中WebClient的创建和使用详解,在Spring5中,出现了Reactive响应式编程思想,并且为网络编程提供相关响应式编程的支持,如提供了WebFlux,它是Spring提供的异步非阻塞的响应式的网络框架,需要的朋友可以参考下

前言

在Spring5中,出现了Reactive响应式编程思想,并且为网络编程提供相关响应式编程的支持,如提供了WebFlux,它是Spring提供的异步非阻塞的响应式的网络框架,相比传统的SpringMVC框架,可以充分利用多CPU并行处理一些功能,虽然不能提高单个请求的响应能力,但是总体可以提高多核的服务器性能,提高系统吞吐量和伸缩性,特别适合于IO密集型服务。

WebClient提供的基于响应式的非阻塞的Web请求客户端,相对于传统的RestTemplate,他不阻塞代码、异步执行。

使用WebClient需要引入下面的依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

WebClient的创建

WebClient可以直接通过new来创建,也可以使用构造者模式来构造。

package com.morris.user.demo;

import com.morris.user.entity.Order;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.client.WebClient;

import java.util.Arrays;
import java.util.concurrent.TimeUnit;

/**
 * WebClient的创建
 */
public class WebClientDemo1 {

    public static void main(String[] args) throws InterruptedException {
        WebClient webClient = WebClient.create();
        webClient.get().uri("http://127.0.0.1:8020/order/findOrderByUserId?userId={userId}", 1).retrieve()
                .bodyToMono(Order[].class).map(Arrays::asList).subscribe(System.out::println);

        WebClient webClient2 = WebClient.builder()
                .baseUrl("http://127.0.0.1:8020")
                .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .build();
        webClient2.get().uri("/order/findOrderByUserId?userId={userId}", 1).retrieve()
                .bodyToMono(Order[].class).map(Arrays::asList).subscribe(System.out::println);
        TimeUnit.SECONDS.sleep(5);
    }
}

在应用中使用WebClient时也许你要访问的URL都来自同一个应用,只是对应不同的URI地址,这个时候可以把公用的部分抽出来定义为baseUrl,然后在进行WebClient请求的时候只指定相对于baseUrl的URL部分即可。这样的好处是你的baseUrl需要变更的时候可以只要修改一处即可。

WebClient发送Get请求

先创建个webclient.create()实例,之后调用get()、post()等调用方式,uri()指定路径,retrieve()用来发起请求并获得响应,bodyToFlux(Order.class)用来将请求结果需要处理为Order数组,并包装为Reactor的Flux对象。

如果返回结果是一个JSON字符串,可以使用bodyToMono(),将接收到的JSON字符串转换为对应的对象。

如果返回结果是一个JSON数组,可以使用bodyToFlux(),将接收到的JSON数组转换为对应的对象集合,然后依次处理每一个元素。

package com.morris.user.demo;

import com.morris.user.entity.Order;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.client.WebClient;

import java.util.Arrays;
import java.util.concurrent.TimeUnit;

/**
 * WebClient发送Get请求
 */
public class WebClientGetDemo {

    public static void main(String[] args) throws InterruptedException {
        WebClient webClient = WebClient.create();
        webClient.get().uri("http://127.0.0.1:8020/order/findOrderByUserId?userId={userId}", 1).retrieve()
                .bodyToFlux(Order.class).subscribe(System.out::println);;

        // 休眠一会,否则WebClient中的线程池还没执行,看不到效果
        TimeUnit.SECONDS.sleep(5);
    }
}

WebClient发送Post请求

可以使用BodyInserters类提供的各种工厂方法来构造BodyInserter对象并将其传递给body方法。BodyInserters类包含从Object,Publisher,Resource,FormData,MultipartData等创建BodyInserter的方法。

package com.morris.user.demo;

import com.morris.user.entity.Order;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

/**
 * WebClient发送Post请求
 */
public class WebClientPostDemo {

    public static void main(String[] args) {
        WebClient webClient = WebClient.create();
        Order order = new Order();
        order.setId(1L);
        order.setUserId(666L);
        order.setGoodName("Iphone 13");
        order.setPrice(9999);
        Mono<Long> mono = webClient.post().uri("http://127.0.0.1:8020/order/saveOrder")
                .body(BodyInserters.fromValue(order))
                // .body(Mono.just(order), Order.class)
                .retrieve()
                .bodyToMono(Long.class);

        // 阻塞等待获取结果
        System.out.println(mono.block());
    }
}

WebClient对失败的处理

package com.morris.user.demo;

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.WebClientResponseException;
import reactor.core.publisher.Mono;

/**
 * WebClient对失败的处理
 */
@Slf4j
public class WebClientDealFailDemo {

    public static void main(String[] args) {
        WebClient webClient = WebClient.create();
        WebClient.ResponseSpec responseSpec = webClient.get().uri("http://127.0.0.1:8020/order/error")
                .retrieve();

        Mono<String> mono = responseSpec
                .onStatus(HttpStatus::is4xxClientError, resp -> {
                    log.error("error4xx:{},msg:{}",resp.statusCode().value(),resp.statusCode().getReasonPhrase());
                    return Mono.error(new RuntimeException(resp.statusCode().value() + " : " + resp.statusCode().getReasonPhrase()));
                })
                .bodyToMono(String.class)
                .doOnError(WebClientResponseException.class, err -> {
                    log.info("ERROR status:{},msg:{}",err.getRawStatusCode(),err.getResponseBodyAsString());
                    throw new RuntimeException(err.getMessage());
                })
                .onErrorReturn("fallback");

        // 阻塞等待获取结果
        System.out.println(mono.block());
    }
}

可以使用onStatus根据响应的status code进行适配,可以使用doOnError对异常进行适配,可以使用onErrorReturn返回默认值。

exchange()

retrieve()方法是直接获取响应body,但是,如果需要响应的头信息、Cookie等,可以使用exchange方法,该方法可以访问整个ClientResponse。由于响应的得到是异步的,所以都可以调用block()方法来阻塞当前程序,等待获得响应的结果。

package com.morris.user.demo;

import com.morris.user.entity.Order;
import org.springframework.web.reactive.function.client.WebClient;

import java.util.concurrent.TimeUnit;

/**
 * WebClient使用Exchange发送请求
 */
public class WebClientExchangeDemo {

    public static void main(String[] args) throws InterruptedException {
        WebClient webClient = WebClient.create();
        webClient.get().uri("http://127.0.0.1:8020/order/findOrderByUserId?userId={userId}", 1)
                .exchange()
                .subscribe(r -> {
                    System.out.println(r.headers());
                    r.bodyToFlux(Order.class).subscribe(System.out::println);
                });

        // 休眠一会,否则WebClient中的线程池还没执行,看不到效果
        TimeUnit.SECONDS.sleep(5);
    }
}

filter

WebClient也提供了Filter,对应于org.springframework.web.reactive.function.client.ExchangeFilterFunction接口,可以拦截request,也可以拦截response。

package com.morris.user.demo;

import com.morris.user.entity.Order;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

import java.util.concurrent.TimeUnit;

/**
 * WebClient使用filter拦截器
 */
@Slf4j
public class WebClientFilterDemo {

    private static ExchangeFilterFunction logResponseStatus() {
        return ExchangeFilterFunction.ofResponseProcessor(clientResponse -> {
            log.info("Response Status {}", clientResponse.statusCode());
            return Mono.just(clientResponse);
        });
    }

    public static void main(String[] args) throws InterruptedException {
        WebClient webClient = WebClient.builder().filter(logResponseStatus()).build();
        webClient.get().uri("http://127.0.0.1:8020/order/findOrderByUserId?userId={userId}", 1)
                .exchange()
                .subscribe(r -> {
                    System.out.println(r.headers());
                    r.bodyToFlux(Order.class).subscribe(System.out::println);
                });

        // 休眠一会,否则WebClient中的线程池还没执行,看不到效果
        TimeUnit.SECONDS.sleep(5);
    }
}

Attributes

可以使用attribute在多个filter之间传递参数。

package com.morris.user.demo;

import com.morris.user.entity.Order;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
 * WebClient使用attribute传递参数
 */
@Slf4j
public class WebClientAttributesDemo {

    private static ExchangeFilterFunction filterRequest() {
        return ExchangeFilterFunction.ofRequestProcessor(clientRequest -> {
            Optional<Object> myAttribute = clientRequest.attribute("myAttribute");
            System.out.println(myAttribute.get());
            return Mono.just(clientRequest);
        });
    }

    public static void main(String[] args) throws InterruptedException {
        WebClient webClient = WebClient.builder().filter(filterRequest()).build();
        webClient.get().uri("http://127.0.0.1:8020/order/findOrderByUserId?userId={userId}", 1)
                .attribute("myAttribute", "myAttribute")
                .exchange()
                .subscribe(r -> {
                    System.out.println(r.headers());
                    r.bodyToFlux(Order.class).subscribe(System.out::println);
                });

        // 休眠一会,否则WebClient中的线程池还没执行,看不到效果
        TimeUnit.SECONDS.sleep(5);
    }
}

到此这篇关于Spring中WebClient的创建和使用详解的文章就介绍到这了,更多相关WebClient的创建和使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Springboot中useGeneratedKeys用法小结

    Springboot中useGeneratedKeys用法小结

    本文主要介绍了Springboot中useGeneratedKeys用法小结,useGeneratedKeys 是 MyBatis 框架中的一个参数,用于指定是否允许 JDBC 支持自动生成主键,感兴趣的可以了解一下
    2024-09-09
  • Java16 JDK安装并设置环境变量的方法步骤

    Java16 JDK安装并设置环境变量的方法步骤

    突然想起自己大学刚接触java的时候,要下载JDK和配置环境变量,那时候我上网找了很多教学,本文就详细的介绍一下Java16 JDK安装并设置环境变量,感兴趣的可以了解一下
    2021-09-09
  • Java实战之仿天猫商城系统的实现

    Java实战之仿天猫商城系统的实现

    这篇文章主要介绍了如何利用Java制作一个基于SSM框架的迷你天猫商城系统,文中采用的技术有JSP、Springboot、SpringMVC、Spring等,需要的可以参考一下
    2022-03-03
  • 深入浅析springsecurity入门登录授权

    深入浅析springsecurity入门登录授权

    SpringSecurity为我们提供了基于注解的权限控制方案,这也是我们项目中主要采用的方式,我们可以使用注解去指定访问对应的资源所需的权限,这篇文章主要介绍了springsecurity入门登录授权,需要的朋友可以参考下
    2024-05-05
  • 关于maven项目中使用BCrypt加密方式

    关于maven项目中使用BCrypt加密方式

    BCrypt是一种基于Blowfish加密算法的密码散列函数,用于安全存储和验证用户密码,它通过引入盐和工作因子增加计算复杂度,有效防止彩虹表攻击和破解,BCrypt具备适应性工作因子、成本参数调整、迭代哈希和密钥扩展等特点,被广泛应用于Web应用程序的安全性设计中
    2024-10-10
  • jackson 如何将实体转json json字符串转实体

    jackson 如何将实体转json json字符串转实体

    这篇文章主要介绍了jackson 实现将实体转json json字符串转实体,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10
  • Springboot并发调优之大事务和长连接

    Springboot并发调优之大事务和长连接

    这篇文章主要介绍了Springboot并发调优之大事务和长连接,重点分享长事务以及长连接导致的并发排查和优化思路和示例,具有一定的参考价值,感兴趣的可以了解一下
    2022-05-05
  • Springboot实现多文件上传代码解析

    Springboot实现多文件上传代码解析

    这篇文章主要介绍了Springboot实现多文件上传代码解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-04-04
  • 解决IDEA2020.1.2IDEA打不开的问题(最新分享)

    解决IDEA2020.1.2IDEA打不开的问题(最新分享)

    由于idea安装多了某个jar,点击出现读条后闪退情况,接下来通过本文给大家分享解决IDEA2020.1.2IDEA打不开的问题,非常不错,具有一定的参考借鉴价值,感兴趣的朋友跟随小编一起看看吧
    2020-07-07
  • Spring AI内置DeepSeek的详细步骤

    Spring AI内置DeepSeek的详细步骤

    Spring AI 最新快照版已经内置 DeepSeek 了,所以以后项目中对接 DeepSeek 就方便多了,但因为快照版会有很多 Bug,所以今天咱们就来看稳定版的 Spring AI 如何对接 DeepSeek 满血版,感兴趣的小伙伴跟着小编一起来看看吧
    2025-02-02

最新评论