Spring Batch如何避免无数据异常时创建输出文件?
Spring Batch 实现无异常数据时不生成输出文件
问题背景
我们有一个用于检查数据库数据的Spring Batch批处理作业:当数据存在异常时,向输出文件写入内容;无异常时则不写入。但目前无论数据是否异常,输出文件都会被创建,我们希望在数据正常时不生成输出文件。
当前配置XML
<batch:job id="DEFAULT_JOB_ID"> <batch:step id="step"> <batch:tasklet transaction-manager="transactionManager" start-limit="100"> <batch:chunk reader="dbReader01" processor="testProc" writer="classifierWriter01" commit-interval="100" > <batch:streams> <batch:stream ref="fileWriter01"/> </batch:streams> </batch:chunk> <batch:listeners> <batch:listener ref="defaultChunkListener" /> </batch:listeners> </batch:tasklet> </batch:step> <batch:listeners> <batch:listener ref="defaultJobExecutionListener" /> </batch:listeners> </batch:job> <bean id="classifierWriter01" class="org.springframework.batch.item.support.ClassifierCompositeItemWriter" scope="step"> <property name="classifier"> <bean class="org.springframework.classify.BackToBackPatternClassifier"> <property name="routerDelegate"> <bean class="src.test.DefaultClassifier" /> </property> <property name="matcherMap"> <util:map id="matcherMap" scope="step"> <!-- FLAG == 0 THEN write something --> <entry key="0" value-ref="fileWriter01" /> <!-- FLAG == 1 THEN write nothing --> <entry key="1" value-ref="dummyWriter" /> </util:map> </property> </bean> </property> </bean> <bean id="fileWriter01" class="org.springframework.batch.item.file.FlatFileItemWriter" scope="step"> <property name="encoding" value="UTF-8" /> <property name="resource" value="file:#{jobParameters['outFile']}" /> <property name="appendAllowed" value="false"/> <property name="shouldDeleteIfExists" value="true"/> <property name="lineAggregator"> <bean class="src.test.DelimitedLineAggregator"> <property name="delimiter" value="," /> <property name="allEnclosing" value="true" /> <property name="enclosure" value='"' /> <property name="fieldExtractor"> <bean class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor" > <property name="names" value="seq,errCode,errMsg" /> </bean> </property> </bean> </property> </bean> <bean id="dummyWriter" class="src.test.DummyItemWriter" scope="step"/>
核心问题
使用batch:streams配置fileWriter01时,输出文件会在作业启动时就被创建;如果不配置batch:streams,又会抛出异常:org.springframework.batch.item.WriterNotOpenException: Writer must be open before it can be written to。我们需要实现仅当有异常数据时才生成输出文件。
解决方案
1. 自定义延迟加载Writer(推荐)
不要把fileWriter01放到batch:streams中,而是自定义一个包装Writer,仅当有数据要写入时才初始化并打开FlatFileItemWriter,避免提前创建文件。
自定义Writer代码:
public class LazyFlatFileItemWriter<T> implements ItemWriter<T> { private FlatFileItemWriter<T> delegate; private boolean isOpen = false; @Override public void write(List<? extends T> items) throws Exception { if (items == null || items.isEmpty()) { return; } if (!isOpen) { delegate.open(new ExecutionContext()); isOpen = true; } delegate.write(items); } @PreDestroy public void close() throws Exception { if (isOpen) { delegate.close(); } } // 注入委托Writer的setter方法 public void setDelegate(FlatFileItemWriter<T> delegate) { this.delegate = delegate; } }
修改XML配置:
- 添加自定义Writer的Bean定义:
<bean id="lazyFileWriter" class="src.test.LazyFlatFileItemWriter" scope="step"> <property name="delegate" ref="fileWriter01" /> </bean>
- 修改
classifierWriter01的matcherMap,引用这个自定义Writer:
<util:map id="matcherMap" scope="step"> <!-- FLAG == 0 THEN write something --> <entry key="0" value-ref="lazyFileWriter" /> <!-- FLAG == 1 THEN write nothing --> <entry key="1" value-ref="dummyWriter" /> </util:map>
- 删除
batch:streams配置块:
<!-- 移除这段配置 --> <batch:streams> <batch:stream ref="fileWriter01"/> </batch:streams>
2. 作业结束后清理空文件(简单方案)
保留现有配置,在Job执行完成后检查输出文件,若为空则删除。通过自定义JobExecutionListener实现:
监听器代码:
public class CleanEmptyFileListener implements JobExecutionListener { private Resource outputResource; @Override public void beforeJob(JobExecution jobExecution) {} @Override public void afterJob(JobExecution jobExecution) { try { File file = outputResource.getFile(); if (file.exists() && file.length() == 0) { file.delete(); } } catch (IOException e) { // 日志记录异常,根据实际情况处理 e.printStackTrace(); } } // 设置输出资源的setter方法 public void setOutputResource(Resource outputResource) { this.outputResource = outputResource; } }
修改XML配置:
- 添加监听器Bean:
<bean id="cleanEmptyFileListener" class="src.test.CleanEmptyFileListener" scope="step"> <property name="outputResource" value="file:#{jobParameters['outFile']}" /> </bean>
- 将监听器添加到Job的监听器列表:
<batch:job id="DEFAULT_JOB_ID"> <!-- 原有Step配置 --> <batch:listeners> <batch:listener ref="defaultJobExecutionListener" /> <batch:listener ref="cleanEmptyFileListener" /> </batch:listeners> </batch:job>
关键说明
FlatFileItemWriter本身可以自行创建文件,但默认在open()方法中执行创建操作。通过batch:streams配置会让Spring Batch在Step启动时就调用所有stream中Writer的open()方法,导致文件提前创建。- 去掉
batch:streams后,必须确保Writer在第一次写入前被正确打开,自定义延迟Writer就是通过延迟调用open()方法实现按需创建文件。
内容的提问来源于stack exchange,提问作者l4ru3
相关产品推荐
相关产品推荐

