1. Spring WebFlux
Spring框架中包含的原始Web框架Spring Web MVC是专门为Servlet API和Servlet容器而构建的。反应性堆栈Web框架Spring WebFlux在更高版本5.0中添加。它是完全非阻塞的,支持 Reactive Streams,并在Netty,Undertow和Servlet 3.1+容器等服务器上运行。
WebFlux需要Reactor作为核心依赖项,但是它可以通过Reactive Streams与其他React库进行互操作。通常,WebFlux API接受平原Publisher 作为输入,在内部将其适应于Reactor类型,使用它,然后返回Flux或Mono作为输出。
2. WebClient
Spring WebFlux包括WebClient对HTTP请求的响应式,非阻塞式。WebFlux客户端和服务器依靠相同的非阻塞编解码器对请求和响应内容进行编码和解码。
内部WebClient
委托给HTTP客户端库。默认情况下,它使用 Reactor Netty,内置了对Jetty 反应式HttpClient的支持,其他的则可以通过插入ClientHttpConnector
。
2.1 创建webclient
创建最简单方法WebClient是通过静态工厂方法之一:
- WebClient.create()
- WebClient.create(String baseUrl)
上面的方法使用HttpClient具有默认设置的Reactor Netty ,并且期望 io.projectreactor.netty:reactor-netty在类路径上。
您还可以使用WebClient.builder()其他选项:
uriBuilderFactory:自定义UriBuilderFactory用作基本URL。
defaultHeader:每个请求的标题。
defaultCookie:针对每个请求的Cookie。
defaultRequest:Consumer自定义每个请求。
filter:针对每个请求的客户端过滤器。
exchangeStrategies:HTTP消息读取器/写入器定制。
clientConnector:HTTP客户端库设置。
java示例代码:
WebClient client = WebClient.builder()
.exchangeStrategies(builder -> {
return builder.codecs(codecConfigurer -> {
//...
});
})
.build();
一旦建立,WebClient实例是不可变的。但是,您可以克隆它并构建修改后的副本,而不会影响原始实例,如以下示例所示:
WebClient client1 = WebClient.builder()
.filter(filterA).filter(filterB).build();
WebClient client2 = client1.mutate()
.filter(filterC).filter(filterD).build();
// client1 has filterA, filterB
// client2 has filterA, filterB, filterC, filterD
2.2 retrieve
该retrieve()方法是获取响应主体并对其进行解码的最简单方法。以下示例显示了如何执行此操作:
Mono<Person> result = client.get()
.uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
.retrieve()
.onStatus(HttpStatus::is4xxClientError, response -> ...)
.onStatus(HttpStatus::is5xxServerError, response -> ...)
.bodyToMono(Person.class);
默认情况下,4XX或5xx状态代码的应答导致 WebClientResponseException或它的HTTP状态的具体子类之一,比如 WebClientResponseException.BadRequest,WebClientResponseException.NotFound和其他人。您还可以使用该onStatus方法来自定义所产生的异常
2.3。 exchange()
该exchange()方法比该方法提供更多的控制retrieve。以下示例等效于retrieve()但也提供对的访问ClientResponse:
ono<ResponseEntity<Person>> result = client.get()
.uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
.exchange()
.flatMap(response -> response.toEntity(Person.class));
请注意(与不同retrieve()),对于exchange(),没有4xx和5xx响应的自动错误信号。您必须检查状态码并决定如何进行。
与相比retrieve(),当使用时exchange(),应用程序有责任使用任何响应内容,而不管情况如何(成功,错误,意外数据等),否则会导致内存泄漏.
2.4 Request Body
Mono<Person> personMono = ... ;
Mono<Void> result = client.post()
.uri("/persons/{id}", id)
.contentType(MediaType.APPLICATION_JSON)
.body(personMono, Person.class)
.retrieve()
.bodyToMono(Void.class);
2.5 表单提交
MultiValueMap<String, String> formData = ... ;
Mono<Void> result = client.post()
.uri("/path", id)
.bodyValue(formData)
.retrieve()
.bodyToMono(Void.class);
或者
import static org.springframework.web.reactive.function.BodyInserters.*;
Mono<Void> result = client.post()
.uri("/path", id)
.body(fromFormData("k1", "v1").with("k2", "v2"))
.retrieve()
.bodyToMono(Void.class);
2.5. Client Filters
您可以ExchangeFilterFunction通过来注册客户端过滤器()WebClient.Builder ,以拦截和修改请求,可以用于登录验证,如以下示例所示:
WebClient client = WebClient.builder()
.filter((request, next) -> {
ClientRequest filtered = ClientRequest.from(request)
.header("foo", "bar")
.build();
return next.exchange(filtered);
})
.build();
// or
import static org.springframework.web.reactive.function.client.ExchangeFilterFunctions.basicAuthentication;
WebClient client = WebClient.builder()
.filter(basicAuthentication("user", "password"))
.build();
### 2.6 block同步操作
```java
Person person = client.get().uri("/person/{id}", i).retrieve()
.bodyToMono(Person.class)
.block();
List<Person> persons = client.get().uri("/persons").retrieve()
.bodyToFlux(Person.class)
.collectList()
.block();
但是,如果需要进行多个调用,则避免单独阻塞每个响应,而等待合并的结果会更有效:
Mono<Person> personMono = client.get().uri("/person/{id}", personId)
.retrieve().bodyToMono(Person.class);
Mono<List<Hobby>> hobbiesMono = client.get().uri("/person/{id}/hobbies", personId)
.retrieve().bodyToFlux(Hobby.class).collectList();
Map<String, Object> data = Mono.zip(personMono, hobbiesMono, (person, hobbies) -> {
Map<String, String> map = new LinkedHashMap<>();
map.put("person", person);
map.put("hobbies", hobbies);
return map;
})
.block();
以上仅是一个示例。还有许多其他模式和运算符可用于构建响应式管道,该响应式管道可进行许多远程调用(可能是嵌套的,相互依赖的),而不会阻塞到最后。