You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Batch 6.x统计成功处理项并打印摘要的最佳实践是什么?

Spring Batch 6.x 项统计与摘要打印最佳实践

一、Spring Batch 内置的项跟踪机制

Spring Batch 原生提供StepExecution和JobExecution对象跟踪项处理状态,核心统计字段完全覆盖你的需求:

  • readCount: 已读取的项总数
  • writeCount: 成功写入且事务提交的项数(自动排除回滚、跳过的项)
  • skipCount: 被跳过的项数(含处理/写入失败且触发跳过规则的项)
  • rollbackCount: 事务回滚次数

这些统计会自动持久化到元数据存储(如数据库),重启作业时会基于历史执行状态续算,无需额外处理基础统计逻辑。

二、统计位置选择:Writer 阶段更可靠

绝对不要在Processor中统计——Processor处理完成的项可能在Writer阶段失败导致回滚,这类项不能算作成功。只有Writer成功提交后,项才是真正处理完成的,推荐两种实现方式:

1. 直接使用内置 writeCount

如果仅需统计成功项总数,StepExecution的writeCount是最准确的数值:它仅统计成功写入且事务提交的项,回滚的项不会被计入,重试成功的项也只会被统计一次。

2. 自定义细粒度统计

如果需要按业务分类统计成功项,可实现ItemWriteListener,在afterWrite方法中累加统计值,并将结果存入ExecutionContext(保证重启时能续算):

@Component
public class CustomItemWriteListener implements ItemWriteListener<BusinessItem> {
    @Override
    public void afterWrite(List<? extends BusinessItem> items) {
        StepExecution stepExecution = StepSynchronizationManager.getContext().getStepExecution();
        ExecutionContext executionContext = stepExecution.getExecutionContext();
        
        // 从上下文读取当前统计值,初始为0
        int validItemCount = executionContext.getInt("validItemCount", 0);
        // 按业务规则过滤统计(示例:统计状态为"VALID"的项)
        long count = items.stream().filter(item -> "VALID".equals(item.getStatus())).count();
        validItemCount += count;
        executionContext.putInt("validItemCount", validItemCount);
    }
}

将监听器注册到Step:

@Bean
public Step businessStep(ItemReader<BusinessItem> reader, ItemProcessor<BusinessItem, BusinessItem> processor,
                         ItemWriter<BusinessItem> writer, CustomItemWriteListener listener) {
    return new StepBuilder("businessStep", jobRepository)
            .<BusinessItem, BusinessItem>chunk(20, transactionManager)
            .reader(reader)
            .processor(processor)
            .writer(writer)
            .listener(listener)
            .build();
}

三、重启与重试的处理

1. 重启作业

Spring Batch 会自动从上次失败的位置恢复执行,内置的writeCount会自动累加历史成功项数。自定义统计时,只要将值存入ExecutionContext(它会被持久化到元数据),重启时就能自动读取并续算,无需额外逻辑。

2. 重试处理

当配置重试规则(如通过RetryTemplate或@Retryable),只有重试成功的项才会被写入并计入writeCount;如果重试失败触发跳过规则,该项会被计入skipCount,不会被统计到成功项中。自定义统计的afterWrite方法仅在项成功写入后触发,重试过程中失败的项不会触发该方法,因此统计结果天然准确。

四、作业完成后打印摘要

实现JobExecutionListener,在afterJob方法中获取统计数据并打印到控制台:

@Component
public class JobCompletionSummaryListener implements JobExecutionListener {
    @Override
    public void afterJob(JobExecution jobExecution) {
        if (BatchStatus.COMPLETED.equals(jobExecution.getStatus())) {
            System.out.println("===== 作业执行完成摘要 =====");
            for (StepExecution stepExecution : jobExecution.getStepExecutions()) {
                System.out.printf("步骤 [%s]: 读取项数=%d, 成功处理项数=%d, 跳过项数=%d, 回滚次数=%d%n",
                        stepExecution.getStepName(),
                        stepExecution.getReadCount(),
                        stepExecution.getWriteCount(),
                        stepExecution.getSkipCount(),
                        stepExecution.getRollbackCount());
            }
            // 打印自定义统计(示例)
            StepExecution targetStep = jobExecution.getStepExecutions().stream()
                    .filter(step -> "businessStep".equals(step.getStepName()))
                    .findFirst().orElse(null);
            if (targetStep != null) {
                int validItemCount = targetStep.getExecutionContext().getInt("validItemCount", 0);
                System.out.printf("有效业务项统计: %d%n", validItemCount);
            }
        }
    }
}

将监听器注册到Job:

@Bean
public BusinessJob businessJob(JobRepository jobRepository, Step businessStep, JobCompletionSummaryListener listener) {
    return new JobBuilder("businessJob", jobRepository)
            .start(businessStep)
            .listener(listener)
            .build();
}

内容的提问来源于stack exchange,提问作者Gorock

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 10:40:36