Spring中WebClient的創(chuàng)建和使用詳解
前言
在Spring5中,出現(xiàn)了Reactive響應式編程思想,并且為網(wǎng)絡編程提供相關響應式編程的支持,如提供了WebFlux,它是Spring提供的異步非阻塞的響應式的網(wǎng)絡框架,相比傳統(tǒng)的SpringMVC框架,可以充分利用多CPU并行處理一些功能,雖然不能提高單個請求的響應能力,但是總體可以提高多核的服務器性能,提高系統(tǒng)吞吐量和伸縮性,特別適合于IO密集型服務。
WebClient提供的基于響應式的非阻塞的Web請求客戶端,相對于傳統(tǒng)的RestTemplate,他不阻塞代碼、異步執(zhí)行。
使用WebClient需要引入下面的依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
WebClient的創(chuàng)建
WebClient可以直接通過new來創(chuàng)建,也可以使用構造者模式來構造。
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的創(chuàng)建
*/
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發(fā)送Get請求
先創(chuàng)建個webclient.create()實例,之后調用get()、post()等調用方式,uri()指定路徑,retrieve()用來發(fā)起請求并獲得響應,bodyToFlux(Order.class)用來將請求結果需要處理為Order數(shù)組,并包裝為Reactor的Flux對象。
如果返回結果是一個JSON字符串,可以使用bodyToMono(),將接收到的JSON字符串轉換為對應的對象。
如果返回結果是一個JSON數(shù)組,可以使用bodyToFlux(),將接收到的JSON數(shù)組轉換為對應的對象集合,然后依次處理每一個元素。
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發(fā)送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中的線程池還沒執(zhí)行,看不到效果
TimeUnit.SECONDS.sleep(5);
}
}
WebClient發(fā)送Post請求
可以使用BodyInserters類提供的各種工廠方法來構造BodyInserter對象并將其傳遞給body方法。BodyInserters類包含從Object,Publisher,Resource,F(xiàn)ormData,MultipartData等創(chuàng)建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發(fā)送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根據(jù)響應的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發(fā)送請求
*/
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中的線程池還沒執(zhí)行,看不到效果
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中的線程池還沒執(zhí)行,看不到效果
TimeUnit.SECONDS.sleep(5);
}
}
Attributes
可以使用attribute在多個filter之間傳遞參數(shù)。
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傳遞參數(shù)
*/
@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中的線程池還沒執(zhí)行,看不到效果
TimeUnit.SECONDS.sleep(5);
}
}
到此這篇關于Spring中WebClient的創(chuàng)建和使用詳解的文章就介紹到這了,更多相關WebClient的創(chuàng)建和使用內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Java基于Spire Cloud Excel把Excel轉換成PDF
這篇文章主要介紹了Java基于Spire Cloud Excel把Excel轉換成PDF,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-05-05
Java?Swing實現(xiàn)自定義彈窗組件CusDialog(附源碼)
Swing 原生對話框 JDialog 和 JFrame 一樣,標題欄是系統(tǒng)自帶的,樣式陳舊且無法自定義,下面我們就來看看如何通過Java Swing實現(xiàn)自定義彈窗組件CusDialog吧2026-05-05
Windows10系統(tǒng)下修改jar中的文件并重新打包成jar文件然后運行的操作步驟
這篇文章主要介紹了Windows10系統(tǒng)下修改jar中的文件并重新打包成jar文件然后運行的操作步驟,文中通過圖文結合的形式給大家講解的非常詳細,對大家的學習或工作有一定的幫助,需要的朋友可以參考下2024-08-08
Java C++解決在排序數(shù)組中查找數(shù)字出現(xiàn)次數(shù)問題
本文終于介紹了分別通過Java和C++實現(xiàn)統(tǒng)計一個數(shù)字在排序數(shù)組中出現(xiàn)的次數(shù)。文中詳細介紹了實現(xiàn)思路,感興趣的小伙伴可以跟隨小編學習一下2021-12-12
mybatis?mapper.xml中如何根據(jù)數(shù)據(jù)庫類型選擇對應SQL語句
這篇文章主要介紹了mybatis?mapper.xml中如何根據(jù)數(shù)據(jù)庫類型選擇對應SQL語句,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-01-01

