自定义ItemReader的read方法与StepExecutionListener共存时未被调用排查
问题根因分析
- 空指针异常阻断执行流程:
myCustomReader类中dataList仅声明未实例化,beforeStep方法中直接调用dataList.add()会抛出NullPointerException。Spring Batch捕获该异常后,Step会直接进入结束流程,导致read()方法从未被执行。 - Listener执行特性:
StepExecutionListener.afterStep()方法无论Step成功还是失败都会触发,因此你能看到beforeStep和afterStep的日志,但看不到read()的日志。 - 移除Listener后的正常逻辑:当移除
StepExecutionListener实现时,beforeStep不会执行,若此时你在read()中隐式或显式初始化了dataList,则read()能正常被调用。
修复方案
方案一:初始化dataList
在类中直接初始化集合,或者在beforeStep方法中完成初始化,避免空指针异常:
public class myCustomReader implements ItemReader<List<String>>, StepExecutionListener { Logger logger = LogManager.getLogger(this); // 直接初始化dataList private List<String> dataList = new ArrayList<>(); @Override public List<String> read() throws Exception { logger.info("read called"); // 注意:需添加终止条件,避免无限循环 dataList.add("A"); dataList.add("B"); dataList.add("C"); dataList.add("D"); dataList.add("E"); // 示例:返回一次数据后返回null,结束Step return dataList.size() <= 5 ? dataList : null; } @Override public void beforeStep(StepExecution stepExecution) { logger.info("beforeStep called"); dataList.add("A"); dataList.add("B"); dataList.add("C"); dataList.add("D"); dataList.add("E"); } @Override public ExitStatus afterStep(StepExecution stepExecution) { logger.info("afterStep called"); return ExitStatus.COMPLETED; } }
方案二:优化read()方法逻辑(必做)
当前read()方法每次调用都会往集合添加元素且永远不返回null,会导致Spring Batch无限循环执行read操作。需添加终止条件,当没有更多数据时返回null:
private int readCount = 0; private final int MAX_READ_TIMES = 1; // 控制读取次数 @Override public List<String> read() throws Exception { logger.info("read called"); // 达到读取上限,返回null结束Step if (readCount >= MAX_READ_TIMES) { return null; } List<String> data = new ArrayList<>(); data.add("A"); data.add("B"); data.add("C"); data.add("D"); data.add("E"); readCount++; return data; }
方案三:职责分离(可选)
将StepExecutionListener的逻辑抽离到单独的类中,避免一个类同时承担读取和监听职责,降低代码耦合:
// 单独的Listener类 public class MyStepListener implements StepExecutionListener { Logger logger = LogManager.getLogger(this); @Override public void beforeStep(StepExecution stepExecution) { logger.info("beforeStep called"); // 处理原来的beforeStep逻辑 } @Override public ExitStatus afterStep(StepExecution stepExecution) { logger.info("afterStep called"); return ExitStatus.COMPLETED; } } // 仅实现ItemReader的Reader类 public class myCustomReader implements ItemReader<List<String>> { Logger logger = LogManager.getLogger(this); @Override public List<String> read() throws Exception { logger.info("read called"); List<String> dataList = new ArrayList<>(); dataList.add("A"); dataList.add("B"); dataList.add("C"); dataList.add("D"); dataList.add("E"); // 添加终止条件 return dataList; } }
然后在XML配置中显式注册Listener:
<batch:step id="stepdId"> <batch:tasklet task-executor="batchTaskExecuter"> <batch:chunk reader="myCustomReader" writer="myCustomWriter" commit-interval="10" /> <!-- 注册Listener --> <batch:listeners> <batch:listener ref="myStepListener" /> </batch:listeners> </batch:tasklet> </batch:step>
内容的提问来源于stack exchange,提问作者javatime
相关产品推荐
相关产品推荐

