
MongoDB Java 驱动返回的 Iterator(实际为 MongoCursor)实现了 AutoCloseable,若未显式关闭,会导致底层游标、网络连接和内存资源长期占用,引发资源泄漏。
mongodb java 驱动返回的 `iterator`(实际为 `mongocursor`)实现了 `autocloseable`,若未显式关闭,会导致底层游标、网络连接和内存资源长期占用,引发资源泄漏。
在 MongoDB Java Driver 3.0+ 中,FindIterable.iterator() 返回的对象是 MongoCursor
你当前的代码将原始 MongoCursor
✅ 正确解决方案:确保游标可关闭且被关闭
方案一:使用 try-with-resources(推荐,适用于消费端控制生命周期)
让调用方负责资源管理,需暴露可关闭的迭代器类型:
@Override
public Iterable<String> getErrorUrls(int pCrawlId) {
String collectionName = "collectionName";
MongoCollection<Document> c = mongoDB.getCollection(collectionName);
Bson filter = Filters.not(eq("exploreStatus", "OK"));
Bson projection = fields(include("url"), excludeId());
FindIterable<Document> docs = c.find(filter).projection(projection).batchSize(1000);
return () -> {
MongoCursor<Document> cursor = docs.iterator(); // 注意:此处获取的是可关闭的游标
return new Iterator<String>() {
@Override
public boolean hasNext() {
return cursor.hasNext();
}
@Override
public String next() {
Document doc = cursor.next();
return doc.getString("url");
}
// 关键:实现 close() 并委托给 cursor
public void close() {
cursor.close();
}
};
};
}然后调用时使用 try-with-resources:
立即学习“Java免费学习笔记(深入)”;
try (Iterable<String> urls = getErrorUrls(123)) {
for (String url : urls) {
System.out.println(url);
}
} // 自动触发 close() → cursor.close()⚠️ 注意:Iterable 本身不可关闭,因此需配合自定义 AutoCloseableIterable 或使用 StreamSupport.stream(..., false) + onClose()(见下文进阶方案)。
方案二:封装为 AutoCloseableIterator(更精确、更安全)
定义一个支持关闭的迭代器包装类:
public class CloseableIterator<T> implements Iterator<T>, AutoCloseable {
private final Iterator<T> delegate;
private final Runnable onClose;
public CloseableIterator(Iterator<T> delegate, Runnable onClose) {
this.delegate = delegate;
this.onClose = onClose;
}
@Override
public boolean hasNext() { return delegate.hasNext(); }
@Override
public T next() { return delegate.next(); }
@Override
public void close() { onClose.run(); }
}改造主方法:
@Override
public CloseableIterator<String> getErrorUrls(int pCrawlId) {
String collectionName = "collectionName";
MongoCollection<Document> c = mongoDB.getCollection(collectionName);
Bson filter = Filters.not(eq("exploreStatus", "OK"));
Bson projection = fields(include("url"), excludeId());
MongoCursor<Document> cursor = c.find(filter).projection(projection).batchSize(1000).iterator();
Iterator<String> stringIterator = new Iterator<String>() {
@Override
public boolean hasNext() { return cursor.hasNext(); }
@Override
public String next() { return cursor.next().getString("url"); }
};
return new CloseableIterator<>(stringIterator, cursor::close);
}调用示例(显式关闭):
try (CloseableIterator<String> it = getErrorUrls(123)) {
while (it.hasNext()) {
String url = it.next();
process(url);
}
} // 自动调用 cursor.close()方案三:避免暴露迭代器 —— 改用回调式 API(彻底规避泄漏风险)
若业务逻辑允许,推荐采用“拉取即处理”模式,由方法内部完成迭代与关闭:
@Override
public void forEachErrorUrl(int pCrawlId, Consumer<String> action) {
String collectionName = "collectionName";
MongoCollection<Document> c = mongoDB.getCollection(collectionName);
Bson filter = Filters.not(eq("exploreStatus", "OK"));
Bson projection = fields(include("url"), excludeId());
try (MongoCursor<Document> cursor = c.find(filter)
.projection(projection)
.batchSize(1000)
.iterator()) {
while (cursor.hasNext()) {
String url = cursor.next().getString("url");
action.accept(url);
}
} // cursor 自动关闭
}? 总结与最佳实践
- ❗ MongoDB 的 MongoCursor 是有状态的、有资源的 AutoCloseable 对象,绝不能“一借了之”;
- ? 封装迭代器时,必须显式提供关闭能力(如实现 AutoCloseable 或提供 close() 方法);
- ✅ 优先使用 try-with-resources 管理生命周期,确保 close() 在异常/正常路径下均被执行;
- ? 避免返回裸 Iterator 包装对象——它掩盖了资源契约,破坏了 RAII 原则;
- ? 若需解耦 MongoDB 依赖,应通过接口抽象(如 CloseableIterable
)而非简单类型转换。
遵循以上原则,即可彻底消除 Eclipse 警告,并保障系统在高负载下的稳定性与可观测性。


















