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

Spring Batch:使用ClassifierCompositeItemWriter避免空文件写入表头

问题描述

项目采用Spring XML配置,使用ClassifierCompositeItemWriter根据某列数据分类,将数据写入四个不同文件(每个文件数据唯一)。当前遇到的问题是:当某个文件没有符合分类器的数据时,仍会写入表头,希望这类空文件保持纯空状态,不生成表头。

现有配置及代码如下:

XML配置

<bean id="someCvsWriter" class="org.springframework.batch.item.file.FlatFileItemWriter">
    <property name="resource" value="file:csv/someReport.csv"/>
    <property name="shouldDeleteIfExists" value="true"/>
    <property name="encoding" value="UTF-8"/>
    <property name="lineAggregator" ref="someCustomLineAggregator"/>
    <property name="headerCallback" ref="someReportHeaderCallback" />
</bean>

HeaderCallback实现类

public class SomeReportFlatFileHeaderCallback implements FlatFileHeaderCallback {

    @Override
    public void writeHeader(Writer writer) throws IOException {
        writer.write("COL1, COL2, COL3");
    }
}
解决方案

方法1:扩展FlatFileItemWriter重写表头逻辑

直接扩展FlatFileItemWriter,内部跟踪是否有数据写入,仅在有数据时生成表头,无需额外依赖:

public class ConditionalHeaderFlatFileItemWriter<T> extends FlatFileItemWriter<T> {
    private boolean hasWrittenItems = false;

    @Override
    public void write(List<? extends T> items) throws Exception {
        if (!items.isEmpty()) {
            hasWrittenItems = true;
            super.write(items);
        }
    }

    @Override
    protected void writeHeader(Writer writer) throws IOException {
        // 仅当有数据写入时才输出表头
        if (hasWrittenItems) {
            super.writeHeader(writer);
        }
    }

    @Override
    public void open(ExecutionContext executionContext) {
        super.open(executionContext);
        // 每次作业启动时重置标记
        hasWrittenItems = false;
    }
}

修改XML配置,替换原FlatFileItemWriter为自定义版本:

<bean id="someCvsWriter" class="com.yourpackage.ConditionalHeaderFlatFileItemWriter">
    <property name="resource" value="file:csv/someReport.csv"/>
    <property name="shouldDeleteIfExists" value="true"/>
    <property name="encoding" value="UTF-8"/>
    <property name="lineAggregator" ref="someCustomLineAggregator"/>
    <property name="headerCallback" ref="someReportHeaderCallback" />
</bean>

方法2:自定义条件HeaderCallback(适合需复用原Header逻辑场景)

如果需要保留原HeaderCallback的逻辑,可通过计数Bean判断是否输出表头:

  1. 定义数据计数跟踪类
public class ItemCountTracker {
    private int count = 0;

    public void increment() {
        count++;
    }

    public boolean hasItems() {
        return count > 0;
    }

    public void reset() {
        count = 0;
    }
}
  1. 实现条件HeaderCallback
public class ConditionalHeaderCallback implements FlatFileHeaderCallback {
    private FlatFileHeaderCallback delegate;
    private ItemCountTracker itemCountTracker;

    public ConditionalHeaderCallback(FlatFileHeaderCallback delegate, ItemCountTracker itemCountTracker) {
        this.delegate = delegate;
        this.itemCountTracker = itemCountTracker;
    }

    @Override
    public void writeHeader(Writer writer) throws IOException {
        if (itemCountTracker.hasItems()) {
            delegate.writeHeader(writer);
        }
    }
}
  1. 包装原Writer实现计数
public class CountingItemWriter<T> implements ItemWriter<T> {
    private ItemWriter<T> delegate;
    private ItemCountTracker itemCountTracker;

    public CountingItemWriter(ItemWriter<T> delegate, ItemCountTracker itemCountTracker) {
        this.delegate = delegate;
        this.itemCountTracker = itemCountTracker;
    }

    @Override
    public void write(List<? extends T> items) throws Exception {
        if (!items.isEmpty()) {
            itemCountTracker.increment();
            delegate.write(items);
        }
    }
}
  1. 修改XML配置
<!-- 每个Writer对应独立的计数Bean,step作用域保证每次作业重置 -->
<bean id="someItemCountTracker" class="com.yourpackage.ItemCountTracker" scope="step"/>

<!-- 条件HeaderCallback依赖原Header和计数Bean -->
<bean id="conditionalSomeHeaderCallback" class="com.yourpackage.ConditionalHeaderCallback">
    <constructor-arg ref="someReportHeaderCallback"/>
    <constructor-arg ref="someItemCountTracker"/>
</bean>

<!-- 包装原Writer为计数Writer -->
<bean id="countingSomeCvsWriter" class="com.yourpackage.CountingItemWriter">
    <constructor-arg ref="someCvsWriter"/>
    <constructor-arg ref="someItemCountTracker"/>
</bean>

<!-- 更新原Writer的headerCallback为条件版本 -->
<bean id="someCvsWriter" class="org.springframework.batch.item.file.FlatFileItemWriter">
    <property name="resource" value="file:csv/someReport.csv"/>
    <property name="shouldDeleteIfExists" value="true"/>
    <property name="encoding" value="UTF-8"/>
    <property name="lineAggregator" ref="someCustomLineAggregator"/>
    <property name="headerCallback" ref="conditionalSomeHeaderCallback" />
</bean>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 12:06:00