如何高效使用Spring WebFlux的get()和post()方法处理响应(替代block()方法)
如何高效使用Spring WebFlux的get()和post()方法处理响应(替代block()方法)
嘿,我完全懂你的痛点!用block()确实能快速拿到结果,但这完全违背了Spring WebFlux的异步非阻塞设计初衷——不仅会阻塞当前线程,还会拖垮高并发场景下的性能。结合你用Spring Boot 3、JDK17的环境,还有生成文件、提取JSON关键数据的业务需求,我给你几个替代block()的实用方案:
方案一:用subscribe()异步处理独立逻辑
如果你的业务逻辑不需要同步等待请求结果再继续,直接用subscribe()来异步处理响应就行,全程不会阻塞调用线程:
针对GET请求生成文件的场景
webclient.get() .uri("https://testclient/") .headers(headers -> /* 这里传入你的HttpHeaders设置逻辑 */) .retrieve() .bodyToMono(byte[].class) .subscribe(bytes -> { // 异步写入文件 try (FileOutputStream fos = new FileOutputStream("output.file")) { fos.write(bytes); } catch (IOException e) { // 按需处理文件写入异常 e.printStackTrace(); } }, error -> { // 处理请求失败的情况,比如打印日志或抛出业务异常 error.printStackTrace(); });
针对POST请求提取JSON关键数据的场景
webclient.post() .uri("https://testclient/") .contentType(MediaType.MULTIPART_FORM_DATA) .headers(headers -> /* 这里传入你的HttpHeaders设置逻辑 */) .retrieve() .bodyToMono(String.class) .subscribe(response -> { // 解析JSON提取关键数据 ObjectMapper mapper = new ObjectMapper(); try { JsonNode jsonNode = mapper.readTree(response); String targetKeyData = jsonNode.get("你需要的key").asText(); // 把拿到的关键数据传给后续的Java应用逻辑 } catch (JsonProcessingException e) { // 处理JSON解析异常 e.printStackTrace(); } }, error -> { // 处理请求失败的情况 error.printStackTrace(); });
方案二:在WebFlux Controller中直接返回Mono/Flux
如果这段代码是写在WebFlux的Controller层,要对外提供服务的话,直接返回Mono/Flux即可,框架会自动处理异步订阅和响应,完全贴合非阻塞模型:
针对GET请求返回文件资源
@GetMapping("/download-target-file") public Mono<Resource> downloadFile() { return webclient.get() .uri("https://testclient/") .headers(headers -> /* 你的Header设置逻辑 */) .retrieve() .bodyToMono(byte[].class) .map(bytes -> new ByteArrayResource(bytes)); }
针对POST请求返回提取后的关键数据
先定义一个DTO来封装关键数据:
public class KeyDataDto { private String targetKey; // 构造方法、getter、setter public KeyDataDto(String targetKey) { this.targetKey = targetKey; } public String getTargetKey() { return targetKey; } }
然后在Controller中返回封装好的Mono:
@PostMapping("/fetch-key-data") public Mono<KeyDataDto> fetchKeyData() { return webclient.post() .uri("https://testclient/") .contentType(MediaType.MULTIPART_FORM_DATA) .headers(headers -> /* 你的Header设置逻辑 */) .retrieve() .bodyToMono(String.class) .map(response -> { ObjectMapper mapper = new ObjectMapper(); try { JsonNode jsonNode = mapper.readTree(response); return new KeyDataDto(jsonNode.get("你需要的key").asText()); } catch (JsonProcessingException e) { throw new RuntimeException("解析响应JSON失败", e); } }); }
方案三:用flatMap()串联多步异步操作
如果你的业务逻辑需要依赖多个请求的结果(比如先用POST拿到key,再用这个key调用GET),用flatMap()就能实现无阻塞的异步串联:
webclient.post() .uri("https://testclient/") .contentType(MediaType.MULTIPART_FORM_DATA) .headers(headers -> /* 你的Header设置逻辑 */) .retrieve() .bodyToMono(String.class) .map(response -> { // 从POST响应中提取key ObjectMapper mapper = new ObjectMapper(); try { return mapper.readTree(response).get("你需要的key").asText(); } catch (JsonProcessingException e) { throw new RuntimeException(e); } }) .flatMap(extractedKey -> { // 用拿到的key发起GET请求 return webclient.get() .uri(uriBuilder -> uriBuilder.path("https://testclient/").queryParam("key", extractedKey).build()) .headers(headers -> /* 你的Header设置逻辑 */) .retrieve() .bodyToMono(byte[].class); }) .subscribe(finalBytes -> { // 处理最终的GET响应,生成文件 try (FileOutputStream fos = new FileOutputStream("file-with-key.data")) { fos.write(finalBytes); } catch (IOException e) { e.printStackTrace(); } });
一些额外的注意事项
- 尽量避免在同步代码中混合调用WebFlux异步方法,实在要同步的话,也得注意线程池配置,但还是优先推荐异步方案
- 耗时的操作(比如大文件写入、复杂JSON解析)如果可能的话,最好包装成
Mono/Flux,避免阻塞WebFlux的事件循环线程 - 异常处理要优雅,除了
subscribe()里的错误回调,还可以用onErrorResume()、onErrorReturn()等操作符自定义异常逻辑,比如:
.bodyToMono(byte[].class) .onErrorResume(error -> { // 自定义异常处理,返回默认值或抛出业务异常 return Mono.just(new byte[0]); }) .subscribe(...)
备注:内容来源于stack exchange,提问作者Dev9321
相关产品推荐
相关产品推荐

