配置步骤

配置Step

域章节中所述,Step是一个 domain 对象,该对象封装了批处理作业的独立顺序阶段,并且 包含定义和控制实际批次所需的所有信息 加工。这必然是一个模糊的描述,因为任何给定Step由编写Job.一个Step可以像 或开发人员希望的复杂。一个简单的Step可能会将数据从文件加载到 数据库,只需要很少或不需要代码(取决于所使用的实现)。一个 更多 复杂Step可能具有复杂的业务规则,这些规则作为 processing,如下图所示:spring-doc.cadn.net.cn

步
图 1.步

面向数据块的处理

Spring Batch 在其最常见的 实现。面向数据块的处理是指一次读取一个数据,并且 创建在事务边界内写出的 'chunks' 。一旦 items read 等于提交间隔,则整个 chunk 由ItemWriter,然后提交事务。下图显示了 过程:spring-doc.cadn.net.cn

面向数据块的处理
图 2.面向数据块的处理

以下伪代码以简化的形式显示了相同的概念:spring-doc.cadn.net.cn

List items = new Arraylist();
for(int i = 0; i < commitInterval; i++){
    Object item = itemReader.read();
    if (item != null) {
        items.add(item);
    }
}
itemWriter.write(items);

面向数据块的步骤也可以使用可选的ItemProcessor在将项目传递给ItemWriter.下图 显示了ItemProcessor在步骤中注册:spring-doc.cadn.net.cn

使用 Item Processor 进行面向数据块的处理
图 3.使用 Item Processor 进行面向数据块的处理

以下伪代码显示了如何以简化的形式实现这一点:spring-doc.cadn.net.cn

List items = new Arraylist();
for(int i = 0; i < commitInterval; i++){
    Object item = itemReader.read();
    if (item != null) {
        items.add(item);
    }
}

List processedItems = new Arraylist();
for(Object item: items){
    Object processedItem = itemProcessor.process(item);
    if (processedItem != null) {
        processedItems.add(processedItem);
    }
}

itemWriter.write(processedItems);

有关项目处理器及其使用案例的更多详细信息,请参阅 项目处理 中。spring-doc.cadn.net.cn

配置Step

尽管Step,它是一个 极其复杂的类,可能包含许多协作者。spring-doc.cadn.net.cn

为了简化配置,可以使用 Spring Batch XML 命名空间,如 以下示例:spring-doc.cadn.net.cn

XML 配置
<job id="sampleJob" job-repository="jobRepository">
    <step id="step1">
        <tasklet transaction-manager="transactionManager">
            <chunk reader="itemReader" writer="itemWriter" commit-interval="10"/>
        </tasklet>
    </step>
</job>

使用 Java 配置时,可以使用 Spring Batch 构建器,如 以下示例:spring-doc.cadn.net.cn

Java 配置
/**
 * Note the JobRepository is typically autowired in and not needed to be explicitly
 * configured
 */
@Bean
public Job sampleJob(JobRepository jobRepository, Step sampleStep) {
    return this.jobBuilderFactory.get("sampleJob")
    			.repository(jobRepository)
                .start(sampleStep)
                .build();
}

/**
 * Note the TransactionManager is typically autowired in and not needed to be explicitly
 * configured
 */
@Bean
public Step sampleStep(PlatformTransactionManager transactionManager) {
	return this.stepBuilderFactory.get("sampleStep")
				.transactionManager(transactionManager)
				.<String, String>chunk(10)
				.reader(itemReader())
				.writer(itemWriter())
				.build();
}

上面的配置包括创建面向项目的 步:spring-doc.cadn.net.cn

  • transaction-manager: Spring的PlatformTransactionManager开始并提交 处理期间的事务。spring-doc.cadn.net.cn

  • transactionManager: Spring的PlatformTransactionManager开始并提交 处理期间的事务。spring-doc.cadn.net.cn

  • job-repository:特定于 XML 的JobRepository定期存储 这StepExecutionExecutionContext在处理期间(就在提交之前)。为 内联<step/>(在<job/>),它是<job/>元素。对于独立的<step/>,它被定义为 <tasklet/> 的属性。spring-doc.cadn.net.cn

  • repository:特定于 Java 的JobRepository定期存储 这StepExecutionExecutionContext在处理期间(就在提交之前)。spring-doc.cadn.net.cn

  • chunk:依赖项的 Java 特定名称,指示这是一个 基于项目的步骤,交易前要处理的项目数为 承诺。spring-doc.cadn.net.cn

需要注意的是,job-repository默认为jobRepositorytransaction-manager默认为transactionManager.此外,ItemProcessor是 可选,因为该项可以直接从 Reader 传递给 Writer。spring-doc.cadn.net.cn

需要注意的是,repository默认为jobRepositorytransactionManager默认为transactionManager(全部通过@EnableBatchProcessing).此外,ItemProcessor是可选的,因为 Item 可以是 直接从 reader 传递给 writer。spring-doc.cadn.net.cn

从 Parent 继承Step

如果一组Steps共享类似的配置,那么定义一个 “父级”Step从中得到混凝土Steps可以继承属性。与 class 相似 继承,即 “child”Step将其元素和属性与 父级的。子对象还会覆盖父对象的任何Steps.spring-doc.cadn.net.cn

在以下示例中,Step,“concreteStep1”,继承自 “parentStep”。是的 使用 'itemReader'、'itemProcessor'、'itemWriter'、startLimit=5allowStartIfComplete=true.此外,commitInterval是 '5',因为它是 被 “concreteStep1” 覆盖Step,如以下示例所示:spring-doc.cadn.net.cn

<step id="parentStep">
    <tasklet allow-start-if-complete="true">
        <chunk reader="itemReader" writer="itemWriter" commit-interval="10"/>
    </tasklet>
</step>

<step id="concreteStep1" parent="parentStep">
    <tasklet start-limit="5">
        <chunk processor="itemProcessor" commit-interval="5"/>
    </tasklet>
</step>

id属性在 job 元素中的步骤中仍然是必需的。这是两个人 原因:spring-doc.cadn.net.cn

  • id用作步骤名称,当保留StepExecution.如果相同 独立步骤在作业的多个步骤中引用,则会发生错误。spring-doc.cadn.net.cn

  • 在创建任务流时,如本章后面所述,next属性 应该指的是流程中的步骤,而不是独立步骤。spring-doc.cadn.net.cn

抽象Step

有时,可能需要定义父级Step那不是一个完整的Step配置。例如,如果reader,writertaskletattributes 是 从Step配置,则初始化失败。如果父级必须 定义时,则abstract属性。一abstract Step仅扩展,从不实例化。spring-doc.cadn.net.cn

在以下示例中,Step abstractParentStep不会被实例化,如果它 没有被宣布为抽象的。这Step、 “concreteStep2”、 具有 'itemReader'、 'itemWriter' 和 commit-interval=10。spring-doc.cadn.net.cn

<step id="abstractParentStep" abstract="true">
    <tasklet>
        <chunk commit-interval="10"/>
    </tasklet>
</step>

<step id="concreteStep2" parent="abstractParentStep">
    <tasklet>
        <chunk reader="itemReader" writer="itemWriter"/>
    </tasklet>
</step>
合并列表

上的一些可配置元素Steps是列表,例如<listeners/>元素。 如果父级和子级Steps声明一个<listeners/>元素,则 child's 列表将覆盖 parent's 列表。为了允许子项添加额外的 listeners 添加到父级定义的 list 中,每个 list 元素都有一个merge属性。 如果元素指定merge="true",则子项的列表将与 parent's 而不是覆盖它。spring-doc.cadn.net.cn

在以下示例中,Step“concreteStep3” 是使用两个侦听器创建的:listenerOnelistenerTwo:spring-doc.cadn.net.cn

<step id="listenersParentStep" abstract="true">
    <listeners>
        <listener ref="listenerOne"/>
    <listeners>
</step>

<step id="concreteStep3" parent="listenersParentStep">
    <tasklet>
        <chunk reader="itemReader" writer="itemWriter" commit-interval="5"/>
    </tasklet>
    <listeners merge="true">
        <listener ref="listenerTwo"/>
    <listeners>
</step>

提交间隔

如前所述,步骤读入和写出项目,并定期提交 使用随附的PlatformTransactionManager.使用commit-interval的 1,它 在写入每个单独的项目后提交。这在许多情况下并不理想, 因为开始和提交事务的成本很高。理想情况下,最好是 在每笔交易中处理尽可能多的项目,这完全取决于 正在处理的数据类型以及步骤与之交互的资源。 因此,提交中处理的项目数可以是 配置。spring-doc.cadn.net.cn

以下示例显示了step谁的tasklet具有commit-interval值 10,因为它将在 XML 中定义:spring-doc.cadn.net.cn

XML 配置
<job id="sampleJob">
    <step id="step1">
        <tasklet>
            <chunk reader="itemReader" writer="itemWriter" commit-interval="10"/>
        </tasklet>
    </step>
</job>

以下示例显示了step谁的tasklet具有commit-interval值为 10,因为它将在 Java 中定义:spring-doc.cadn.net.cn

Java 配置
@Bean
public Job sampleJob() {
    return this.jobBuilderFactory.get("sampleJob")
                     .start(step1())
                     .build();
}

@Bean
public Step step1() {
	return this.stepBuilderFactory.get("step1")
				.<String, String>chunk(10)
				.reader(itemReader())
				.writer(itemWriter())
				.build();
}

在前面的示例中,每个事务中处理 10 个项目。在 开始处理,开始交易。此外,每次readItemReader,则计数器将递增。当它达到 10 时,聚合项的列表 传递给ItemWriter,并提交事务。spring-doc.cadn.net.cn

配置Step用于重启

在 “Configuring and Running a Job” 部分 ,重新启动Job进行了讨论。重新启动对步骤有许多影响,因此可能会 需要一些特定的配置。spring-doc.cadn.net.cn

设置起始限制

在许多情况下,您可能希望控制Step五月 启动。例如,特定的Step可能需要进行配置,以便仅 运行一次,因为它会使某些资源失效,而这些资源必须先手动修复才能 再次运行。这可以在步骤级别上进行配置,因为不同的步骤可能具有 不同的要求。一个Step只能执行一次,可以作为 相同Job作为Step可以无限运行。spring-doc.cadn.net.cn

以下代码片段显示了 XML 中的启动限制配置示例:spring-doc.cadn.net.cn

XML 配置
<step id="step1">
    <tasklet start-limit="1">
        <chunk reader="itemReader" writer="itemWriter" commit-interval="10"/>
    </tasklet>
</step>

以下代码片段显示了 Java 中的启动限制配置示例:spring-doc.cadn.net.cn

Java 配置
@Bean
public Step step1() {
	return this.stepBuilderFactory.get("step1")
				.<String, String>chunk(10)
				.reader(itemReader())
				.writer(itemWriter())
				.startLimit(1)
				.build();
}

前面示例中显示的步骤只能运行一次。尝试再次运行 导致StartLimitExceededException被扔出去。请注意, start-limit 为Integer.MAX_VALUE.spring-doc.cadn.net.cn

重新启动已完成的Step

对于可重启的作业,可能有一个或多个步骤应始终为 运行,无论他们第一次是否成功。例如,可能 是验证步骤或Step,这会在处理之前清理资源。在 重新启动的作业的正常处理,任何状态为 'COMPLETED' 的步骤,这意味着它 已成功完成,将被跳过。设置allow-start-if-complete自 “true” 将覆盖此 URL,以便 Step 始终运行。spring-doc.cadn.net.cn

以下代码片段显示了如何在 XML 中定义可重启的作业:spring-doc.cadn.net.cn

XML 配置
<step id="step1">
    <tasklet allow-start-if-complete="true">
        <chunk reader="itemReader" writer="itemWriter" commit-interval="10"/>
    </tasklet>
</step>

以下代码片段显示了如何在 Java 中定义可重启作业:spring-doc.cadn.net.cn

Java 配置
@Bean
public Step step1() {
	return this.stepBuilderFactory.get("step1")
				.<String, String>chunk(10)
				.reader(itemReader())
				.writer(itemWriter())
				.allowStartIfComplete(true)
				.build();
}
Step重启配置示例

以下 XML 示例演示如何将作业配置为具有可以 重新 启动:spring-doc.cadn.net.cn

XML 配置
<job id="footballJob" restartable="true">
    <step id="playerload" next="gameLoad">
        <tasklet>
            <chunk reader="playerFileItemReader" writer="playerWriter"
                   commit-interval="10" />
        </tasklet>
    </step>
    <step id="gameLoad" next="playerSummarization">
        <tasklet allow-start-if-complete="true">
            <chunk reader="gameFileItemReader" writer="gameWriter"
                   commit-interval="10"/>
        </tasklet>
    </step>
    <step id="playerSummarization">
        <tasklet start-limit="2">
            <chunk reader="playerSummarizationSource" writer="summaryWriter"
                   commit-interval="10"/>
        </tasklet>
    </step>
</job>

以下 Java 示例演示如何将作业配置为具有可以 重新 启动:spring-doc.cadn.net.cn

Java 配置
@Bean
public Job footballJob() {
	return this.jobBuilderFactory.get("footballJob")
				.start(playerLoad())
				.next(gameLoad())
				.next(playerSummarization())
				.build();
}

@Bean
public Step playerLoad() {
	return this.stepBuilderFactory.get("playerLoad")
			.<String, String>chunk(10)
			.reader(playerFileItemReader())
			.writer(playerWriter())
			.build();
}

@Bean
public Step gameLoad() {
	return this.stepBuilderFactory.get("gameLoad")
			.allowStartIfComplete(true)
			.<String, String>chunk(10)
			.reader(gameFileItemReader())
			.writer(gameWriter())
			.build();
}

@Bean
public Step playerSummarization() {
	return this.stepBuilderFactory.get("playerSummarization")
			.startLimit(2)
			.<String, String>chunk(10)
			.reader(playerSummarizationSource())
			.writer(summaryWriter())
			.build();
}

前面的示例配置适用于加载有关足球的信息的作业 游戏并总结它们。它包含三个步骤:playerLoad,gameLoadplayerSummarization.这playerLoadstep 从平面文件加载玩家信息, 虽然gameLoadStep 对 Games 执行相同的作。最后一步playerSummarization,然后根据 提供的游戏。假设由playerLoad必须仅加载 一次,但那gameLoad可以加载在特定目录中找到的任何游戏, 在它们成功加载到数据库后将其删除。因此, 这playerLoad步骤不包含其他配置。它可以启动任何数量 的次数,如果完成,则跳过。这gameLoadStep 需要运行 每次,以防自上次运行以来添加了额外的文件。它有 'allow-start-if-complete' 设置为 'true' 以便始终启动。(假设 数据库桌面游戏加载到的上面有一个进程指示器,以确保 新游戏可以通过 Summarization 步骤正确找到)。汇总步骤 这是作业中最重要的,配置为具有 2 的启动限制。这 非常有用,因为如果步骤持续失败,则会将新的退出代码返回到 作符,并且它不能再次启动,直到手动 干预已经发生。spring-doc.cadn.net.cn

此 job 为本文档提供了一个示例,与footballJob在 samples 项目中找到。spring-doc.cadn.net.cn

本节的其余部分描述了footballJob例。spring-doc.cadn.net.cn

  1. playerLoad运行并成功完成,将 400 名玩家添加到“玩家” 桌子。spring-doc.cadn.net.cn

  2. gameLoad运行和处理 11 个文件的游戏数据,加载其内容 进入 'GAMES' 表。spring-doc.cadn.net.cn

  3. playerSummarization开始处理,并在 5 分钟后失败。spring-doc.cadn.net.cn

  1. playerLoad不会运行,因为它已经成功完成,并且allow-start-if-complete为 'false' (默认值)。spring-doc.cadn.net.cn

  2. gameLoad再次运行并处理另外 2 个文件,将它们的内容加载到 “GAMES”表(带有一个进程指示器,表明他们尚未 已处理)。spring-doc.cadn.net.cn

  3. playerSummarization开始处理所有剩余的游戏数据(使用 进程指示器),并在 30 分钟后再次失败。spring-doc.cadn.net.cn

  1. playerLoad不会运行,因为它已经成功完成,并且allow-start-if-complete为 'false' (默认值)。spring-doc.cadn.net.cn

  2. gameLoad再次运行并处理另外 2 个文件,将它们的内容加载到 “GAMES”表(带有一个进程指示器,表明他们尚未 已处理)。spring-doc.cadn.net.cn

  3. playerSummarization未启动,并且 Job 会立即终止,因为这是 第三次执行playerSummarization,其限制仅为 2。要么是极限 必须引发,或者Job必须作为新的JobInstance.spring-doc.cadn.net.cn

配置 Skip Logic

在许多情况下,处理时遇到的错误不应导致Stepfailure,但应跳过。这通常是一个必须 由了解数据本身及其含义的人制作。财务数据, 例如,可能无法跳过,因为它会导致资金被转移,这 需要完全准确。另一方面,加载供应商列表可能会 允许跳过。如果供应商由于格式不正确或被 缺少必要的信息,则可能没有问题。通常,这些 还会记录记录,稍后在讨论侦听器时将对此进行介绍。spring-doc.cadn.net.cn

下面的 XML 示例显示了使用跳过限制的示例:spring-doc.cadn.net.cn

XML 配置
<step id="step1">
   <tasklet>
      <chunk reader="flatFileItemReader" writer="itemWriter"
             commit-interval="10" skip-limit="10">
         <skippable-exception-classes>
            <include class="org.springframework.batch.item.file.FlatFileParseException"/>
         </skippable-exception-classes>
      </chunk>
   </tasklet>
</step>

下面的 Java 示例显示了使用跳过限制的示例:spring-doc.cadn.net.cn

Java 配置
@Bean
public Step step1() {
	return this.stepBuilderFactory.get("step1")
				.<String, String>chunk(10)
				.reader(flatFileItemReader())
				.writer(itemWriter())
				.faultTolerant()
				.skipLimit(10)
				.skip(FlatFileParseException.class)
				.build();
}

在前面的示例中,FlatFileItemReader被使用。如果在任何时候,FlatFileParseException被抛出,则跳过该项并计入总数 跳过限制为 10。可能会引发声明的异常(及其子类) 在 chunk 处理的任何阶段(读取、处理、写入),但单独计数 由 read、process 和 write 中的 skip 组成 步骤执行,但限制适用于所有跳过。一旦跳过限制为 reached,则找到的下一个异常将导致步骤失败。换句话说,第十一个 skip 会触发异常,而不是第十个异常。spring-doc.cadn.net.cn

前面示例的一个问题是,除了FlatFileParseException导致Job失败。在某些情况下,这可能是 正确的行为。但是,在其他情况下,可能更容易确定 异常应该会导致失败并跳过其他所有内容。spring-doc.cadn.net.cn

下面的 XML 示例显示了一个排除特定异常的示例:spring-doc.cadn.net.cn

XML 配置
<step id="step1">
    <tasklet>
        <chunk reader="flatFileItemReader" writer="itemWriter"
               commit-interval="10" skip-limit="10">
            <skippable-exception-classes>
                <include class="java.lang.Exception"/>
                <exclude class="java.io.FileNotFoundException"/>
            </skippable-exception-classes>
        </chunk>
    </tasklet>
</step>

下面的 Java 示例显示了一个排除特定异常的示例:spring-doc.cadn.net.cn

Java 配置
@Bean
public Step step1() {
	return this.stepBuilderFactory.get("step1")
				.<String, String>chunk(10)
				.reader(flatFileItemReader())
				.writer(itemWriter())
				.faultTolerant()
				.skipLimit(10)
				.skip(Exception.class)
				.noSkip(FileNotFoundException.class)
				.build();
}

通过识别java.lang.Exception作为可跳过的 Exception 类,配置 表示所有Exceptions是可跳过的。但是,通过“排除”java.io.FileNotFoundException,则配置会细化 exception 类设置为 allExceptions 除了 FileNotFoundException.任何被排除的 如果遇到 Exception 类,则 Exception 类是致命的(即,不会跳过它们)。spring-doc.cadn.net.cn

对于遇到的任何异常,可跳过性由最近的超类决定 在 Class 层次结构中。任何未分类的异常都被视为 “致命” 异常。spring-doc.cadn.net.cn

的顺序<include/><exclude/>元素无关紧要。spring-doc.cadn.net.cn

的顺序skipnoSkip方法调用无关紧要。spring-doc.cadn.net.cn

配置重试逻辑

在大多数情况下,您希望异常导致 skip 或Step失败。然而 并非所有异常都是确定性的。如果FlatFileParseException遇到 读取时,它总是针对该记录抛出。重置ItemReader没有帮助。 但是,对于其他例外情况,例如DeadlockLoserDataAccessException哪 表示当前进程已尝试更新另一个进程的记录 保持锁定。等待并重试可能会成功。spring-doc.cadn.net.cn

在 XML 中,应按如下方式配置 retry:spring-doc.cadn.net.cn

<step id="step1">
   <tasklet>
      <chunk reader="itemReader" writer="itemWriter"
             commit-interval="2" retry-limit="3">
         <retryable-exception-classes>
            <include class="org.springframework.dao.DeadlockLoserDataAccessException"/>
         </retryable-exception-classes>
      </chunk>
   </tasklet>
</step>

在 Java 中,应按如下方式配置 retry:spring-doc.cadn.net.cn

@Bean
public Step step1() {
	return this.stepBuilderFactory.get("step1")
				.<String, String>chunk(2)
				.reader(itemReader())
				.writer(itemWriter())
				.faultTolerant()
				.retryLimit(3)
				.retry(DeadlockLoserDataAccessException.class)
				.build();
}

Step允许对单个项的重试次数进行限制,并且 “可重试”的异常列表。有关重试工作原理的更多详细信息,请参阅 重试spring-doc.cadn.net.cn

控制回滚

默认情况下,无论重试还是跳过,从ItemWriter导致由Step回滚。如果 skip 配置为 前面描述的,从ItemReader不会导致回滚。 但是,在许多情况下,从ItemWriter应该 不会导致回滚,因为尚未执行任何作来使事务无效。 因此,Step可以配置不应配置的例外列表 cause rollback.spring-doc.cadn.net.cn

在 XML 中,您可以按如下方式控制回滚:spring-doc.cadn.net.cn

XML 配置
<step id="step1">
   <tasklet>
      <chunk reader="itemReader" writer="itemWriter" commit-interval="2"/>
      <no-rollback-exception-classes>
         <include class="org.springframework.batch.item.validator.ValidationException"/>
      </no-rollback-exception-classes>
   </tasklet>
</step>

在 Java 中,您可以按如下方式控制回滚:spring-doc.cadn.net.cn

Java 配置
@Bean
public Step step1() {
	return this.stepBuilderFactory.get("step1")
				.<String, String>chunk(2)
				.reader(itemReader())
				.writer(itemWriter())
				.faultTolerant()
				.noRollback(ValidationException.class)
				.build();
}
事务性读取器

基本合约ItemReader是它只是向前的。步骤缓冲区 Reader Input,以便在回滚的情况下,不需要重新读取 Item 来自读者。但是,在某些情况下,reader 是建立的 事务资源(如 JMS 队列)的顶部。在这种情况下,由于队列是 绑定到回滚的事务,则从 队列重新打开。因此,可以将该步骤配置为不缓冲 项目。spring-doc.cadn.net.cn

下面的示例演示如何创建不在 XML 中缓冲项目的 reader:spring-doc.cadn.net.cn

XML 配置
<step id="step1">
    <tasklet>
        <chunk reader="itemReader" writer="itemWriter" commit-interval="2"
               is-reader-transactional-queue="true"/>
    </tasklet>
</step>

以下示例演示如何在 Java 中创建不缓冲项目的 reader:spring-doc.cadn.net.cn

Java 配置
@Bean
public Step step1() {
	return this.stepBuilderFactory.get("step1")
				.<String, String>chunk(2)
				.reader(itemReader())
				.writer(itemWriter())
				.readerIsTransactionalQueue()
				.build();
}

交易属性

Transaction 属性可用于控制isolation,propagationtimeout设置。有关设置交易属性的更多信息,请参阅 Spring 核心文档spring-doc.cadn.net.cn

以下示例将isolation,propagationtimeout交易 XML 中的属性:spring-doc.cadn.net.cn

XML 配置
<step id="step1">
    <tasklet>
        <chunk reader="itemReader" writer="itemWriter" commit-interval="2"/>
        <transaction-attributes isolation="DEFAULT"
                                propagation="REQUIRED"
                                timeout="30"/>
    </tasklet>
</step>

以下示例将isolation,propagationtimeout交易 Java 中的属性:spring-doc.cadn.net.cn

Java 配置
@Bean
public Step step1() {
	DefaultTransactionAttribute attribute = new DefaultTransactionAttribute();
	attribute.setPropagationBehavior(Propagation.REQUIRED.value());
	attribute.setIsolationLevel(Isolation.DEFAULT.value());
	attribute.setTimeout(30);

	return this.stepBuilderFactory.get("step1")
				.<String, String>chunk(2)
				.reader(itemReader())
				.writer(itemWriter())
				.transactionAttribute(attribute)
				.build();
}

注册ItemStream替换为Step

步骤必须注意ItemStreamcallbacks 在其 生命周期(有关ItemStream接口,请参阅 ItemStream )。如果某个步骤失败并且可能会失败,这一点至关重要 需要重新启动,因为ItemStreaminterface 是 Step 获取 它需要的有关执行之间持久状态的信息。spring-doc.cadn.net.cn

如果ItemReader,ItemProcessorItemWriter本身实现了ItemStream接口,则会自动注册这些任何其他流都需要 单独注册。这通常是间接依赖项(例如 委托被注入到 Reader 和 Writer 中。可以在step通过 'stream' 元素。spring-doc.cadn.net.cn

以下示例演示如何注册streamstep在 XML 中:spring-doc.cadn.net.cn

XML 配置
<step id="step1">
    <tasklet>
        <chunk reader="itemReader" writer="compositeWriter" commit-interval="2">
            <streams>
                <stream ref="fileItemWriter1"/>
                <stream ref="fileItemWriter2"/>
            </streams>
        </chunk>
    </tasklet>
</step>

<beans:bean id="compositeWriter"
            class="org.springframework.batch.item.support.CompositeItemWriter">
    <beans:property name="delegates">
        <beans:list>
            <beans:ref bean="fileItemWriter1" />
            <beans:ref bean="fileItemWriter2" />
        </beans:list>
    </beans:property>
</beans:bean>

以下示例演示如何注册streamstep在 Java 中:spring-doc.cadn.net.cn

Java 配置
@Bean
public Step step1() {
	return this.stepBuilderFactory.get("step1")
				.<String, String>chunk(2)
				.reader(itemReader())
				.writer(compositeItemWriter())
				.stream(fileItemWriter1())
				.stream(fileItemWriter2())
				.build();
}

/**
 * In Spring Batch 4, the CompositeItemWriter implements ItemStream so this isn't
 * necessary, but used for an example.
 */
@Bean
public CompositeItemWriter compositeItemWriter() {
	List<ItemWriter> writers = new ArrayList<>(2);
	writers.add(fileItemWriter1());
	writers.add(fileItemWriter2());

	CompositeItemWriter itemWriter = new CompositeItemWriter();

	itemWriter.setDelegates(writers);

	return itemWriter;
}

在上面的示例中,CompositeItemWriter不是ItemStream,但是它的 代表是。因此,两个委托编写器都必须显式注册为流 以便框架正确处理它们。这ItemReader不需要 显式注册为流,因为它是Step.步骤 现在可以重新启动,并且读取器和写入器的状态会正确地保留在 事件。spring-doc.cadn.net.cn

拦截Step执行

就像Job,在执行Step其中 a 用户可能需要执行某些功能。例如,为了写出到平面 文件,则ItemWriterStep具有 已完成,以便可以写入页脚。这可以通过许多Step作用域侦听器。spring-doc.cadn.net.cn

实现StepListener(但不是那个界面 本身,因为它是空的)可以应用于通过listeners元素。 这listeners元素在 step、tasklet 或 chunk 声明中有效。是的 建议您在其函数适用的级别声明侦听器, 或者,如果它是多功能的(例如StepExecutionListenerItemReadListener), 然后在应用它的最精细级别声明它。spring-doc.cadn.net.cn

以下示例显示了在 XML 中的块级别应用的侦听器:spring-doc.cadn.net.cn

XML 配置
<step id="step1">
    <tasklet>
        <chunk reader="reader" writer="writer" commit-interval="10"/>
        <listeners>
            <listener ref="chunkListener"/>
        </listeners>
    </tasklet>
</step>

以下示例显示了在 Java 中应用于块级别的侦听器:spring-doc.cadn.net.cn

Java 配置
@Bean
public Step step1() {
	return this.stepBuilderFactory.get("step1")
				.<String, String>chunk(10)
				.reader(reader())
				.writer(writer())
				.listener(chunkListener())
				.build();
}

ItemReader,ItemWriterItemProcessor它本身实现了一个StepListenerinterfaces 会自动注册到Step如果使用 Namespace<step>元素或*StepFactoryBean工厂。仅此 适用于直接注入到Step.如果侦听器嵌套在 另一个组件,它需要显式注册(如前面的注册ItemStream替换为Step).spring-doc.cadn.net.cn

除了StepListener接口中,提供了注解来解决 同样的担忧。普通的旧 Java 对象可以具有带有这些注释的方法,这些注释是 然后转换为相应的StepListener类型。注释 块组件的自定义实现,例如ItemReaderItemWriterTasklet.注释由 XML 解析器分析<listener/>元素 以及在listener方法,因此您需要做的就是 是使用 XML 命名空间或生成器将侦听器注册到步骤。spring-doc.cadn.net.cn

StepExecutionListener

StepExecutionListener表示Step执行。它 允许在Step已启动,结束后,是否结束 normally 或 failed,如以下示例所示:spring-doc.cadn.net.cn

public interface StepExecutionListener extends StepListener {

    void beforeStep(StepExecution stepExecution);

    ExitStatus afterStep(StepExecution stepExecution);

}

ExitStatus的返回类型为afterStep为了让听众有机会 修改在完成Step.spring-doc.cadn.net.cn

与此接口对应的注解是:spring-doc.cadn.net.cn

ChunkListener

块定义为在事务范围内处理的项目。提交 transaction 在每个提交间隔提交一个 'chunk'。一个ChunkListener可用于 在 chunk 开始处理之前或 chunk 完成后执行逻辑 成功,如以下接口定义所示:spring-doc.cadn.net.cn

public interface ChunkListener extends StepListener {

    void beforeChunk(ChunkContext context);
    void afterChunk(ChunkContext context);
    void afterChunkError(ChunkContext context);

}

beforeChunk 方法在事务启动后调用,但在 read 之前调用 调用ItemReader.相反afterChunk在 chunk 被 committed(如果有回滚,则根本不提交)。spring-doc.cadn.net.cn

与此接口对应的注解是:spring-doc.cadn.net.cn

一个ChunkListener可以在没有 chunk 声明时应用。这TaskletStep是 负责调用ChunkListener,因此它适用于非面向项的 tasklet 以及(它在 tasklet 之前和之后调用)。spring-doc.cadn.net.cn

ItemReadListener

在之前讨论跳过逻辑时,提到对 跳过的记录,以便以后可以处理它们。在读取错误的情况下, 这可以通过ItemReaderListener,如以下界面所示 定义:spring-doc.cadn.net.cn

public interface ItemReadListener<T> extends StepListener {

    void beforeRead();
    void afterRead(T item);
    void onReadError(Exception ex);

}

beforeRead方法在每次调用之前调用 read。ItemReader.这afterReadmethod 在每次成功调用 read 后调用,并将项目传递给 那被读了。如果在读取时出现错误,则onReadError方法。 提供遇到的异常,以便可以记录它。spring-doc.cadn.net.cn

与此接口对应的注解是:spring-doc.cadn.net.cn

ItemProcessListener

就像ItemReadListener,则项目的处理可以被 “监听” ,因为 如以下接口定义所示:spring-doc.cadn.net.cn

public interface ItemProcessListener<T, S> extends StepListener {

    void beforeProcess(T item);
    void afterProcess(T item, S result);
    void onProcessError(T item, Exception e);

}

beforeProcessmethod 之前被调用processItemProcessor和 is 已递出要处理的项目。这afterProcessmethod 在 商品已成功处理。如果在处理过程中出现错误,onProcessError方法。遇到的异常以及 尝试处理,以便可以记录它们。spring-doc.cadn.net.cn

与此接口对应的注解是:spring-doc.cadn.net.cn

ItemWriteListener

项目的写入可以使用ItemWriteListener,如 接口定义如下:spring-doc.cadn.net.cn

public interface ItemWriteListener<S> extends StepListener {

    void beforeWrite(List<? extends S> items);
    void afterWrite(List<? extends S> items);
    void onWriteError(Exception exception, List<? extends S> items);

}

beforeWritemethod 之前被调用writeItemWriter并递给 写入的项目列表。这afterWritemethod 在项 已成功写入。如果在写入时出现错误,则onWriteErrormethod 为 叫。遇到的异常和尝试写入的项为 提供,以便可以记录它们。spring-doc.cadn.net.cn

与此接口对应的注解是:spring-doc.cadn.net.cn

SkipListener

ItemReadListener,ItemProcessListenerItemWriteListener都提供机制 收到错误通知,但没有通知你记录实际上已被 跳。onWriteError,即使重试某个项,也会调用 成功的。因此,有一个单独的接口用于跟踪跳过的项目,如 如以下接口定义所示:spring-doc.cadn.net.cn

public interface SkipListener<T,S> extends StepListener {

    void onSkipInRead(Throwable t);
    void onSkipInProcess(T item, Throwable t);
    void onSkipInWrite(S item, Throwable t);

}

onSkipInRead每当读取时跳过项时调用。需要注意的是 回滚可能会导致同一项目多次注册为 skipped。onSkipInWrite在写入时跳过项时调用。因为该项目具有 已成功读取(且未跳过),它还将项目本身作为 论点。spring-doc.cadn.net.cn

与此接口对应的注解是:spring-doc.cadn.net.cn

SkipListeners 和事务

最常见的 a 用例之一SkipListener是注销跳过的项目,因此 另一个批处理过程甚至人工过程可用于评估和修复 导致跳过的问题。因为在很多情况下,原始交易 可以回滚,Spring Batch 做出两个保证:spring-doc.cadn.net.cn

  1. 适当的 skip 方法(取决于错误发生的时间)只调用一次 每件。spring-doc.cadn.net.cn

  2. SkipListener总是在提交事务之前调用。这是 要确保侦听器调用的任何事务资源不会被 失败ItemWriter.spring-doc.cadn.net.cn

TaskletStep

面向数据块的处理并不是在Step.如果Step必须包含一个简单的存储过程调用吗?你可以 将调用实现为ItemReader并在该过程完成后返回 null。 但是,这样做有点不自然,因为需要无作ItemWriter. Spring Batch 提供了TaskletStep对于此方案。spring-doc.cadn.net.cn

Tasklet是一个简单的接口,它只有一个方法execute,称为 由TaskletStep直到它返回RepeatStatus.FINISHED或者投掷 表示失败的异常。每次调用Tasklet包装在事务中。Tasklet实现者可以调用存储过程、脚本或简单的 SQL 更新 陈述。spring-doc.cadn.net.cn

要创建TaskletStep在 XML 中,使用<tasklet/>元素应 引用一个定义Tasklet对象。不<chunk/>元素 在<tasklet/>.下面的示例展示了一个简单的 tasklet:spring-doc.cadn.net.cn

<step id="step1">
    <tasklet ref="myTasklet"/>
</step>

要创建TaskletStep在 Java 中,传递给tasklet构建器的方法 应实现Tasklet接口。无需调用chunk应在 构建TaskletStep.下面的示例展示了一个简单的 tasklet:spring-doc.cadn.net.cn

@Bean
public Step step1() {
    return this.stepBuilderFactory.get("step1")
    			.tasklet(myTasklet())
    			.build();
}

TaskletStep自动注册 tasklet 作为StepListener如果它实现了StepListener接口。spring-doc.cadn.net.cn

TaskletAdapter

ItemReaderItemWriterinterfaces 中,,Taskletinterface 包含一个实现,该实现允许自身适应任何预先存在的 类:TaskletAdapter.这可能有用的一个示例是现有的 DAO,它是 用于更新一组记录上的标志。这TaskletAdapter可以用来调用 this 类中,而不必为Tasklet接口。spring-doc.cadn.net.cn

以下示例演示如何定义TaskletAdapter在 XML 中:spring-doc.cadn.net.cn

XML 配置
<bean id="myTasklet" class="o.s.b.core.step.tasklet.MethodInvokingTaskletAdapter">
    <property name="targetObject">
        <bean class="org.mycompany.FooDao"/>
    </property>
    <property name="targetMethod" value="updateFoo" />
</bean>

以下示例演示如何定义TaskletAdapter在 Java 中:spring-doc.cadn.net.cn

Java 配置
@Bean
public MethodInvokingTaskletAdapter myTasklet() {
	MethodInvokingTaskletAdapter adapter = new MethodInvokingTaskletAdapter();

	adapter.setTargetObject(fooDao());
	adapter.setTargetMethod("updateFoo");

	return adapter;
}

Tasklet实现

许多批处理作业包含在主处理开始之前必须完成的步骤 来设置各种资源,或者在处理完成后清理这些资源 资源。对于大量处理文件的工作,通常需要 在将某些文件成功上传到另一个文件后,在本地删除这些文件 位置。以下示例(摘自 Spring Batch samples 项目)是一个Tasklet实现中就有这样的责任:spring-doc.cadn.net.cn

public class FileDeletingTasklet implements Tasklet, InitializingBean {

    private Resource directory;

    public RepeatStatus execute(StepContribution contribution,
                                ChunkContext chunkContext) throws Exception {
        File dir = directory.getFile();
        Assert.state(dir.isDirectory());

        File[] files = dir.listFiles();
        for (int i = 0; i < files.length; i++) {
            boolean deleted = files[i].delete();
            if (!deleted) {
                throw new UnexpectedJobExecutionException("Could not delete file " +
                                                          files[i].getPath());
            }
        }
        return RepeatStatus.FINISHED;
    }

    public void setDirectoryResource(Resource directory) {
        this.directory = directory;
    }

    public void afterPropertiesSet() throws Exception {
        Assert.notNull(directory, "directory must be set");
    }
}

前面的taskletimplementation 会删除给定目录中的所有文件。它 需要注意的是,executemethod 仅调用一次。剩下的就是 引用taskletstep.spring-doc.cadn.net.cn

以下示例演示如何引用taskletstep在 XML 中:spring-doc.cadn.net.cn

XML 配置
<job id="taskletJob">
    <step id="deleteFilesInDir">
       <tasklet ref="fileDeletingTasklet"/>
    </step>
</job>

<beans:bean id="fileDeletingTasklet"
            class="org.springframework.batch.sample.tasklet.FileDeletingTasklet">
    <beans:property name="directoryResource">
        <beans:bean id="directory"
                    class="org.springframework.core.io.FileSystemResource">
            <beans:constructor-arg value="target/test-outputs/test-dir" />
        </beans:bean>
    </beans:property>
</beans:bean>

以下示例演示如何引用taskletstep在 Java 中:spring-doc.cadn.net.cn

Java 配置
@Bean
public Job taskletJob() {
	return this.jobBuilderFactory.get("taskletJob")
				.start(deleteFilesInDir())
				.build();
}

@Bean
public Step deleteFilesInDir() {
	return this.stepBuilderFactory.get("deleteFilesInDir")
				.tasklet(fileDeletingTasklet())
				.build();
}

@Bean
public FileDeletingTasklet fileDeletingTasklet() {
	FileDeletingTasklet tasklet = new FileDeletingTasklet();

	tasklet.setDirectoryResource(new FileSystemResource("target/test-outputs/test-dir"));

	return tasklet;
}

控制 Step Flow

随着能够在拥有作业中将步骤组合在一起,需要能够 来控制作业如何从一个步骤“流”到另一个步骤。失败Step不 必然意味着Job应该失败。此外,可能有多种类型 的“成功”决定了哪个Step应该执行。根据 组Steps配置后,某些步骤甚至可能根本不被处理。spring-doc.cadn.net.cn

顺序流

最简单的流场景是所有步骤按顺序执行的作业,如图所示 在下图中:spring-doc.cadn.net.cn

顺序流
图 4.顺序流

这可以通过在step.spring-doc.cadn.net.cn

以下示例演示如何使用next属性:spring-doc.cadn.net.cn

XML 配置
<job id="job">
    <step id="stepA" parent="s1" next="stepB" />
    <step id="stepB" parent="s2" next="stepC"/>
    <step id="stepC" parent="s3" />
</job>

以下示例演示如何使用next()method 中:spring-doc.cadn.net.cn

Java 配置
@Bean
public Job job() {
	return this.jobBuilderFactory.get("job")
				.start(stepA())
				.next(stepB())
				.next(stepC())
				.build();
}

在上面的场景中,“步骤 A”首先运行,因为它是第一个Step上市。如果 “step A”正常完成,然后“step B”运行,依此类推。但是,如果“步骤 A”失败,则 然后整个Job失败,并且“步骤 B”未执行。spring-doc.cadn.net.cn

使用 Spring Batch XML 命名空间时,配置中列出的第一步始终是由Job.其他步骤元素的顺序不会 matter 的 API 中,但第一步必须始终首先出现在 XML 中。spring-doc.cadn.net.cn

条件流

在上面的示例中,只有两种可能性:spring-doc.cadn.net.cn

  1. step成功了,接下来step应该执行。spring-doc.cadn.net.cn

  2. step失败,因此,job应该失败。spring-doc.cadn.net.cn

在许多情况下,这可能就足够了。但是,如果 失败step应该触发不同的step,而不是导致失败?这 下图显示了这样的流程:spring-doc.cadn.net.cn

条件流
图 5.条件流

为了处理更复杂的场景,Spring Batch XML 命名空间允许过渡 要在 Step 元素中定义的元素。其中一个转换是next元素。与next属性、next元素告诉JobStep自 执行 next。但是,与 attribute 不同的是,任意数量的next元素允许 一个给定的Step,并且在失败的情况下没有默认行为。这意味着,如果 transition 元素,则Step过渡必须为 显式定义。另请注意,单个步骤不能同时具有nextattribute 和 一个transition元素。spring-doc.cadn.net.cn

next元素指定要匹配的模式和接下来要执行的步骤,如 以下示例:spring-doc.cadn.net.cn

XML 配置
<job id="job">
    <step id="stepA" parent="s1">
        <next on="*" to="stepB" />
        <next on="FAILED" to="stepC" />
    </step>
    <step id="stepB" parent="s2" next="stepC" />
    <step id="stepC" parent="s3" />
</job>

Java API 提供了一组 Fluent 方法,允许您指定流和要执行的作 当步骤失败时。以下示例显示如何指定一个步骤 (stepA),然后 继续执行两个不同步骤之一 (stepBstepC),具体取决于stepA成功:spring-doc.cadn.net.cn

Java 配置
@Bean
public Job job() {
	return this.jobBuilderFactory.get("job")
				.start(stepA())
				.on("*").to(stepB())
				.from(stepA()).on("FAILED").to(stepC())
				.end()
				.build();
}

使用 XML 配置时,on属性使用简单的 pattern-matching 方案匹配ExitStatus,这是执行Step.spring-doc.cadn.net.cn

使用 java 配置时,on()方法使用简单的模式匹配方案来 匹配ExitStatus,这是执行Step.spring-doc.cadn.net.cn

模式中只允许使用两个特殊字符:spring-doc.cadn.net.cn

例如,“c*t” 匹配 “cat” 和 “count”,而 “c?t” 匹配 “cat” 但不匹配 “count”。spring-doc.cadn.net.cn

虽然Step,如果Step执行会导致ExitStatus未被元素覆盖,则 framework 会引发异常,并且Job失败。框架会自动排序 从最特异性到最不特异性的转换。这意味着,即使 被替换为 “stepA” 在上面的示例中,一个ExitStatus的 “FAILED” 仍会显示 设置为 “stepC”。spring-doc.cadn.net.cn

批处理状态与退出状态

配置Job对于条件流,了解 区别BatchStatusExitStatus.BatchStatus是一个枚举,其中 是两者的属性JobExecutionStepExecution,并被框架用于 记录JobStep.它可以是以下值之一:COMPLETED,STARTING,STARTED,STOPPING,STOPPED,FAILED,ABANDONEDUNKNOWN.他们中的大多数都是不言自明的:COMPLETED是步骤 或作业已成功完成,FAILED在失败时设置,依此类推。spring-doc.cadn.net.cn

以下示例在使用 XML 配置时包含 'next' 元素:spring-doc.cadn.net.cn

<next on="FAILED" to="stepB" />

以下示例在使用 Java 配置时包含 'on' 元素:spring-doc.cadn.net.cn

...
.from(stepA()).on("FAILED").to(stepB())
...

乍一看,“on”似乎引用了BatchStatusStep自 它属于它。但是,它实际上引用了ExitStatusStep.由于 顾名思义ExitStatus表示Step在它完成执行之后。spring-doc.cadn.net.cn

更具体地说,当使用 XML 配置时,显示在 前面的 XML 配置示例引用了ExitStatus.spring-doc.cadn.net.cn

当使用 Java 配置时,前面 Java 配置示例引用了ExitStatus.spring-doc.cadn.net.cn

在英语中,它说:“如果退出代码是FAILED".默认情况下,exit code 始终与BatchStatus对于Step,这就是为什么上面的条目 工程。但是,如果退出代码需要不同,该怎么办?一个很好的例子来自 Samples 项目中的 Skip Sample 作业:spring-doc.cadn.net.cn

下面的示例演示如何在 XML 中使用不同的退出代码:spring-doc.cadn.net.cn

XML 配置
<step id="step1" parent="s1">
    <end on="FAILED" />
    <next on="COMPLETED WITH SKIPS" to="errorPrint1" />
    <next on="*" to="step2" />
</step>

以下示例演示如何在 Java 中使用不同的退出代码:spring-doc.cadn.net.cn

Java 配置
@Bean
public Job job() {
	return this.jobBuilderFactory.get("job")
			.start(step1()).on("FAILED").end()
			.from(step1()).on("COMPLETED WITH SKIPS").to(errorPrint1())
			.from(step1()).on("*").to(step2())
			.end()
			.build();
}

step1有三种可能性:spring-doc.cadn.net.cn

  1. Stepfailed,在这种情况下,作业应该会失败。spring-doc.cadn.net.cn

  2. Step成功完成。spring-doc.cadn.net.cn

  3. Step已成功完成,但退出代码为“COMPLETED WITH SKIPS”。在 在这种情况下,应运行不同的步骤来处理错误。spring-doc.cadn.net.cn

上述配置有效。但是,需要根据 执行跳过记录的条件,如以下示例所示:spring-doc.cadn.net.cn

public class SkipCheckingListener extends StepExecutionListenerSupport {
    public ExitStatus afterStep(StepExecution stepExecution) {
        String exitCode = stepExecution.getExitStatus().getExitCode();
        if (!exitCode.equals(ExitStatus.FAILED.getExitCode()) &&
              stepExecution.getSkipCount() > 0) {
            return new ExitStatus("COMPLETED WITH SKIPS");
        }
        else {
            return null;
        }
    }
}

上面的代码是一个StepExecutionListener首先检查以确保Step是 successful,然后检查StepExecution高于 0. 如果两个条件都满足,则新的ExitStatus退出代码为COMPLETED WITH SKIPS返回。spring-doc.cadn.net.cn

配置 Stop

在讨论了 BatchStatus 和 ExitStatus 之后, 有人可能想知道BatchStatusExitStatus确定为Job. 虽然这些状态是针对Step通过执行的代码, statuss 的Job根据配置确定。spring-doc.cadn.net.cn

到目前为止,讨论的所有作业配置都至少有一个 finalStep跟 无过渡。spring-doc.cadn.net.cn

在下面的 XML 示例中,在step执行时,将Job结束:spring-doc.cadn.net.cn

<step id="stepC" parent="s3"/>

在下面的 Java 示例中,在step执行时,将Job结束:spring-doc.cadn.net.cn

@Bean
public Job job() {
	return this.jobBuilderFactory.get("job")
				.start(step1())
				.build();
}

如果没有为Step,则Job定义为 遵循:spring-doc.cadn.net.cn

虽然这种终止批处理作业的方法对于某些批处理作业(如 可能需要简单的 Sequential Step Job,自定义 Job-Stopping 场景。为 为此,Spring Batch 提供了三个 transition 元素来停止Job(在 添加到next元素我们之前讨论过)。 这些停止元素中的每一个都会停止一个Job替换为特定的BatchStatus.是的 请务必注意,Stop 过渡元素对BatchStatusExitStatus任何StepsJob.这些元素仅影响 的最终状态Job.例如,作业中的每个步骤都可能具有 状态为FAILED但要使作业的状态为COMPLETED.spring-doc.cadn.net.cn

在步骤处结束

配置步骤结束会指示JobBatchStatusCOMPLETED.一个Job已完成,且状态COMPLETED无法重新启动(框架会引发 一个JobInstanceAlreadyCompleteException).spring-doc.cadn.net.cn

使用 XML 配置时,“end”元素用于此任务。这end元素 还允许可选的 'exit-code' 属性,可用于自定义ExitStatusJob.如果未给出 'exit-code' 属性,则ExitStatusCOMPLETED默认情况下,要匹配BatchStatus.spring-doc.cadn.net.cn

使用 Java 配置时,'end' 方法用于此任务。这end方法 还允许使用可选的 'exitStatus' 参数,该参数可用于自定义ExitStatusJob.如果未提供 'exitStatus' 值,则ExitStatusCOMPLETED默认情况下,要匹配BatchStatus.spring-doc.cadn.net.cn

考虑以下场景:如果step2失败,则JobBatchStatusCOMPLETED以及一个ExitStatusCOMPLETEDstep3不会运行。 否则,执行将移至step3.请注意,如果step2失败时,该Job莫 Restartable (因为状态为COMPLETED).spring-doc.cadn.net.cn

以下示例显示了 XML 中的场景:spring-doc.cadn.net.cn

<step id="step1" parent="s1" next="step2">

<step id="step2" parent="s2">
    <end on="FAILED"/>
    <next on="*" to="step3"/>
</step>

<step id="step3" parent="s3">

以下示例显示了 Java 中的场景:spring-doc.cadn.net.cn

@Bean
public Job job() {
	return this.jobBuilderFactory.get("job")
				.start(step1())
				.next(step2())
				.on("FAILED").end()
				.from(step2()).on("*").to(step3())
				.end()
				.build();
}
步骤失败

将步骤配置为在给定点失败会指示JobBatchStatusFAILED.与 end 不同,一个Job不会阻止Job免于重新启动。spring-doc.cadn.net.cn

当使用 XML 配置时,'fail' 元素还允许可选的 'exit-code' 属性,可用于自定义ExitStatusJob.如果没有 'exit-code' 属性,则ExitStatusFAILED默认情况下,要匹配BatchStatus.spring-doc.cadn.net.cn

请考虑以下场景,如果step2失败,则JobBatchStatusFAILED以及一个ExitStatusEARLY TERMINATIONstep3不 执行。否则,执行将移至step3.此外,如果step2fails 和Job重新启动,然后执行再次开始step2.spring-doc.cadn.net.cn

以下示例显示了 XML 中的场景:spring-doc.cadn.net.cn

XML 配置
<step id="step1" parent="s1" next="step2">

<step id="step2" parent="s2">
    <fail on="FAILED" exit-code="EARLY TERMINATION"/>
    <next on="*" to="step3"/>
</step>

<step id="step3" parent="s3">

以下示例显示了 Java 中的场景:spring-doc.cadn.net.cn

Java 配置
@Bean
public Job job() {
	return this.jobBuilderFactory.get("job")
			.start(step1())
			.next(step2()).on("FAILED").fail()
			.from(step2()).on("*").to(step3())
			.end()
			.build();
}
在给定步骤停止作业

将作业配置为在特定步骤停止会指示JobBatchStatusSTOPPED.停止Job可以在处理中提供临时中断, 以便作员可以在重新启动Job.spring-doc.cadn.net.cn

使用 XML 配置时,'stop' 元素需要一个 'restart' 属性,该属性指定 重新启动 Job 时应开始执行的步骤。spring-doc.cadn.net.cn

使用 Java 配置时,stopAndRestartmethod 需要 'restart' 属性 ,该步骤指定在重新启动 Job 时应执行的步骤。spring-doc.cadn.net.cn

考虑以下场景:如果step1finishs 以COMPLETE,然后 Job 停止。重新启动后,执行将从step2.spring-doc.cadn.net.cn

下面的清单显示了 XML 中的场景:spring-doc.cadn.net.cn

<step id="step1" parent="s1">
    <stop on="COMPLETED" restart="step2"/>
</step>

<step id="step2" parent="s2"/>

以下示例显示了 Java 中的场景:spring-doc.cadn.net.cn

@Bean
public Job job() {
	return this.jobBuilderFactory.get("job")
			.start(step1()).on("COMPLETED").stopAndRestart(step2())
			.end()
			.build();
}

编程流决策

在某些情况下,比ExitStatus可能需要决定 下一步要执行的步骤。在这种情况下,JobExecutionDecider可用于协助 在决策中,如以下示例所示:spring-doc.cadn.net.cn

public class MyDecider implements JobExecutionDecider {
    public FlowExecutionStatus decide(JobExecution jobExecution, StepExecution stepExecution) {
        String status;
        if (someCondition()) {
            status = "FAILED";
        }
        else {
            status = "COMPLETED";
        }
        return new FlowExecutionStatus(status);
    }
}

在以下示例作业配置中,decision指定要用作的决策程序 以及所有过渡:spring-doc.cadn.net.cn

XML 配置
<job id="job">
    <step id="step1" parent="s1" next="decision" />

    <decision id="decision" decider="decider">
        <next on="FAILED" to="step2" />
        <next on="COMPLETED" to="step3" />
    </decision>

    <step id="step2" parent="s2" next="step3"/>
    <step id="step3" parent="s3" />
</job>

<beans:bean id="decider" class="com.MyDecider"/>

在下面的示例中,实现JobExecutionDecider的传递 直接发送到nextcall 时调用。spring-doc.cadn.net.cn

Java 配置
@Bean
public Job job() {
	return this.jobBuilderFactory.get("job")
			.start(step1())
			.next(decider()).on("FAILED").to(step2())
			.from(decider()).on("COMPLETED").to(step3())
			.end()
			.build();
}

拆分流

到目前为止描述的每个场景都涉及Job,在 时间以线性方式。除了这种典型的样式之外, Spring Batch 还允许 对于要配置并行流的作业。spring-doc.cadn.net.cn

XML 命名空间允许您使用 'split' 元素。如下例所示, 'split' 元素包含一个或多个 'flow' 元素,其中整个单独的 flows 可以 被定义。'split' 元素也可以包含前面讨论的任何过渡 元素,例如 'next' 属性或 'next'、'end' 或 'fail' 元素。spring-doc.cadn.net.cn

<split id="split1" next="step4">
    <flow>
        <step id="step1" parent="s1" next="step2"/>
        <step id="step2" parent="s2"/>
    </flow>
    <flow>
        <step id="step3" parent="s3"/>
    </flow>
</split>
<step id="step4" parent="s4"/>

基于 Java 的配置允许您通过提供的构建器配置拆分。由于 以下示例显示,'split' 元素包含一个或多个 'flow' 元素,其中 可以定义整个单独的流。'split' 元素也可以包含任何 前面讨论过的过渡元素,比如 'next' 属性或 'next', 'end' 或 'fail' 元素。spring-doc.cadn.net.cn

@Bean
public Flow flow1() {
	return new FlowBuilder<SimpleFlow>("flow1")
			.start(step1())
			.next(step2())
			.build();
}

@Bean
public Flow flow2() {
	return new FlowBuilder<SimpleFlow>("flow2")
			.start(step3())
			.build();
}

@Bean
public Job job(Flow flow1, Flow flow2) {
	return this.jobBuilderFactory.get("job")
				.start(flow1)
				.split(new SimpleAsyncTaskExecutor())
				.add(flow2)
				.next(step4())
				.end()
				.build();
}

外部化流程定义和作业之间的依赖关系

作业中的一部分流可以外部化为单独的 bean 定义,然后 重复使用。有两种方法可以做到这一点。第一种是简单地将流声明为 引用到其他地方定义的 one。spring-doc.cadn.net.cn

以下示例演示如何将流声明为对定义的流的引用 XML 中的其他位置:spring-doc.cadn.net.cn

XML 配置
<job id="job">
    <flow id="job1.flow1" parent="flow1" next="step3"/>
    <step id="step3" parent="s3"/>
</job>

<flow id="flow1">
    <step id="step1" parent="s1" next="step2"/>
    <step id="step2" parent="s2"/>
</flow>

以下示例演示如何将流声明为对定义的流的引用 Java 中的其他位置:spring-doc.cadn.net.cn

Java 配置
@Bean
public Job job() {
	return this.jobBuilderFactory.get("job")
				.start(flow1())
				.next(step3())
				.end()
				.build();
}

@Bean
public Flow flow1() {
	return new FlowBuilder<SimpleFlow>("flow1")
			.start(step1())
			.next(step2())
			.build();
}

如前面的示例所示,定义外部流的效果是将 从外部流入作业的步骤,就像它们已内联声明一样。在 这样,许多 Job 可以引用相同的模板流,并将此类模板组合成 不同的逻辑流。这也是将 个人流动。spring-doc.cadn.net.cn

外部化流的另一种形式是使用JobStep.一个JobStep类似于FlowStep但实际上为 指定的流。spring-doc.cadn.net.cn

以下示例如何JobStep在 XML 中:spring-doc.cadn.net.cn

XML 配置
<job id="jobStepJob" restartable="true">
   <step id="jobStepJob.step1">
      <job ref="job" job-launcher="jobLauncher"
          job-parameters-extractor="jobParametersExtractor"/>
   </step>
</job>

<job id="job" restartable="true">...</job>

<bean id="jobParametersExtractor" class="org.spr...DefaultJobParametersExtractor">
   <property name="keys" value="input.file"/>
</bean>

以下示例显示了JobStep在 Java 中:spring-doc.cadn.net.cn

Java 配置
@Bean
public Job jobStepJob() {
	return this.jobBuilderFactory.get("jobStepJob")
				.start(jobStepJobStep1(null))
				.build();
}

@Bean
public Step jobStepJobStep1(JobLauncher jobLauncher) {
	return this.stepBuilderFactory.get("jobStepJobStep1")
				.job(job())
				.launcher(jobLauncher)
				.parametersExtractor(jobParametersExtractor())
				.build();
}

@Bean
public Job job() {
	return this.jobBuilderFactory.get("job")
				.start(step1())
				.build();
}

@Bean
public DefaultJobParametersExtractor jobParametersExtractor() {
	DefaultJobParametersExtractor extractor = new DefaultJobParametersExtractor();

	extractor.setKeys(new String[]{"input.file"});

	return extractor;
}

作业参数提取器是一种策略,用于确定ExecutionContext为 这Step转换为JobParameters对于Job那就是 RUN。这JobStep是 当您希望使用一些更精细的选项来监控和报告 作业和步骤。用JobStep也常常是很好的回答:“我怎么做 在作业之间创建依赖关系?这是将大型系统分解为 更小的模块并控制作业流。spring-doc.cadn.net.cn

的后期绑定JobStep属性

前面显示的 XML 和平面文件示例都使用 SpringResource抽象化 以获取文件。这之所以有效,是因为Resource具有getFile方法,该方法返回一个java.io.File.XML 和平面文件资源都可以使用标准 Spring 进行配置 构建:spring-doc.cadn.net.cn

以下示例显示了 XML 中的后期绑定:spring-doc.cadn.net.cn

XML 配置
<bean id="flatFileItemReader"
      class="org.springframework.batch.item.file.FlatFileItemReader">
    <property name="resource"
              value="file://outputs/file.txt" />
</bean>

以下示例显示了 Java 中的后期绑定:spring-doc.cadn.net.cn

Java 配置
@Bean
public FlatFileItemReader flatFileItemReader() {
	FlatFileItemReader<Foo> reader = new FlatFileItemReaderBuilder<Foo>()
			.name("flatFileItemReader")
			.resource(new FileSystemResource("file://outputs/file.txt"))
			...
}

前面的Resource从指定的文件系统位置加载文件。注意 绝对位置必须以双斜杠 () 开头。在大多数Spring 应用程序,此解决方案已足够好,因为这些资源的名称为 在编译时已知。但是,在批处理方案中,文件名可能需要 在运行时确定为作业的参数。这可以使用 '-D' 参数来解决 读取系统属性。//spring-doc.cadn.net.cn

以下示例演示如何从 XML 中的属性读取文件名:spring-doc.cadn.net.cn

XML 配置
<bean id="flatFileItemReader"
      class="org.springframework.batch.item.file.FlatFileItemReader">
    <property name="resource" value="${input.file.name}" />
</bean>

下面显示了如何从 Java 中的属性读取文件名:spring-doc.cadn.net.cn

Java 配置
@Bean
public FlatFileItemReader flatFileItemReader(@Value("${input.file.name}") String name) {
	return new FlatFileItemReaderBuilder<Foo>()
			.name("flatFileItemReader")
			.resource(new FileSystemResource(name))
			...
}

此解决方案工作所需的只是一个系统参数(例如-Dinput.file.name="file://outputs/file.txt").spring-doc.cadn.net.cn

虽然PropertyPlaceholderConfigurer可以在这里使用,它不是 如果始终设置 system 属性,则为 necessary ,因为ResourceEditor春季 already 对系统属性进行筛选和执行占位符替换。

通常,在批处理设置中,最好在JobParameters而不是通过系统属性访问它们,并访问 道路。为了实现这一点, Spring Batch 允许对各种JobStep属性。spring-doc.cadn.net.cn

以下示例演示如何在 XML 中参数化文件名:spring-doc.cadn.net.cn

XML 配置
<bean id="flatFileItemReader" scope="step"
      class="org.springframework.batch.item.file.FlatFileItemReader">
    <property name="resource" value="#{jobParameters['input.file.name']}" />
</bean>

以下示例演示如何在 Java 中参数化文件名:spring-doc.cadn.net.cn

Java 配置
@StepScope
@Bean
public FlatFileItemReader flatFileItemReader(@Value("#{jobParameters['input.file.name']}") String name) {
	return new FlatFileItemReaderBuilder<Foo>()
			.name("flatFileItemReader")
			.resource(new FileSystemResource(name))
			...
}

JobExecutionStepExecution水平ExecutionContext可以在 同样的方式。spring-doc.cadn.net.cn

以下示例显示如何访问ExecutionContext在 XML 中:spring-doc.cadn.net.cn

XML 配置
<bean id="flatFileItemReader" scope="step"
      class="org.springframework.batch.item.file.FlatFileItemReader">
    <property name="resource" value="#{jobExecutionContext['input.file.name']}" />
</bean>
XML 配置
<bean id="flatFileItemReader" scope="step"
      class="org.springframework.batch.item.file.FlatFileItemReader">
    <property name="resource" value="#{stepExecutionContext['input.file.name']}" />
</bean>

以下示例显示如何访问ExecutionContext在 Java 中:spring-doc.cadn.net.cn

Java 配置
@StepScope
@Bean
public FlatFileItemReader flatFileItemReader(@Value("#{jobExecutionContext['input.file.name']}") String name) {
	return new FlatFileItemReaderBuilder<Foo>()
			.name("flatFileItemReader")
			.resource(new FileSystemResource(name))
			...
}
Java 配置
@StepScope
@Bean
public FlatFileItemReader flatFileItemReader(@Value("#{stepExecutionContext['input.file.name']}") String name) {
	return new FlatFileItemReaderBuilder<Foo>()
			.name("flatFileItemReader")
			.resource(new FileSystemResource(name))
			...
}

任何使用后期绑定的 bean 都必须使用 scope=“step” 进行声明。有关更多信息,请参阅 Step Scope。需要注意的是 那个Stepbean 不应该是 step-scopes。如果步骤中需要后期绑定 定义,该步骤的组件(即 tasklet、item reader/writer 等) 是应该改为限定范围的 Scope。spring-doc.cadn.net.cn

如果您使用的是 Spring 3.0(或更高版本),则步进作用域 bean 中的表达式位于 Spring Expression Language,一种强大的通用语言,具有许多有趣的 特征。为了提供向后兼容性,如果 Spring Batch 检测到存在 旧版本的 Spring,它使用一种功能较弱的原生表达式语言,并且 的解析规则略有不同。主要区别在于 Map 键 上面的示例不需要用 Spring 2.5 引用,但引用是强制性的 在 Spring 3.0 中。spring-doc.cadn.net.cn

Step 范围

前面显示的所有后期绑定示例都在 bean 定义。spring-doc.cadn.net.cn

以下示例显示了在 XML 中绑定到步骤范围的示例:spring-doc.cadn.net.cn

XML 配置
<bean id="flatFileItemReader" scope="step"
      class="org.springframework.batch.item.file.FlatFileItemReader">
    <property name="resource" value="#{jobParameters[input.file.name]}" />
</bean>

以下示例显示了在 Java 中绑定到 step 范围的示例:spring-doc.cadn.net.cn

Java 配置
@StepScope
@Bean
public FlatFileItemReader flatFileItemReader(@Value("#{jobParameters[input.file.name]}") String name) {
	return new FlatFileItemReaderBuilder<Foo>()
			.name("flatFileItemReader")
			.resource(new FileSystemResource(name))
			...
}

使用Step是必需的,因为 Bean 不能 实际上被实例化,直到Step启动,以允许查找属性。 因为它默认不是 Spring 容器的一部分,所以必须添加范围 显式地,通过使用batchnamespace 或通过显式包含 bean 定义 对于StepScope,或使用@EnableBatchProcessing注解。仅使用以下之一 那些方法。以下示例使用batchNamespace:spring-doc.cadn.net.cn

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:batch="http://www.springframework.org/schema/batch"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="...">
<batch:job .../>
...
</beans>

以下示例显式包含 bean 定义:spring-doc.cadn.net.cn

<bean class="org.springframework.batch.core.scope.StepScope" />

工作范围

Jobscope 类似于Step配置中的 scope 但都是Jobcontext,因此只有这样一个 bean 的实例 每个正在运行的作业。此外,还支持引用的后期绑定 可从JobContext#{..}占位符。使用此功能,bean 可以从 Job 或 Job execution 上下文以及 Job 参数中提取属性。spring-doc.cadn.net.cn

以下示例显示了在 XML 中绑定到作业范围的示例:spring-doc.cadn.net.cn

XML 配置
<bean id="..." class="..." scope="job">
    <property name="name" value="#{jobParameters[input]}" />
</bean>
XML 配置
<bean id="..." class="..." scope="job">
    <property name="name" value="#{jobExecutionContext['input.name']}.txt" />
</bean>

以下示例显示了在 Java 中绑定到作业范围的示例:spring-doc.cadn.net.cn

Java 配置
@JobScope
@Bean
public FlatFileItemReader flatFileItemReader(@Value("#{jobParameters[input]}") String name) {
	return new FlatFileItemReaderBuilder<Foo>()
			.name("flatFileItemReader")
			.resource(new FileSystemResource(name))
			...
}
Java 配置
@JobScope
@Bean
public FlatFileItemReader flatFileItemReader(@Value("#{jobExecutionContext['input.name']}") String name) {
	return new FlatFileItemReaderBuilder<Foo>()
			.name("flatFileItemReader")
			.resource(new FileSystemResource(name))
			...
}

因为它默认不是 Spring 容器的一部分,所以必须添加范围 显式地,通过使用batch命名空间,通过显式包含 JobScope,或使用@EnableBatchProcessing注释(但不是全部)。 以下示例使用batchNamespace:spring-doc.cadn.net.cn

<beans xmlns="http://www.springframework.org/schema/beans"
		  xmlns:batch="http://www.springframework.org/schema/batch"
		  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		  xsi:schemaLocation="...">

<batch:job .../>
...
</beans>

以下示例包括一个 Bean,它显式定义了JobScope:spring-doc.cadn.net.cn

<bean class="org.springframework.batch.core.scope.JobScope" />

在多线程中使用作业范围的 bean 存在一些实际限制 或分区步骤。Spring Batch 不控制在这些 用例,因此无法正确设置它们以使用此类 bean。因此 不建议在多线程或分区步骤中使用作业范围的 bean。spring-doc.cadn.net.cn

范围ItemStream组件

当使用 Java 配置样式定义作业或步骤范围ItemStream豆 Bean 定义方法的返回类型应至少为ItemStream.这是必需的 以便 Spring Batch 正确创建实现此接口的代理,因此 通过调用open,updateclose方法。spring-doc.cadn.net.cn

建议将此类 bean 的 bean 定义方法返回最具体的 已知实现,如以下示例所示:spring-doc.cadn.net.cn

定义具有最具体返回类型的步进作用域 Bean
@Bean
@StepScope
public FlatFileItemReader flatFileItemReader(@Value("#{jobParameters['input.file.name']}") String name) {
	return new FlatFileItemReaderBuilder<Foo>()
			.resource(new FileSystemResource(name))
			// set other properties of the item reader
			.build();
}