Configuring a Job

Job 接口有多个实现。然而,这些实现通过提供的构建器(用于 Java 配置)或 XML 命名空间(用于基于 XML 的配置)抽象出来。以下示例显示了 Java 和 XML 配置:

Java
@Bean
public Job footballJob(JobRepository jobRepository) {
    return new JobBuilder("footballJob", jobRepository)
                     .start(playerLoad())
                     .next(gameLoad())
                     .next(playerSummarization())
                     .build();
}

Job(通常还有其中的任何 Step)需要 JobRepositoryJobRepository 的配置通过 Java Configuration 处理。 前面的示例说明了一个由三个 Step 实例组成的 Job。与作业相关的构建器还可以包含帮助并行化(Split)、声明式流控制(Decision)和流定义外化(Flow)的其他元素。

XML

Job 接口有多个实现。然而,命名空间抽象了配置中的差异。它只有三个必需的依赖项:name、JobRepositoryStep 实例的列表。以下示例创建一个 footballJob

<job id="footballJob">
    <step id="playerload"          parent="s1" next="gameLoad"/>
    <step id="gameLoad"            parent="s2" next="playerSummarization"/>
    <step id="playerSummarization" parent="s3"/>
</job>

前面的示例使用父 bean 定义来创建步骤。有关在行内声明特定步骤详细信息的更多选项,请参阅有关步骤配置的部分。XML 命名空间默认为引用带有 idjobRepository 的存储库,这是一个明智的默认设置。但是,你可以明确覆盖此默认设置:

<job id="footballJob" job-repository="specialRepository">
    <step id="playerload"          parent="s1" next="gameLoad"/>
    <step id="gameLoad"            parent="s3" next="playerSummarization"/>
    <step id="playerSummarization" parent="s3"/>
</job>

除步骤外,作业配置还可能包含有助于并行化(<split>)、声明式流控制(<decision>)和流定义外部化(<flow/>)的其他元素。

Restartability

执行批处理作业时一个关键问题在于,当一个“任务”被重新启动时其行为。如果一个 Job 执行已经存在于特定的 Job 实例中,那么启动一个“任务”被认为是一个“重新启动”。理想情况下,所有任务都应该能够从上次中断的地方开始,但是有些情况下这是不可能的。在这个场景中,确保创建一个新的 Job 实例完全取决于开发人员。然而,Spring Batch 确实提供了一些帮助。如果一个“任务”永远不应该被重新启动,而应该始终作为新 Job 实例的一部分运行,你可以将 restartable 属性设置为 false。

Java

下面的例子展示了如何在 Java 中将 restartable 字段设置为 false

Java Configuration
@Bean
public Job footballJob(JobRepository jobRepository) {
    return new JobBuilder("footballJob", jobRepository)
                     .preventRestart()
                     ...
                     .build();
}
XML

下面的例子展示了如何在 XML 中将 restartable 字段设置为 false

XML Configuration
<job id="footballJob" restartable="false">
    ...
</job>

用另一种方式表述,将 restartable 设置为 false 表示“这个任务不支持再次启动”。重新启动一个不可重新启动的“任务”会导致抛出 JobRestartException。下面的 JUnit 代码会导致抛出异常:

Job job = new SimpleJob();
job.setRestartable(false);

JobParameters jobParameters = new JobParameters();

JobExecution firstExecution = jobRepository.createJobExecution(job, jobParameters);
jobRepository.saveOrUpdate(firstExecution);

try {
    jobRepository.createJobExecution(job, jobParameters);
    fail();
}
catch (JobRestartException e) {
    // expected
}

第一次尝试为不可重新启动的任务创建一个 JobExecution 不会造成问题。但是,第二次尝试会抛出 JobRestartException。

Intercepting Job Execution

在执行一个“任务”的过程中,可能会需要获得其生命周期中各个事件的通知,以便运行自定义代码。SimpleJob 通过在适当的时间调用一个 JobListener 来允许这样做:

public interface JobExecutionListener {

    void beforeJob(JobExecution jobExecution);

    void afterJob(JobExecution jobExecution);
}

可以通过在作业中设置侦听器来向 SimpleJob 添加 JobListeners。

Java

下面的例子展示了如何在 Java 作业定义中添加一个侦听器方法:

Java Configuration
@Bean
public Job footballJob(JobRepository jobRepository) {
    return new JobBuilder("footballJob", jobRepository)
                     .listener(sampleListener())
                     ...
                     .build();
}
XML

下面的例子展示了如何在 XML 作业定义中添加一个侦听器元素:

XML Configuration
<job id="footballJob">
    <step id="playerload"          parent="s1" next="gameLoad"/>
    <step id="gameLoad"            parent="s2" next="playerSummarization"/>
    <step id="playerSummarization" parent="s3"/>
    <listeners>
        <listener ref="sampleListener"/>
    </listeners>
</job>

请注意,无论“任务”是否成功或失败,都会调用 afterJob 方法。如果你需要确定成功或失败,你可以从 JobExecution 中获取该信息:

public void afterJob(JobExecution jobExecution){
    if (jobExecution.getStatus() == BatchStatus.COMPLETED ) {
        //job success
    }
    else if (jobExecution.getStatus() == BatchStatus.FAILED) {
        //job failure
    }
}

与该接口对应的注解是:

  • @BeforeJob

  • @AfterJob

Inheriting from a Parent Job

如果一组任务共享相似但不完全相同的配置,则定义一个“父级”任务可能会有所帮助,具体任务实例可以从中继承属性。类似于 Java 中的类继承,“子级”任务结合了其元素和属性与父级的元素和属性。

在下面的例子中,baseJob 是一个抽象“任务”定义,仅定义了一个侦听器列表。这个“任务” (job1) 是一个具体定义,从 baseJob 继承了侦听器列表,并将其与它自己的侦听器列表合并以创建一个具有两个侦听器和一个步骤 (step1) 的“任务”。

<job id="baseJob" abstract="true">
    <listeners>
        <listener ref="listenerOne"/>
    <listeners>
</job>

<job id="job1" parent="baseJob">
    <step id="step1" parent="standaloneStep"/>

    <listeners merge="true">
        <listener ref="listenerTwo"/>
    <listeners>
</job>

请参阅有关 [继承父步骤,inheritingFromParentStep] 的章节以获取更详细的信息。

JobParametersValidator

在 XML 名称空间中声明或者使用 AbstractJob 的任何子类的任务可以选择为运行时的任务参数声明一个验证器。举例来说,当需要断言一个任务已经使用其所有必需的参数启动时,这很有用。有一个 DefaultJobParametersValidator,你可以用它来限制简单必需参数和可选参数的组合。对于更复杂的限制,你可以自己实现该接口。

Java

可以通过 Java 构建器来支持验证器的配置:

@Bean
public Job job1(JobRepository jobRepository) {
    return new JobBuilder("job1", jobRepository)
                     .validator(parametersValidator())
                     ...
                     .build();
}
XML

可以通过 XML 名称空间通过任务的子元素来支持验证器的配置,如下面的例子所示:

<job id="job1" parent="baseJob3">
    <step id="step1" parent="standaloneStep"/>
    <validator ref="parametersValidator"/>
</job>

你可以将验证器指定为一个引用(如前面所示)或在 beans 名称空间中指定为一个嵌套 bean 定义。