前提
不想使用同步处理的httpclient,或者想体验下新技术的时候,可以尝试使用WebClient,反应式的 WebClient 作为在 Spring 5 WebFlux 框架的一部分,具备非阻塞的特点,但是使用起来较为复杂,以下是组件demo
pom.xml 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
配置类
import io.netty.channel.ChannelOption;
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.handler.timeout.WriteTimeoutHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.mybatis.spring.annotation.MapperScan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.ExchangeStrategies;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.netty.http.client.HttpClient;
import reactor.netty.tcp.TcpClient;
/**
* @author chunyang.leng
* @date 2021/1/18 9:44 下午
*/
@Configuration
public class ComponentAutoConfiguration {
private static final Logger logger = LoggerFactory.getLogger(ComponentAutoConfiguration.class);
@Bean
@ConditionalOnMissingBean(WebClient.class)
public WebClient webClient() {
LoggerUtils.info(logger, "初始化WebClient");
TcpClient tcpClient = TcpClient
.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
.doOnConnected(connection -> {
connection.addHandlerLast(new ReadTimeoutHandler(5000, TimeUnit.MILLISECONDS));
connection.addHandlerLast(new WriteTimeoutHandler(5000, TimeUnit.MILLISECONDS));
});
return WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(HttpClient.from(tcpClient)))
.exchangeStrategies(ExchangeStrategies.builder()
.codecs(configurer -> configurer.defaultCodecs()
.maxInMemorySize(16 * 1024 * 1024)
)
.build())
.build();
}
}
工具类
import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
/**
* @author chunyang.leng
* @date 2021-11-23 11:52 上午
*/
@Component
public class WebClientUtils {
@Autowired
private WebClient webClient;
/**
* 发送http post json 请求
*
* @param url 请求url
* @param json json数据
* @param result 返回值结果类型
* @param <T> 返回值类型
* @return 返回订阅对象
*/
public <T> Mono<T> sendPostJSON(String url, String json, Class<T> result) {
// 发送请求
return webClient
// POST 请求
.post()
// 请求路径
.uri(url)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(json))
// 获取响应体
.retrieve()
//响应数据类型转换
.bodyToMono(String.class)
.map(jsonString -> JSON.toJavaObject(JSON.parseObject(jsonString), result));
}
/**
* 发送http post 请求
*
* @param url 请求url
* @param result 返回值结果类型
* @param <T> 返回值类型
* @return 返回订阅对象
*/
public <T> Mono<T> sendPost(String url, Class<T> result) {
// 发送请求
return webClient
// POST 请求
.post()
// 请求路径
.uri(url)
// 获取响应体
.retrieve()
//响应数据类型转换
.bodyToMono(String.class)
.map(jsonString -> JSON.toJavaObject(JSON.parseObject(jsonString), result));
}
/**
* 发送一个get请求
*
* @param url 请求地址
* @param result 应答数据类型
* @param <T> 转换应答类型
* @return 异步包装对象
*/
public <T> Mono<T> sendGet(String url, Class<T> result) {
// 发送请求
return webClient
// POST 请求
.get()
// 请求路径
.uri(url)
// 获取响应体
.retrieve()
//响应数据类型转换
.bodyToMono(String.class)
.map(jsonString -> JSON.toJavaObject(JSON.parseObject(jsonString), result));
}
/**
* 发送http post json 请求
*
* @param url 请求url
* @param param 请求参数
* @param result 返回值结果类型
* @param <T> 返回值类型
* @return 返回订阅对象
*/
public <T> Mono<T> sendPostFormUrlEncoded(String url, MultiValueMap<String, Object> param, Class<T> result) {
// 发送请求
return webClient
// POST 请求
.post()
// 请求路径
.uri(url)
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.body(BodyInserters.fromMultipartData(param))
// 获取响应体
.retrieve()
//响应数据类型转换
.bodyToMono(result);
}
/**
* 发送一个get请求
*
* @param url 请求地址
* @param result 应答数据类型
* @param <T> 转换应答类型
* @return 异步包装对象
*/
public <T> Mono<T> sendGet(String url, Class<T> result, MediaType... mediaTypes) {
// 发送请求
return webClient
// POST 请求
.get()
// 请求路径
.uri(url)
// 允许接收的参数类型
.accept(mediaTypes)
// 获取响应体
.retrieve()
//响应数据类型转换
.bodyToMono(String.class)
.map(jsonString -> JSON.toJavaObject(JSON.parseObject(jsonString), result));
}
}