
本文介绍如何在 spring batch 中高效、可靠地处理同一目录下多个 json 文件(如按国家/地区分组的公司数据),通过“每文件一作业实例”策略替代单步多 reader 的错误尝试,兼顾并行性、可伸缩性与故障隔离能力。
在 Spring Batch 中,一个 Step 内部只允许配置一个 ItemReader 实例,这是框架的核心设计约束——Step 是事务边界和执行单元的基本单位,不支持运行时动态切换或并行启动多个 Reader。因此,试图在单个 Step 中为多个文件分配独立 Reader(例如“每个 JSON 文件一个 Reader”)不仅违背架构原则,也无法通过自定义 MultiResourcePartitioner 或排序逻辑绕过该限制。强行将多个文件塞入一个 Reader(如 MultiResourceItemReader)虽能读取,但会丧失文件级并行、失败隔离与进度追踪能力,与用户期望的“SG 文件优先、同国 alternate_id 文件先行”等有序并行处理目标背道而驰。
✅ 正确解法:采用“每文件一个 Job 实例(Job Instance per File)”模式
即:不追求单 Job 多 Reader,而是为每个待处理文件(如 sg_company_group_alternate_id.json)启动一个独立的 Job 实例,并通过唯一标识参数(如 input.file.path)区分。示例如下:
// 启动作业(例如在调度器或服务中批量触发)
for (String filePath : sortedFilePaths) { // 已按 SG→MY、alternate→primary 排序
JobParameters params = new JobParametersBuilder()
.addString("input.file.path", filePath)
.addLong("run.id", System.currentTimeMillis()) // 确保唯一性
.toJobParameters();
jobLauncher.run(fileProcessingJob, params);
}对应 Job 配置需声明参数化 Reader:
@Bean
@StepScope
public JsonItemReader<CompanyGroup> jsonFileReader(
@Value("#{jobParameters['input.file.path']}") String filePath) {
return new JsonItemReaderBuilder<CompanyGroup>()
.jsonObjectReader(new JacksonJsonObjectReader<>(CompanyGroup.class))
.resource(new FileSystemResource(filePath))
.name("jsonFileReader")
.build();
}? 关键优势说明:
- 天然并行:多个 Job 实例可由 TaskExecutor(如 ThreadPoolTaskExecutor)并发执行,无需 Partitioner 复杂编码;
- 精准容错:任一文件处理失败(如 JSON 格式错误),仅需重启对应 JobInstance,其他文件不受影响;
- 顺序可控:启动循环本身按预设规则(SG 先于 MY,_alternate_id 优先于主文件)调用,保证全局处理时序;
- 监控清晰:每个文件对应独立 JOB_INSTANCE_ID 和执行记录,便于日志追踪、重试审计与运维排查。
⚠️ 注意事项:
- 确保 JobParameters 中至少有一个 runtime 参数(如 input.file.path)作为 Job 实例标识符,避免重复启动报 JobInstanceAlreadyCompleteException;
- 若需强一致性(如所有文件必须全部成功才提交业务状态),应在 Job 层之上引入编排层(如 Spring Cloud Task + Saga 模式),而非在 Batch 内部耦合;
- 对于海量小文件场景,建议增加批量预检(如文件存在性、大小阈值校验)和限流控制(如 ThreadPoolTaskExecutor 设置 corePoolSize 和 queueCapacity)。
综上,放弃“单 Step 多 Reader”的思路,转向“单文件单 Job 实例”的范式,是 Spring Batch 中处理多源异构文件最健壮、最符合框架哲学的工程实践。










