Java网络抓取中函数式接口用于解析后回调,如Consumer<Document>或自定义ParseResultHandler,配合CompletableFuture实现异步、解耦、可插拔的通知链路,并需注意线程安全与异常处理。

Java 中函数式接口在网络抓取工具中,常被用作解析完成后的回调入口,实现轻量、解耦的异步通知链路。核心思路是:把“解析完成后该做什么”抽象成一个函数式接口(如 Consumer<Document> 或自定义接口),在解析线程结束时通过 CompletableFuture 或线程池触发它,避免阻塞抓取主流程。
用 Consumer<Document> 作为默认回调接口
Jsoup 等解析库返回 org.jsoup.nodes.Document,天然适合用 java.util.function.Consumer 接收。它无返回值、单参数,语义清晰——“收到 Document 后执行某操作”。
- 定义抓取器时,接受
Consumer<Document>作为构造参数或方法参数 - 解析成功后,在非主线程(如 ForkJoinPool.commonPool())中调用
callback.accept(doc) - 使用者可用 Lambda 快速定制后续动作,比如提取标题、存入数据库、发消息:
String title = document.title();
System.out.println("抓到标题:" + title);
notifyService.send(title);
}
自定义函数式接口增强语义与错误处理
仅靠 Consumer 无法区分成功/失败,也不带上下文信息。可定义如下的函数式接口:
public interface ParseResultHandler {
void onParseSuccess(String url, Document doc);
default void onParseFailure(String url, Exception e) {
// 可空实现,由调用方选择是否重写
}
}
这样既保持单抽象方法特性(仍是函数式接口),又明确分离关注点。使用时可通过方法引用或 Lambda 实现,例如:
立即学习“Java免费学习笔记(深入)”;
结合 CompletableFuture 构建异步通知链
真实抓取场景中,往往需要串行或并行触发多个后续动作(如解析 → 存库 → 发消息 → 更新索引)。函数式接口配合 CompletableFuture 能自然形成链式调用:
- 抓取方法返回
CompletableFuture<Document> - 调用
.thenAccept(doc -> {...})或.thenCompose(...)注入业务逻辑 - 每个环节仍可接收函数式接口,实现策略可插拔
例如:fetch(url).thenAccept(handler::onParseSuccess),其中 handler 是实现了 ParseResultHandler 的实例。
避免常见陷阱:线程安全与异常吞没
函数式接口本身不保证线程安全,异步执行时需注意:
- 回调中若操作共享资源(如 List、Map),应加锁或使用并发容器
- Lambda 内未捕获的异常会终止 CompletableFuture 链,建议统一用
exceptionally()或handle()拦截 - 不要在回调里做耗时同步操作(如大文件写入),应再提交到专用线程池
不复杂但容易忽略。


















