控制 Step Flow

随着能够在拥有作业中将步骤组合在一起,需要能够 来控制作业如何从一个步骤“流”到另一个步骤。a 的失败不会 必然意味着 应该失败。此外,可能有多种类型 的 “success” 中,确定接下来应该执行哪个。根据 组,则某些步骤甚至可能根本不被处理。StepJobStepStepsspring-doc.cn

流程定义中的 Step Bean 方法代理

步骤实例在流程定义中必须是唯一的。当一个步骤在流程定义中具有多个结果时, 将步骤的相同实例传递给流定义方法(、 、 等)非常重要。 否则,流程执行可能会发生意外行为。startfromspring-doc.cn

在以下示例中,步骤作为参数注入到流或作业 Bean 定义方法中。这种依赖关系注入样式保证了流程定义中步骤的唯一性。 但是,如果流是通过调用注释有 的步骤定义方法定义的,则如果禁用了 bean 方法代理(即 ),则步骤可能不唯一。 如果首选 bean 间注入样式,则必须启用 bean 方法代理。@Bean@Configuration(proxyBeanMethods = false)spring-doc.cn

请参阅 使用 @Configuration 注释 部分,以了解有关 Spring Framework 中 bean 方法代理的更多详细信息。spring-doc.cn

顺序流

最简单的流场景是所有步骤按顺序执行的作业,因为 下图显示了:spring-doc.cn

顺序流
图 1.顺序流

这可以通过在 .nextstepspring-doc.cn

以下示例演示如何在 Java 中使用该方法:next()spring-doc.cn

Java 配置
@Bean
public Job job(JobRepository jobRepository, Step stepA, Step stepB, Step stepC) {
	return new JobBuilder("job", jobRepository)
				.start(stepA)
				.next(stepB)
				.next(stepC)
				.build();
}

下面的示例演示如何在 XML 中使用该属性:nextspring-doc.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>

在上面的方案中,首先运行,因为它是第一个列出的。如果正常完成,则运行,依此类推。但是,如果失败,则 整个失败且不执行。stepAStepstepAstepBstep AJobstepBspring-doc.cn

对于 Spring Batch XML 命名空间,配置中列出的第一步始终是由 .其他步骤元素的顺序不会 很重要,但第一步必须始终首先出现在 XML 中。Job

条件流

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

  1. 操作成功,下一步应执行。stepstepspring-doc.cn

  2. 失败的,因此,应该失败。stepjobspring-doc.cn

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

条件流
图 2.条件流

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

Java 配置
@Bean
public Job job(JobRepository jobRepository, Step stepA, Step stepB, Step stepC) {
	return new JobBuilder("job", jobRepository)
				.start(stepA)
				.on("*").to(stepB)
				.from(stepA).on("FAILED").to(stepC)
				.end()
				.build();
}

为了处理更复杂的情况,Spring Batch XML 命名空间允许您定义转换 元素。其中一种过渡是 element。与属性一样,元素告诉 which to 执行 next。但是,与 attribute 不同的是,允许在 给定的 ,并且在失败的情况下没有默认行为。这意味着,如果 transition 元素,则 transition 的所有行为都必须是 显式定义。另请注意,单个步骤不能同时具有属性和 一个元素。nextnextnextJobStepnextStepStepnexttransitionspring-doc.cn

该元素指定要匹配的模式和接下来要执行的步骤,如 以下示例显示:nextspring-doc.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 配置时,该方法使用简单的模式匹配方案来 匹配执行 .on()ExitStatusStepspring-doc.cn

使用 XML 配置时,transition 元素的属性使用简单的 pattern-matching 方案匹配 ,以匹配执行 .onExitStatusStepspring-doc.cn

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

例如,matches 和 ,while matches but not .c*tcatcountc?tcatcountspring-doc.cn

虽然 上的过渡元素数量没有限制 ,但如果执行导致 an 未被元素覆盖,则 framework 引发异常,并且失败。框架会自动排序 从最特异性到最不特异性的转换。这意味着,即使 在前面的示例中交换,则 OF 仍会 自。StepStepExitStatusJobstepAExitStatusFAILEDstepCspring-doc.cn

批处理状态与退出状态

配置 for 条件流时,了解 和 之间的区别。 是一个枚举,其中 是 和 和 的属性,框架使用它来 记录 或 的状态。它可以是以下值之一: 、 、 、 、 或 。它们中的大多数是不言自明的:是当一个步骤 或 Job 已成功完成,在失败时设置,依此类推。JobBatchStatusExitStatusBatchStatusJobExecutionStepExecutionJobStepCOMPLETEDSTARTINGSTARTEDSTOPPINGSTOPPEDFAILEDABANDONEDUNKNOWNCOMPLETEDFAILEDspring-doc.cn

以下示例包含使用 Java 配置时的元素:onspring-doc.cn

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

以下示例包含使用 XML 配置时的元素:nextspring-doc.cn

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

乍一看,似乎引用了 to 它属于它。但是,它实际上引用了 .由于 name 表示 A 完成执行后的状态。onBatchStatusStepExitStatusStepExitStatusStepspring-doc.cn

使用 Java 配置时,前面显示的方法 Java 配置示例引用了 的退出代码。on()ExitStatusspring-doc.cn

更具体地说,在使用 XML 配置时, 前面的 XML 配置示例引用了 的退出代码。nextExitStatusspring-doc.cn

在英语中,它说:“如果退出代码为 FAILED,则转到 stepB”。默认情况下,exit code 始终与 for the 相同,这就是为什么前面的条目 工程。但是,如果退出代码需要不同,该怎么办?一个很好的例子来自 Samples 项目中的 Skip Sample 作业:BatchStatusStepspring-doc.cn

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

Java 配置
@Bean
public Job job(JobRepository jobRepository, Step step1, Step step2, Step errorPrint1) {
	return new JobBuilder("job", jobRepository)
			.start(step1).on("FAILED").end()
			.from(step1).on("COMPLETED WITH SKIPS").to(errorPrint1)
			.from(step1).on("*").to(step2)
			.end()
			.build();
}

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

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

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

  • 失败,在这种情况下,作业应失败。Stepspring-doc.cn

  • 成功完成。Stepspring-doc.cn

  • 成功完成,但退出代码为 。在 在这种情况下,应运行不同的步骤来处理错误。StepCOMPLETED WITH SKIPSspring-doc.cn

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

public class SkipCheckingListener implements StepExecutionListener {
    @Override
    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;
        }
    }
}

前面的代码是首先检查以确保 成功,然后检查 的跳过计数是否高于 0. 如果两个条件都满足,则返回退出代码为 new 的 new。StepExecutionListenerStepStepExecutionExitStatusCOMPLETED WITH SKIPSspring-doc.cn

配置 Stop

在讨论了 BatchStatusExitStatus 之后, 人们可能想知道 和 是如何确定 的。 虽然这些状态是由执行的代码确定的,但 的状态是根据配置确定的。BatchStatusExitStatusJobStepJobspring-doc.cn

到目前为止,讨论的所有作业配置都至少有一个 final with 无过渡。Stepspring-doc.cn

在下面的 Java 示例中,执行后, end:stepJobspring-doc.cn

@Bean
public Job job(JobRepository jobRepository, Step step1) {
	return new JobBuilder("job", jobRepository)
				.start(step1)
				.build();
}

在下面的 XML 示例中,执行后, end:stepJobspring-doc.cn

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

如果未为 定义过渡 ,则 的状态定义为 遵循:StepJobspring-doc.cn

  • 如果以 of 结尾,则 和 这两者都是 .StepExitStatusFAILEDBatchStatusExitStatusJobFAILEDspring-doc.cn

  • 否则,的 和 都是 。BatchStatusExitStatusJobCOMPLETEDspring-doc.cn

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

在步骤处结束

配置步骤结束会指示 a 以 的 a 停止。已完成且状态为 的 A 无法重新启动(框架会引发 a ).JobBatchStatusCOMPLETEDJobCOMPLETEDJobInstanceAlreadyCompleteExceptionspring-doc.cn

使用 Java 配置时,该方法用于此任务。方法 还允许使用可选参数,您可以使用该参数自定义 .如果未提供任何值,则默认情况下 为 匹配 .endendexitStatusExitStatusJobexitStatusExitStatusCOMPLETEDBatchStatusspring-doc.cn

使用 XML 配置时,您可以将 元素用于此任务。元素 还允许使用可选属性来自定义 .如果未给出任何属性,则默认情况下 将匹配 .endendexit-codeExitStatusJobexit-codeExitStatusCOMPLETEDBatchStatusspring-doc.cn

请考虑以下场景:如果失败,则使用 a of 和 of 停止,并且不会运行。 否则,执行将移动到 。请注意,如果失败,则 is not 可重启(因为状态为 )。step2JobBatchStatusCOMPLETEDExitStatusCOMPLETEDstep3step3step2JobCOMPLETEDspring-doc.cn

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

@Bean
public Job job(JobRepository jobRepository, Step step1, Step step2, Step step3) {
	return new JobBuilder("job", jobRepository)
				.start(step1)
				.next(step2)
				.on("FAILED").end()
				.from(step2).on("*").to(step3)
				.end()
				.build();
}

以下示例显示了 XML 中的场景:spring-doc.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">

步骤失败

将步骤配置为在给定点失败会指示 a 停止并显示 of 。与 end 不同,a 的失败不会阻止 重新启动。JobBatchStatusFAILEDJobJobspring-doc.cn

使用 XML 配置时,该元素还允许使用可选属性来自定义 .如果未给出任何属性,则默认情况下 将匹配 .failexit-codeExitStatusJobexit-codeExitStatusFAILEDBatchStatusspring-doc.cn

请考虑以下场景:如果失败,则带有 a of 和 of 和 does not 的停止点 执行。否则,执行将移动到 。此外,如果失败并重新启动 ,则执行将在 上再次开始。step2JobBatchStatusFAILEDExitStatusEARLY TERMINATIONstep3step3step2Jobstep2spring-doc.cn

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

Java 配置
@Bean
public Job job(JobRepository jobRepository, Step step1, Step step2, Step step3) {
	return new JobBuilder("job", jobRepository)
			.start(step1)
			.next(step2).on("FAILED").fail()
			.from(step2).on("*").to(step3)
			.end()
			.build();
}

以下示例显示了 XML 中的场景:spring-doc.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">

在给定步骤停止作业

将作业配置为在特定步骤停止会指示 a 停止 of 。停止 可以在处理中提供临时中断, 以便操作员可以在重新启动 .JobBatchStatusSTOPPEDJobJobspring-doc.cn

使用 Java 配置时,该方法需要一个属性 ,该步骤指定在重新启动 Job 时应执行的步骤。stopAndRestartrestartspring-doc.cn

使用 XML 配置时,元素需要一个属性,该属性指定 重新启动 时应执行的步骤。stoprestartJobspring-doc.cn

请考虑以下场景:如果以 结束,则作业 停止。重新启动后,执行将从 开始。step1COMPLETEstep2spring-doc.cn

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

@Bean
public Job job(JobRepository jobRepository, Step step1, Step step2) {
	return new JobBuilder("job", jobRepository)
			.start(step1).on("COMPLETED").stopAndRestart(step2)
			.end()
			.build();
}

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

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

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

编程流决策

在某些情况下,可能需要比 更多的信息来决定 下一步要执行的步骤。在这种情况下,a 可用于协助 在 decision 中,如下例所示:ExitStatusJobExecutionDeciderspring-doc.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);
    }
}

在下面的示例中,将传递一个实现 直接调用:JobExecutionDecidernextspring-doc.cn

Java 配置
@Bean
public Job job(JobRepository jobRepository, MyDecider decider, Step step1, Step step2, Step step3) {
	return new JobBuilder("job", jobRepository)
			.start(step1)
			.next(decider).on("FAILED").to(step2)
			.from(decider).on("COMPLETED").to(step3)
			.end()
			.build();
}

在以下示例作业配置中,a 指定要用作 以及所有过渡:decisionspring-doc.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"/>

拆分流

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

基于 Java 的配置允许您通过提供的生成器配置拆分。由于 以下示例显示,该元素包含一个或多个元素,其中 可以定义整个单独的流。元素还可以包含任何 前面讨论过 transition 元素,例如 attribute 或 、 、 或 元素。splitflowsplitnextnextendfailspring-doc.cn

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

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

@Bean
public Job job(JobRepository jobRepository, Flow flow1, Flow flow2, Step step4) {
	return new JobBuilder("job", jobRepository)
				.start(flow1)
				.split(new SimpleAsyncTaskExecutor())
				.add(flow2)
				.next(step4)
				.end()
				.build();
}

XML 命名空间允许您使用元素。如下例所示, 该元素包含一个或多个元素,其中整个单独的流可以 被定义。元素还可以包含前面讨论的任何过渡 元素,例如属性或 、 、 或 元素。splitsplitflowsplitnextnextendfailspring-doc.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"/>

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

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

下面的 Java 示例演示如何将流声明为对定义的流的引用 别处:spring-doc.cn

Java 配置
@Bean
public Job job(JobRepository jobRepository, Flow flow1, Step step3) {
	return new JobBuilder("job", jobRepository)
				.start(flow1)
				.next(step3)
				.end()
				.build();
}

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

下面的 XML 示例演示如何将流声明为对定义的流的引用 别处:spring-doc.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>

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

外部化流的另一种形式是使用 .A 与 a 类似,但实际上为 指定的流。JobStepJobStepFlowStepspring-doc.cn

以下示例显示了 Java 中的 a 示例:JobStepspring-doc.cn

Java 配置
@Bean
public Job jobStepJob(JobRepository jobRepository, Step jobStepJobStep1) {
	return new JobBuilder("jobStepJob", jobRepository)
				.start(jobStepJobStep1)
				.build();
}

@Bean
public Step jobStepJobStep1(JobRepository jobRepository, JobLauncher jobLauncher, Job job, JobParametersExtractor jobParametersExtractor) {
	return new StepBuilder("jobStepJobStep1", jobRepository)
				.job(job)
				.launcher(jobLauncher)
				.parametersExtractor(jobParametersExtractor)
				.build();
}

@Bean
public Job job(JobRepository jobRepository) {
	return new JobBuilder("job", jobRepository)
				// ...
				.build();
}

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

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

	return extractor;
}

以下示例是 XML 中的 a 示例:JobStepspring-doc.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>

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