Spring Batch自定义Item Reader返回列表致作业无限循环求建议
嘿,这个问题我之前帮不少开发者排查过,核心就是违反了Spring Batch ItemReader的核心契约导致的无限循环,我来给你拆解下问题和解决方案:
问题根源
Spring Batch的ItemReader有个铁规则:read()方法必须在没有更多数据可读取时返回null,否则框架会一直认为还有数据要处理,反复调用read(),直接导致作业无限循环。
你的代码里有两个致命问题:
- 用了静态变量
CCTransDlyLg:一旦从DAO拿到数据,这个变量就永远不会被置空或重置,每次调用read()都会返回同一个列表,永远触不到返回null的终止条件。 - 即使是批量返回
List<T>的场景,你也没做“读取完成”的标记,框架根本不知道什么时候该停止。
解决办法
根据你的需求,有两种常用的修正方案:
方案一:改成返回单个Item(更符合Spring Batch常规用法)
如果你的处理器是处理单个数据项,建议把ItemReader<List<T>>改成ItemReader<T>,这样更贴合框架设计,也不容易踩坑:
public class CustomReader implements ItemReader<T> { @Autowired private CustomDao customDao; // 用实例变量存迭代器,绝对别用静态变量(多实例/重启作业会出大问题) private Iterator<T> dataIterator; @Override public T read() throws Exception { // 第一次调用时初始化迭代器 if (dataIterator == null) { List<T> dataList = customDao.getList(); dataIterator = dataList.iterator(); } // 有数据就返回,没数据就返回null终止 if (dataIterator.hasNext()) { log.info("Reading next item from dataset"); return dataIterator.next(); } else { // 重置迭代器,方便作业重启(可选但推荐) dataIterator = null; return null; } } }
方案二:保持批量返回List,但正确处理终止逻辑
如果你确实需要一次性返回整个列表(比如后续是批量处理器),那必须在读取完成后返回null,同时去掉静态变量:
public class CustomReader implements ItemReader<List<T>> { @Autowired private CustomDao customDao; // 用实例变量标记是否已经读取过数据 private boolean hasReadData = false; @Override public List<T> read() throws Exception { // 已经读过数据?直接返回null终止 if (hasReadData) { return null; } List<T> dataList = customDao.getList(); // 如果DAO返回空列表,直接终止 if (dataList == null || dataList.isEmpty()) { hasReadData = true; return null; } // 标记为已读取,下次调用就返回null hasReadData = true; log.info("Fetched batch data, size: {}", dataList.size()); return dataList; } }
额外提醒
- 绝对别用静态变量存读取状态或数据:静态变量是JVM全局共享的,多作业实例运行、或者作业重启时,会导致状态混乱,要么重复读数据,要么读不到新数据。
- 严格遵守
ItemReader契约:不管是单次还是批量读取,read()返回null是框架判断读取结束的唯一标准,这一点不能打折扣。 - 考虑作业重启:如果你的作业需要支持重启,记得在读取结束后重置状态(比如方案一里的
dataIterator = null),这样重启时能重新拉取数据。
内容的提问来源于stack exchange,提问作者Priya
相关产品推荐
相关产品推荐

