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的创建和使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java 使用Axis调用WebService的示例代码

    Java 使用Axis调用WebService的示例代码

    这篇文章主要介绍了Java 使用Axis调用WebService的示例代码,帮助大家更好的理解和使用Java,感兴趣的朋友可以了解下
    2020-09-09
  • 详解Java实现LRU缓存

    详解Java实现LRU缓存

    这篇文章主要介绍了详解Java实现LRU缓存,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-08-08
  • Mybatis-Plus处理Mysql Json类型字段的详细教程

    Mybatis-Plus处理Mysql Json类型字段的详细教程

    这篇文章主要给大家介绍了关于Mybatis-Plus处理Mysql Json类型字段的详细教程,Mybatis-Plus可以很方便地处理JSON字段,在实体类中可以使用@JSONField注解来标记JSON字段,同时在mapper.xml中使用json函数来操作JSON字段,需要的朋友可以参考下
    2024-01-01
  • java指纹识别以及谷歌图片识别技术源码

    java指纹识别以及谷歌图片识别技术源码

    这篇文章主要为大家详细爱介绍了java指纹识别以及谷歌图片识别技术源码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-09-09
  • logback的FileAppender文件追加模式和冲突检测解读

    logback的FileAppender文件追加模式和冲突检测解读

    这篇文章主要为大家介绍了logback的FileAppender文件追加模式和冲突检测解读,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-10-10
  • SpringBoot多controller添加URL前缀的实现方法

    SpringBoot多controller添加URL前缀的实现方法

    这篇文章主要介绍了SpringBoot多controller添加URL前缀的方法,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-02-02
  • spring-boot-starter-parent的作用详解

    spring-boot-starter-parent的作用详解

    这篇文章主要介绍了spring-boot-starter-parent的作用详解,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-08-08
  • 浅谈java是如何做资源回收补救的

    浅谈java是如何做资源回收补救的

    这篇文章主要介绍了浅谈java是如何做资源回收补救的,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-06-06
  • mybatis-plus动态表名的实现示例

    mybatis-plus动态表名的实现示例

    这篇文章主要介绍了mybatis-plus动态表名的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-04-04
  • Spring Boot 通过CORS实现跨域问题

    Spring Boot 通过CORS实现跨域问题

    这篇文章主要介绍了Spring Boot 通过CORS实现跨域,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-09-09

最新评论