此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Batch 文档 5.1.2spring-doc.cn

此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Batch 文档 5.1.2spring-doc.cn

写出到平面文件与从文件读入存在相同的问题 必须克服。步骤必须能够在 交易方式。spring-doc.cn

LineAggregator

就像接口需要获取一个项目并将其转换为 一样,文件写入必须有一种方法可以将多个字段聚合成一个字符串 用于写入文件。在 Spring Batch 中,这是 ,如 接口定义如下:LineTokenizerStringLineAggregatorspring-doc.cn

public interface LineAggregator<T> {

    public String aggregate(T item);

}

这是 的逻辑对立面。 接受 a 并返回 a ,而接受 an 并返回 .LineAggregatorLineTokenizerLineTokenizerStringFieldSetLineAggregatoritemStringspring-doc.cn

PassThroughLineAggregator

接口的最基本实现是 ,它假定对象已经是一个字符串,或者 它的字符串表示形式可以写入,如下面的代码所示:LineAggregatorPassThroughLineAggregatorspring-doc.cn

public class PassThroughLineAggregator<T> implements LineAggregator<T> {

    public String aggregate(T item) {
        return item.toString();
    }
}

如果直接控制创建字符串 必需,但具有 的优点,例如 transaction 和 restart 支持是必要的。FlatFileItemWriterspring-doc.cn

简化文件编写示例

现在,接口及其最基本的实现 已经定义,基本的编写流程可以是 解释:LineAggregatorPassThroughLineAggregatorspring-doc.cn

  1. 将要写入的对象传递给 ,以获得 .LineAggregatorStringspring-doc.cn

  2. 返回的将写入配置的文件中。Stringspring-doc.cn

以下摘录在代码中表达了这一点:FlatFileItemWriterspring-doc.cn

public void write(T item) throws Exception {
    write(lineAggregator.aggregate(item) + LINE_SEPARATOR);
}

在 Java 中,一个简单的配置示例可能如下所示:spring-doc.cn

Java 配置
@Bean
public FlatFileItemWriter itemWriter() {
	return  new FlatFileItemWriterBuilder<Foo>()
           			.name("itemWriter")
           			.resource(new FileSystemResource("target/test-outputs/output.txt"))
           			.lineAggregator(new PassThroughLineAggregator<>())
           			.build();
}

在 XML 中,配置的简单示例可能如下所示:spring-doc.cn

XML 配置
<bean id="itemWriter" class="org.spr...FlatFileItemWriter">
    <property name="resource" value="file:target/test-outputs/output.txt" />
    <property name="lineAggregator">
        <bean class="org.spr...PassThroughLineAggregator"/>
    </property>
</bean>

FieldExtractor

前面的示例对于写入文件的最基本用途可能很有用。 但是,大多数用户都有一个需要 写出,因此必须转换为一行。在文件读取中,以下内容为 必填:FlatFileItemWriterspring-doc.cn

  1. 从文件中读取一行。spring-doc.cn

  2. 将该行传递到方法中,以便检索 .LineTokenizer#tokenize()FieldSetspring-doc.cn

  3. 将 returned from tokenizing 传递给 a ,返回 方法的结果。FieldSetFieldSetMapperItemReader#read()spring-doc.cn

文件写入具有类似但相反的步骤:spring-doc.cn

  1. 将要写入的项传递给写入器。spring-doc.cn

  2. 将项上的字段转换为数组。spring-doc.cn

  3. 将结果数组聚合为一行。spring-doc.cn

因为框架无法知道对象的哪些字段需要 被写出,则必须写入 a 才能完成将 item 添加到数组中,如以下接口定义所示:FieldExtractorspring-doc.cn

public interface FieldExtractor<T> {

    Object[] extract(T item);

}

接口的实现应该从字段 中,可以在 元素或作为固定宽度线条的一部分。FieldExtractorspring-doc.cn

PassThroughFieldExtractor

在许多情况下,集合(例如数组、 、 或 、 ) 需要写出来。从这些集合类型之一中“提取”数组非常 简单。为此,请将集合转换为数组。因此,应在此方案中使用 。需要注意的是,如果 传入的对象不是一种 collection 类型,则返回一个仅包含要提取的项目的数组。CollectionFieldSetPassThroughFieldExtractorPassThroughFieldExtractorspring-doc.cn

BeanWrapperFieldExtractor

与文件读取部分中描述的一样,它是 通常最好配置如何将域对象转换为对象数组,而不是 而不是自己编写转换。的 提供了这个 功能,如以下示例所示:BeanWrapperFieldSetMapperBeanWrapperFieldExtractorspring-doc.cn

BeanWrapperFieldExtractor<Name> extractor = new BeanWrapperFieldExtractor<>();
extractor.setNames(new String[] { "first", "last", "born" });

String first = "Alan";
String last = "Turing";
int born = 1912;

Name n = new Name(first, last, born);
Object[] values = extractor.extract(n);

assertEquals(first, values[0]);
assertEquals(last, values[1]);
assertEquals(born, values[2]);

此提取器实现只有一个必需的属性:要 地图。就像 needs 字段名称将所提供对象上的 setter 上的字段映射到 setter 一样,需要名称 映射到 getter 以创建对象数组。值得注意的是, names 确定数组中字段的顺序。BeanWrapperFieldSetMapperFieldSetBeanWrapperFieldExtractorspring-doc.cn

分隔文件写入示例

最基本的平面文件格式是所有字段都由分隔符分隔的格式。 这可以使用 .以下示例编写 输出一个简单的 domain 对象,该对象表示客户账户的信用:DelimitedLineAggregatorspring-doc.cn

public class CustomerCredit {

    private int id;
    private String name;
    private BigDecimal credit;

    //getters and setters removed for clarity
}

由于正在使用域对象,因此必须提供接口的实现以及要使用的分隔符。FieldExtractorspring-doc.cn

以下示例演示如何在 Java 中将 与分隔符一起使用:FieldExtractorspring-doc.cn

Java 配置
@Bean
public FlatFileItemWriter<CustomerCredit> itemWriter(Resource outputResource) throws Exception {
	BeanWrapperFieldExtractor<CustomerCredit> fieldExtractor = new BeanWrapperFieldExtractor<>();
	fieldExtractor.setNames(new String[] {"name", "credit"});
	fieldExtractor.afterPropertiesSet();

	DelimitedLineAggregator<CustomerCredit> lineAggregator = new DelimitedLineAggregator<>();
	lineAggregator.setDelimiter(",");
	lineAggregator.setFieldExtractor(fieldExtractor);

	return new FlatFileItemWriterBuilder<CustomerCredit>()
				.name("customerCreditWriter")
				.resource(outputResource)
				.lineAggregator(lineAggregator)
				.build();
}

以下示例演示如何在 XML 中将 与分隔符一起使用:FieldExtractorspring-doc.cn

XML 配置
<bean id="itemWriter" class="org.springframework.batch.item.file.FlatFileItemWriter">
    <property name="resource" ref="outputResource" />
    <property name="lineAggregator">
        <bean class="org.spr...DelimitedLineAggregator">
            <property name="delimiter" value=","/>
            <property name="fieldExtractor">
                <bean class="org.spr...BeanWrapperFieldExtractor">
                    <property name="names" value="name,credit"/>
                </bean>
            </property>
        </bean>
    </property>
</bean>

在前面的示例中,前面描述的 chapter 用于将 name 和 credit 字段转换为 Object 数组,然后在每个字段之间用逗号写出。BeanWrapperFieldExtractorCustomerCreditspring-doc.cn

也可以使用 to 自动创建 AND,如以下示例所示:FlatFileItemWriterBuilder.DelimitedBuilderBeanWrapperFieldExtractorDelimitedLineAggregatorspring-doc.cn

Java 配置
@Bean
public FlatFileItemWriter<CustomerCredit> itemWriter(Resource outputResource) throws Exception {
	return new FlatFileItemWriterBuilder<CustomerCredit>()
				.name("customerCreditWriter")
				.resource(outputResource)
				.delimited()
				.delimiter("|")
				.names(new String[] {"name", "credit"})
				.build();
}

没有与 using .FlatFileItemWriterBuilderspring-doc.cn

固定宽度文件写入示例

Delimited 不是平面文件格式的唯一类型。许多人喜欢使用 set width for 在字段之间划定的每一列,这通常称为“固定宽度”。 Spring Batch 在使用 .FormatterLineAggregatorspring-doc.cn

使用上述相同的域对象,可以将其配置为 在 Java 中遵循:CustomerCreditspring-doc.cn

Java 配置
@Bean
public FlatFileItemWriter<CustomerCredit> itemWriter(Resource outputResource) throws Exception {
	BeanWrapperFieldExtractor<CustomerCredit> fieldExtractor = new BeanWrapperFieldExtractor<>();
	fieldExtractor.setNames(new String[] {"name", "credit"});
	fieldExtractor.afterPropertiesSet();

	FormatterLineAggregator<CustomerCredit> lineAggregator = new FormatterLineAggregator<>();
	lineAggregator.setFormat("%-9s%-2.0f");
	lineAggregator.setFieldExtractor(fieldExtractor);

	return new FlatFileItemWriterBuilder<CustomerCredit>()
				.name("customerCreditWriter")
				.resource(outputResource)
				.lineAggregator(lineAggregator)
				.build();
}

使用上述相同的域对象,可以将其配置为 在 XML 中遵循:CustomerCreditspring-doc.cn

XML 配置
<bean id="itemWriter" class="org.springframework.batch.item.file.FlatFileItemWriter">
    <property name="resource" ref="outputResource" />
    <property name="lineAggregator">
        <bean class="org.spr...FormatterLineAggregator">
            <property name="fieldExtractor">
                <bean class="org.spr...BeanWrapperFieldExtractor">
                    <property name="names" value="name,credit" />
                </bean>
            </property>
            <property name="format" value="%-9s%-2.0f" />
        </bean>
    </property>
</bean>

前面的大多数示例看起来应该很熟悉。但是,格式 property 是 new。spring-doc.cn

以下示例显示了 Java 中的 format 属性:spring-doc.cn

...
FormatterLineAggregator<CustomerCredit> lineAggregator = new FormatterLineAggregator<>();
lineAggregator.setFormat("%-9s%-2.0f");
...

以下示例显示了 XML 中的 format 属性:spring-doc.cn

<property name="format" value="%-9s%-2.0f" />

底层实现是使用 Java 5 中添加的相同功能构建的。Java 基于 C 编程的功能 语言。有关如何配置格式化程序的最详细信息,请参见 Formatter 的 Javadoc。FormatterFormatterprintfspring-doc.cn

也可以使用 to 自动创建 and,如以下示例所示:FlatFileItemWriterBuilder.FormattedBuilderBeanWrapperFieldExtractorFormatterLineAggregatorspring-doc.cn

Java 配置
@Bean
public FlatFileItemWriter<CustomerCredit> itemWriter(Resource outputResource) throws Exception {
	return new FlatFileItemWriterBuilder<CustomerCredit>()
				.name("customerCreditWriter")
				.resource(outputResource)
				.formatted()
				.format("%-9s%-2.0f")
				.names(new String[] {"name", "credit"})
				.build();
}

处理文件创建

FlatFileItemReader与文件资源的关系非常简单。当读者 初始化后,它会打开文件(如果存在),如果不存在,则引发异常。 文件写入并不是那么简单。乍一看,它似乎很相似 简单的合约应该存在 :如果文件已经 exists,抛出一个异常,如果没有,则创建它并开始编写。然而 可能重新启动 可能会导致问题。在正常的重启场景中, contract is reversed:如果文件存在,则从最后一个已知 good 开始写入该文件 position 的 Position,如果没有,则引发异常。但是,如果文件名 因为这个工作总是一样的吗?在这种情况下,如果文件 存在,除非是重新启动。由于这种可能性,it 包含属性 .将此属性设置为 true 会导致 打开 Writer 时要删除的同名现有文件。FlatFileItemWriterJobFlatFileItemWritershouldDeleteIfExistsspring-doc.cn