领先一步
VMware提供培训和认证,以加速你的进步。
了解更多欢迎来到另一期《你可能不需要另一个库》(YMNNALFT)!自从2016年以来,我花了很多时间在我的Spring Tips视频中阐明(或者至少尝试阐明!)Spring生态系统中一些更大的机遇。然而,今天我以不同的精神来到这里,想专注于那些功能强大的、有时隐藏的小巧宝石,它们可以帮助你避免额外的第三方依赖及其隐含的复杂性。
今天,我们将研究一个多合一、方便易用的HTTP客户端:WebClient
。
HTTP服务是数据的常见来源。互联网证明了HTTP的可扩展性和弹性,并且在构建网络服务时,它为HTTP(如REST)上的约束提供了非常有说服力的案例。如果HTTP是开放网络的通用语言,那么我们必须用HTTP提出问题。
有一些很棒的库(例如Apache HttpComponents Client和OkHttp)以大致相同的方式工作。如果你没有特别考虑哪个库,但想要一个世界级的选择,并且已经在使用Spring,你可以使用Spring Webflux中基于Netty的非阻塞HTTP客户端WebClient
。
WebClient
是RestTemplate
的响应式非阻塞替代方案。我喜欢WebClient
,因为它是非阻塞的:用于发出网络请求的客户端线程不会被阻塞等待网络服务的响应。这意味着更好的可扩展性——线程可以自由地用于其他事情。我还喜欢WebClient
,因为它使用Reactive Streams API,使组合变得更容易。我们在上一个示例中已经看到了一些。
你可以使用WebClient
与任何旧的HTTP端点通信,而不仅仅是那些在服务器端使用非阻塞或响应式API编写的端点。更好的是,你甚至可以在非响应式代码中使用WebClient
。如果——听我说——如果有人想使用WebClient
,但不能使用完整的响应式Spring Web栈,会怎样?
问我啊!我无法想象你为什么不想使用响应式HTTP栈。即使你不想使用也没关系,因为单独使用WebClient
仍然有很多价值。你可以使用WebClient
进行一个或多个HTTP调用,然后并发地组合结果。对于简单的分散收集类型的组合来说,它是理想的选择。即使你没有使用响应式HTTP运行时(例如,如果你在Servlet环境中运行),这也是你自然想要做的事情。
你需要以下依赖项才能在类路径上获得WebClient
。
org.springframework.boot
: spring-boot-starter-webflux
让我们来看一些代码。这个示例同时执行两件不同的事情:
package bootiful.httpclient.webclient;
import lombok.ToString;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.MediaType;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import java.net.URI;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import static java.nio.file.StandardOpenOption.CREATE;
@EnableAsync
@SpringBootApplication
public class BootifulApplication {
public static void main(String[] args) {
SpringApplication.run(BootifulApplication.class, args);
}
@Bean
WebClient webClient(WebClient.Builder builder) {
return builder//
.filter(//
(clientRequest, exchangeFunction) -> exchangeFunction//
.exchange(clientRequest)//
.doOnNext(response -> System.out.println("got a WebClient response: " + response))//
) //
.build();
}
@Bean
ApplicationListener<ApplicationReadyEvent> ready(@Value("file://${user.home}/Desktop/output.zip") Path output,
WebClient client) {
return event -> {
// initialize a new Spring Boot project .zip archive
Mono<DataBuffer> db = client.get()//
.uri(URI.create("https://start.spring.io/starter.zip"))//
.accept(MediaType.APPLICATION_OCTET_STREAM)//
.retrieve()//
.bodyToMono(DataBuffer.class);
// gets written out to ~/output.zip
Mono<Boolean> write = DataBufferUtils.write(db, output, CREATE).thenReturn(true);
// enumerate all the active Spring projects using the
// JSON API while we're at it...
Mono<ProjectsResponse> json = client//
.get()//
.uri(URI.create("https://springframework.org.cn/api/projects"))//
.retrieve()//
.bodyToMono(ProjectsResponse.class);
// look ma! No threading code! this will launch both network
// calls (the .zip and the json) at the same time
Mono.zip(write, json).subscribe(tuple -> enumerate(tuple.getT2()));
};
}
private void enumerate(ProjectsResponse pr) {
pr._embedded //
.projects //
.stream() //
.filter(p -> p.status.equalsIgnoreCase("active")) //
.forEach(project -> System.out.println(project.toString()));
}
}
@ToString
class ProjectsResponse {
public Embedded _embedded = new Embedded();
@ToString
public static class Project {
public String name, slug, status, repositoryUrl;
}
@ToString
public static class Embedded {
public Collection<Project> projects = new ArrayList<>();
}
}
如果你想引入WebClient
,但_不想_使用其余的响应式Web栈,那么你需要告诉Spring Boot。否则,Spring Boot将尝试启动一个基于Netty的Spring Webflux环境。你需要在你的application.properties
文件中进行以下配置。
spring.main.web-application-type=none
你喜欢这种一目了然的方法吗?你学到什么了吗?一如既往,我很乐意听到你的反馈,所以请在Twitter上(@starbuxman)告诉我!我将带来另一期《YMNNALFT》,所以请不要错过。