RestTemplate、WebClient与HttpInterface
SpringBoot中集成了很多轻量级的Http客户端
- RestTemplate:普通开发
- WebClient:响应式编程开发
- HttpInterface:声明式编程
响应式编程介绍
响应式编程(Reactive Programming)是一种编程范式,用于处理异步数据流
和事件流
。它基于观察者模式
和函数式
编程的概念,通过使用流(Stream)或者事件(Event)来处理数据的变化和交互。
在响应式编程中,数据流被看作是一个连续的时间序列,程序可以订阅这个数据流,并对数据的变化做出相应的处理。当数据发生变化时,程序会自动更新相关的操作,而无需手动干预。
响应式编程具有以下特点:
-
响应式:能够对数据流中的变化做出及时响应,而无需手动触发。
-
异步:能够处理异步操作,如网络请求或用户交互。
-
延迟执行:只有在真正需要结果时才会执行相关操作。
-
可组合性:能够将多个操作组合在一起,形成复杂的数据流处理逻辑。
-
错误处理:能够处理异常和错误,并提供相应的处理机制。
响应式编程在很多场景下都具有优势,例如用户界面的响应性、数据流的处理和分析等。
RestTemplate与WebClient区别
RestTemplate采用同步阻塞的方式运行,WebClient采用异步非阻塞的方式运行,只用需要WebClient返会的结果时对应的方法才会执行
使用WebClient
引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
构建业务
@Service
public class CityServiceImpl implements CityService {
@Override
public Mono<String> query(String city) {
WebClient webClient = WebClient.create();
Map<String, String> param = new HashMap<>();
param.put("city", "西安");
return webClient.get()
.uri("https://wttr.in/{city}?lang=zh", param)
.accept(MediaType.ALL)
.retrieve()
.bodyToMono(String.class);
}
}
package com.vmware.controller;
import com.vmware.service.CityService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
@RequestMapping("/city")
@RestController
public class CityController {
@Autowired
private CityService cityService;
@GetMapping("/{city}")
public Mono<String> city(@PathVariable String city) throws InterruptedException {
Mono<String> mono = cityService.query(city);
return query;
}
}
- 注意:mono对象被返回之前实际上接口处于未调用状态,当返回后延时调用
HttpInterface
介绍:HttpInterfaces是由spring6.0引入的新的http调用方式,通过声明接口的方式对http请求进行调用,与传统硬编码的方法有所不同,依赖于spring-boot-starter-webflux
基本使用
package com.vmware.service;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.service.annotation.GetExchange;
import reactor.core.publisher.Mono;
public interface CityWebClientService {
@GetExchange(url = "https://wttr.in/{city}?lang=zh")
Mono<String> getWeather(@PathVariable String city);
}
@Override
public Mono<String> queryByWebClient(String city) {
WebClient client = WebClient.builder()
.baseUrl("https://wttr.in")
.codecs(clientCodecConfigurer -> {
clientCodecConfigurer.defaultCodecs()
.maxInMemorySize(256 * 1024 * 1024);//修改默认的buffer大小
})
.build();
//创建工厂
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builder(WebClientAdapter.forClient(client)).build();
//创建代理对象
CityWebClientService service = factory.createClient(CityWebClientService.class);
return service.getWeather(city);
}
工程化使用
- 将
WebClient
和HttpServiceProxyFactory
使用配置类注入到容器中 - 将对应的WebClient请求接口代理后注入到容器中
- 在对应的业务中进行注入WebClient请求接口
package com.vmware.config;
import com.vmware.service.CityWebClientService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.support.WebClientAdapter;
import org.springframework.web.service.invoker.HttpServiceProxyFactory;
@Configuration
public class WebClientConfig {
/**
* @apiNote 配置WebClient
* @return
*/
@Bean
public WebClient httpServiceProxyFactory() {
return WebClient.builder()
.codecs(clientCodecConfigurer -> {
clientCodecConfigurer.defaultCodecs()
.maxInMemorySize(256 * 1024 * 1024);//修改默认的buffer大小
})
.build();
}
/**
* @apiNote 构建 HttpServiceProxyFactory
* @param client
* @return
*/
@Bean
public HttpServiceProxyFactory factory(WebClient client) {
//创建工厂
return HttpServiceProxyFactory.builder(WebClientAdapter.forClient(client)).build();
}
/**
* @apiNote 代理HttpClient接口
* @param factory
* @return
*/
@Bean
public CityWebClientService cityWebClientService(HttpServiceProxyFactory factory) {
return factory.createClient(CityWebClientService.class);
}
}
package com.vmware.service.impl;
import com.vmware.service.CityService;
import com.vmware.service.CityWebClientService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.support.WebClientAdapter;
import org.springframework.web.service.invoker.HttpServiceProxyFactory;
import reactor.core.publisher.Mono;
import java.util.HashMap;
import java.util.Map;
@Service
public class CityServiceImpl implements CityService {
@Autowired
private CityWebClientService cityWebClientService;
@Override
public Mono<String> queryByWebClient2(String city) {
return cityWebClientService.getWeather(city);
}
}