You need to enable JavaScript to run this app. Spring Batch下JsonItemReader读取JSON后删除文件报占用问题咨询
优惠活动
大模型
产品
解决方案
定价
更多

Spring Batch下JsonItemReader读取JSON后删除文件报占用问题咨询

问题根因

文件删除时提示目标文件处于打开状态,本质是两个配置疏漏叠加导致的:

  • Spring Batch 4.3.0 版本之前存在已知缺陷:JsonItemReader 的 close() 方法未主动调用关联 JsonObjectReader 的资源释放逻辑,导致底层包装的 FileInputStream、Jackson JsonParser 始终持有目标文件的读锁,未在Step执行结束后正常销毁。
  • 删除逻辑绑定在 JobExecutionListener.afterJob 回调时,存在时序偏差:部分场景下回调触发早于Spring Batch内部的ItemStream组件销毁流程,此时读取句柄尚未完全释放。
分步修复方案
  • 第一步:强制绑定ItemReader的资源关闭钩子
    不要完全依赖框架自动触发的关闭逻辑,构建JsonItemReader时显式增加资源销毁回调,兜底覆盖所有关联流的释放:
    @Bean
    @StepScope
    public JsonItemReader<MyObj> jsonItemReader() {
        Path sourcePath = Paths.get("/data/input/source.json");
        FileSystemResource resource = new FileSystemResource(sourcePath);
        JacksonJsonObjectReader<MyObj> jsonObjectReader = new JacksonJsonObjectReader<>(MyObj.class);
        // 绑定自定义ObjectMapper映射规则
        jsonObjectReader.setMapper(customObjectMapper);
        
        JsonItemReader<MyObj> reader = new JsonItemReaderBuilder<MyObj>()
            .resource(resource)
            .jsonObjectReader(jsonObjectReader)
            .name("jsonItemReader")
            .build();
        
        // 显式注入close逻辑,强制释放关联资源
        reader.setStreams(new ItemStream[] {
            new ItemStreamSupport() {
                @Override
                public void close(ExecutionContext executionContext) {
                    try {
                        // 强制调用JsonObjectReader的close,释放内部持有的JsonParser和关联的FileInputStream
                        jsonObjectReader.close();
                    } catch (IOException e) {
                        log.warn("JSON读取资源关闭异常", e);
                    }
                }
            }
        });
        return reader;
    }
    
  • 第二步:迁移删除逻辑到StepExecutionListener的afterStep回调
    该回调的触发时机为当前Step所有ItemStream组件(包括ItemReader)执行完close()之后,完全不存在时序偏差问题。
    自定义清理监听器实现:
    public class SourceFileCleanStepListener implements StepExecutionListener {
        private final Path sourceFilePath;
        
        public SourceFileCleanStepListener(Path sourceFilePath) {
            this.sourceFilePath = sourceFilePath;
        }
        
        @Override
        public ExitStatus afterStep(StepExecution stepExecution) {
            // 仅当Step执行状态为COMPLETED时触发删除
            if (stepExecution.getStatus() == BatchStatus.COMPLETED) {
                try {
                    Files.deleteIfExists(sourceFilePath);
                    log.info("源文件删除成功,路径:{}", sourceFilePath);
                } catch (IOException e) {
                    log.error("源文件删除失败", e);
                    return ExitStatus.FAILED;
                }
            } else {
                log.warn("Step执行状态为{},跳过源文件删除", stepExecution.getStatus());
            }
            return ExitStatus.COMPLETED;
        }
    }
    
    将监听器绑定到对应Step,而非Job层面:
    @Bean
    public Step jsonParseStep(JobRepository jobRepository, PlatformTransactionManager txManager,
                              JsonItemReader<MyObj> jsonItemReader, ItemWriter<MyObj> myObjItemWriter) {
        return new StepBuilder("jsonParseStep", jobRepository)
            .<MyObj, MyObj>chunk(100, txManager)
            .reader(jsonItemReader)
            .writer(myObjItemWriter)
            .listener(new SourceFileCleanStepListener(Paths.get("/data/input/source.json")))
            .build();
    }
    
  • 第三步:升级Spring Batch版本至4.3.0以上
    官方在该版本中修复了*JsonItemReader.close()未委托调用JsonObjectReader.close()*的历史缺陷,升级后框架会自动完成底层流的释放,无需额外编写兜底关闭逻辑。
  • 兜底防锁校验
    若升级后仍存在偶发文件锁问题,可在删除逻辑前增加短暂的句柄释放校验,避免直接硬删:
    // 最多等待3秒,每500ms检测一次文件锁状态
    long waitEnd = System.currentTimeMillis() + 3000;
    while (System.currentTimeMillis() < waitEnd) {
        try (FileInputStream fis = new FileInputStream(sourceFilePath.toFile())) {
            // 能正常打开流说明句柄已释放
            break;
        } catch (IOException e) {
            Thread.sleep(500);
        }
    }
    // 执行删除
    Files.deleteIfExists(sourceFilePath);
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.27 18:36:04