配置和运行作业

配置和运行作业

域部分,整体 讨论了架构设计,使用下图作为 指导:spring-doc.cadn.net.cn

图 2.1:批量构造型
图 1.批量构造型

虽然Job对象可能看起来很简单 容器中,您必须了解许多配置选项。 此外,您必须考虑许多选项 如何Job可以运行,以及它的元数据如何 存储在该运行期间。本章介绍各种配置 选项和运行时问题Job.spring-doc.cadn.net.cn

配置作业

有多种实现Job接口。然而 构建者抽象化了配置上的差异。 以下示例创建了一个footballJob:spring-doc.cadn.net.cn

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

一个Job(并且,通常,任何Step在其中)需要一个JobRepository.这 配置JobRepository通过Java Configuration.spring-doc.cadn.net.cn

前面的示例说明了Job由三个组成Step实例。工作相关 构建器还可以包含有助于并行化的其他元素(Split), 声明式流控制 (Decision)和流定义的外部化(Flow).spring-doc.cadn.net.cn

有多种实现Job接口。但是,命名空间抽象化了配置中的差异。它有 只有三个必需的依赖项:名称、JobRepository,以及Step实例。 以下示例创建了一个footballJob:spring-doc.cadn.net.cn

<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 命名空间 默认引用 ID 为jobRepository哪 是一个合理的默认值。但是,您可以显式覆盖它:spring-doc.cadn.net.cn

<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/>).spring-doc.cadn.net.cn

可重启性

执行批处理作业时的一个关键问题涉及Job当它是 重新 启动。启动Job如果JobExecution已经存在,用于特定的JobInstance.理想情况下,所有作业都应该能够启动 从他们离开的地方开始,但在某些情况下这是不可能的。在这种情况下,完全由开发人员来确保新的JobInstance被创建。但是,Spring Batch 确实提供了一些帮助。如果Job永远不应该 restarted 但应始终作为新JobInstance,您可以将 restartable 属性设置为false.spring-doc.cadn.net.cn

以下示例演示如何设置restartable字段设置为false在 XML 中:spring-doc.cadn.net.cn

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

以下示例演示如何设置restartable字段设置为false在 Java 中:spring-doc.cadn.net.cn

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

换句话说,将restartablefalse意思是“这Job不支持重新启动“。重新启动Job那不是 restartable 会导致JobRestartException自 被扔掉。 以下 Junit 代码导致抛出异常:spring-doc.cadn.net.cn

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.spring-doc.cadn.net.cn

拦截作业执行

在执行过程中Job,收到各种通知可能会有用 事件,以便可以运行自定义代码。SimpleJob通过调用JobListener在适当的时候:spring-doc.cadn.net.cn

public interface JobExecutionListener {

    void beforeJob(JobExecution jobExecution);

    void afterJob(JobExecution jobExecution);
}

您可以添加JobListeners设置为SimpleJob通过在作业上设置侦听器。spring-doc.cadn.net.cn

以下示例显示了如何将侦听器元素添加到 XML 作业定义中:spring-doc.cadn.net.cn

XML 配置
<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>

以下示例显示了如何将侦听器方法添加到 Java 作业定义中:spring-doc.cadn.net.cn

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

请注意,afterJob方法是否成功或 失败Job.如果您需要确定成功或失败,您可以获取该信息 从JobExecution:spring-doc.cadn.net.cn

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

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

从父作业继承

如果一组作业共享相似但不 相同的配置,定义“父级”可能会有所帮助Job混凝土从中Job实例可以继承属性。与class相似 Java 中的继承,一个“子”Job结合 其元素和属性与父级的元素和属性。spring-doc.cadn.net.cn

在以下示例中,baseJob是一个摘要Job仅定义列表的定义 听众。这Job (job1)是混凝土 从以下位置继承侦听器列表的定义baseJob和合并 它与自己的侦听器列表一起生成一个Job有两个听众和一个Step (step1).spring-doc.cadn.net.cn

<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>

有关更多详细信息,请参阅从父步骤继承部分。spring-doc.cadn.net.cn

作业参数验证器

在 XML 命名空间中声明的作业或使用AbstractJob可以选择为 运行。例如,当您需要断言作业时,这很有用 以所有必需参数开头。有一个DefaultJobParametersValidator可用于约束组合 简单的强制参数和可选参数。对于更复杂的 constraints,您可以自己实现接口。spring-doc.cadn.net.cn

验证器的配置通过子项的 XML 命名空间支持 元素,如以下示例所示:spring-doc.cadn.net.cn

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

您可以将验证器指定为引用(如前面所示)或嵌套 Bean 定义beansNamespace。spring-doc.cadn.net.cn

验证器的配置通过 Java 构建器支持:spring-doc.cadn.net.cn

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

Java 配置

Spring 3 带来了使用 Java 而不是 XML 配置应用程序的能力。截至 Spring Batch 2.2.0,您可以使用相同的 Java 配置来配置批处理作业。 基于 Java 的配置有三个组件:@EnableBatchProcessing注释和两个构建器。spring-doc.cadn.net.cn

@EnableBatchProcessing注释的工作原理与其他注释类似@Enable*注释中的 Spring家族。在这种情况下,@EnableBatchProcessing提供基本配置 构建批处理作业。在此基本配置中,一个StepScopeJobScope是 创建,此外还有许多可供自动连接的 bean:spring-doc.cadn.net.cn

默认实现提供前面列表中提到的 bean,并且需要DataSourcePlatformTransactionManager在上下文中作为 bean 提供。数据源和事务 管理器由JobRepositoryJobExplorer实例。默认情况下,名为dataSource以及名为transactionManager将被使用。您可以使用以下命令自定义其中任何一个 bean 的属性@EnableBatchProcessing注解。以下示例演示了如何提供 自定义数据源和事务管理器:spring-doc.cadn.net.cn

@Configuration
@EnableBatchProcessing(dataSourceRef = "batchDataSource", transactionManagerRef = "batchTransactionManager")
public class MyJobConfiguration {

	@Bean
	public DataSource batchDataSource() {
		return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.HSQL)
				.addScript("/org/springframework/batch/core/schema-hsqldb.sql")
				.generateUniqueName(true).build();
	}

	@Bean
	public JdbcTransactionManager batchTransactionManager(DataSource dataSource) {
		return new JdbcTransactionManager(dataSource);
	}

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

}
只有一个配置类需要具有@EnableBatchProcessing注解。一次 你有一个用它注释的类,你拥有前面描述的所有配置。

从 v5.0 开始,这是一种配置基础基础设施 Bean 的替代编程方式 通过DefaultBatchConfiguration类。此类提供相同的 bean 提供方@EnableBatchProcessing并可用作基类来配置批处理作业。 以下代码段是如何使用它的典型示例:spring-doc.cadn.net.cn

@Configuration
class MyJobConfiguration extends DefaultBatchConfiguration {

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

}

数据源和事务管理器将从应用程序上下文中解析 并在作业存储库和作业资源管理器上设置。您可以自定义配置 通过覆盖所需的 setter 来覆盖任何基础设施 bean。以下示例 显示如何自定义字符编码,例如:spring-doc.cadn.net.cn

@Configuration
class MyJobConfiguration extends DefaultBatchConfiguration {

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

	@Override
	protected Charset getCharset() {
		return StandardCharsets.ISO_8859_1;
	}
}
@EnableBatchProcessing不应DefaultBatchConfiguration.你应该 要么使用声明式方式配置 Spring Batch@EnableBatchProcessing, 或使用编程方式扩展DefaultBatchConfiguration,但不能同时在 同时。

配置 JobRepository

使用时@EnableBatchProcessing一个JobRepository为您提供。 本节介绍如何配置您自己的。spring-doc.cadn.net.cn

如前所述,JobRepository用于各种持久化的基本 CRUD作 domain 对象,例如JobExecutionStepExecution. 许多主要框架功能都需要它,例如JobLauncher,JobStep.spring-doc.cadn.net.cn

批处理命名空间抽象化了JobRepository实现及其合作者。不过,还是有一些 可用的配置选项,如以下示例所示:spring-doc.cadn.net.cn

XML 配置
<job-repository id="jobRepository"
    data-source="dataSource"
    transaction-manager="transactionManager"
    isolation-level-for-create="SERIALIZABLE"
    table-prefix="BATCH_"
	max-varchar-length="1000"/>

除了id,则不需要前面列出的任何配置选项。如果他们是 未设置,则使用前面显示的默认值。 这max-varchar-length默认为2500,即长的长度VARCHAR示例架构中的列 脚本spring-doc.cadn.net.cn

除了dataSourcetransactionManager,则不需要前面列出的任何配置选项。 如果未设置,则前面显示的默认值 被使用。这 麦克斯varcharlength 默认为2500,即 长的长度VARCHAR示例架构脚本中的列spring-doc.cadn.net.cn

JobRepository 的事务配置

如果命名空间或提供的FactoryBean使用,交易建议是 围绕存储库自动创建。这是为了确保批处理元数据 包括故障后重启所需的状态,将正确保留。 如果存储库方法不是,则框架的行为没有得到很好的定义 事务。中的隔离级别create*method 属性被指定 单独确保在启动作业时,如果两个进程尝试启动 同时完成同样的工作,只有一个成功。该的默认隔离级别 方法是SERIALIZABLE,这是相当激进的。READ_COMMITTED通常同样有效 井。READ_UNCOMMITTED如果两个进程不太可能在此中发生冲突,则可以 道路。但是,由于调用create*方法很短,不太可能SERIALIZED导致问题,只要数据库平台支持即可。但是,您 可以覆盖此设置。spring-doc.cadn.net.cn

以下示例演示如何覆盖 XML 中的隔离级别:spring-doc.cadn.net.cn

XML 配置
<job-repository id="jobRepository"
                isolation-level-for-create="REPEATABLE_READ" />

以下示例显示了如何在 Java 中覆盖隔离级别:spring-doc.cadn.net.cn

Java 配置
@Configuration
@EnableBatchProcessing(isolationLevelForCreate = "ISOLATION_REPEATABLE_READ")
public class MyJobConfiguration {

   // job definition

}

如果未使用命名空间,则还必须配置 使用 AOP 的存储库的事务行为。spring-doc.cadn.net.cn

以下示例演示如何配置存储库的事务行为 在 XML 中:spring-doc.cadn.net.cn

XML 配置
<aop:config>
    <aop:advisor
           pointcut="execution(* org.springframework.batch.core..*Repository+.*(..))"/>
    <advice-ref="txAdvice" />
</aop:config>

<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="*" />
    </tx:attributes>
</tx:advice>

您几乎可以按原样使用前面的片段,几乎不做任何更改。还要记住 包括适当的命名空间声明,并确保spring-txspring-aop(或整个 Spring)都在类路径上。spring-doc.cadn.net.cn

以下示例演示如何配置存储库的事务行为 在 Java 中:spring-doc.cadn.net.cn

Java 配置
@Bean
public TransactionProxyFactoryBean baseProxy() {
	TransactionProxyFactoryBean transactionProxyFactoryBean = new TransactionProxyFactoryBean();
	Properties transactionAttributes = new Properties();
	transactionAttributes.setProperty("*", "PROPAGATION_REQUIRED");
	transactionProxyFactoryBean.setTransactionAttributes(transactionAttributes);
	transactionProxyFactoryBean.setTarget(jobRepository());
	transactionProxyFactoryBean.setTransactionManager(transactionManager());
	return transactionProxyFactoryBean;
}

更改表前缀

另一个可修改的属性JobRepository是元数据的表前缀 表。默认情况下,它们都以BATCH_.BATCH_JOB_EXECUTIONBATCH_STEP_EXECUTION是两个例子。但是,有潜在的原因需要修改它 前缀。如果模式名称需要在表名称前面加上,或者如果有多个 同一架构中需要一组元数据表,表前缀需要 被改变。spring-doc.cadn.net.cn

以下示例显示了如何更改 XML 中的表前缀:spring-doc.cadn.net.cn

XML 配置
<job-repository id="jobRepository"
                table-prefix="SYSTEM.TEST_" />

以下示例显示了如何在 Java 中更改表前缀:spring-doc.cadn.net.cn

Java 配置
@Configuration
@EnableBatchProcessing(tablePrefix = "SYSTEM.TEST_")
public class MyJobConfiguration {

   // job definition

}

鉴于上述更改,对元数据表的每个查询都以SYSTEM.TEST_.BATCH_JOB_EXECUTION简称SYSTEM.TEST_JOB_EXECUTION.spring-doc.cadn.net.cn

只有表前缀是可配置的。表和列名称不是。

存储库中的非标准数据库类型

如果您使用的数据库平台不在受支持的平台列表中,则 如果 SQL 变体足够接近,则可以使用受支持的类型之一。去做 这,您可以使用原始的JobRepositoryFactoryBean而不是命名空间快捷方式和 使用它将数据库类型设置为最接近的匹配项。spring-doc.cadn.net.cn

以下示例演示如何使用JobRepositoryFactoryBean设置数据库类型 到 XML 中最接近的匹配项:spring-doc.cadn.net.cn

XML 配置
<bean id="jobRepository" class="org...JobRepositoryFactoryBean">
    <property name="databaseType" value="db2"/>
    <property name="dataSource" ref="dataSource"/>
</bean>

以下示例演示如何使用JobRepositoryFactoryBean设置数据库类型 到 Java 中最接近的匹配项:spring-doc.cadn.net.cn

Java 配置
@Bean
public JobRepository jobRepository() throws Exception {
    JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
    factory.setDataSource(dataSource);
    factory.setDatabaseType("db2");
    factory.setTransactionManager(transactionManager);
    return factory.getObject();
}

如果未指定数据库类型,则JobRepositoryFactoryBean尝试 自动检测数据库类型DataSource. 平台之间的主要区别是 主要由递增主键的策略来解释,所以 通常需要覆盖incrementerFactory以及(通过使用标准之一 Spring Framework 中的实现)。spring-doc.cadn.net.cn

如果即使这样也不起作用,或者您没有使用 RDBMS,则 唯一的选项可能是实现各种Dao接口,其中SimpleJobRepository取决于 打开并以正常的弹簧方式手动连接一个。spring-doc.cadn.net.cn

配置 JobLauncher

当您使用@EnableBatchProcessing一个JobRegistry为您提供。 本节介绍如何配置您自己的。spring-doc.cadn.net.cn

最基本的实现JobLauncher接口是TaskExecutorJobLauncher. 它唯一必需的依赖项是JobRepository(需要获得执行)。spring-doc.cadn.net.cn

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

XML 配置
<bean id="jobLauncher"
      class="org.springframework.batch.core.launch.support.TaskExecutorJobLauncher">
    <property name="jobRepository" ref="jobRepository" />
</bean>

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

Java 配置
...
@Bean
public JobLauncher jobLauncher() throws Exception {
	TaskExecutorJobLauncher jobLauncher = new TaskExecutorJobLauncher();
	jobLauncher.setJobRepository(jobRepository);
	jobLauncher.afterPropertiesSet();
	return jobLauncher;
}
...

一旦获得 JobExecution,它就会传递给 execute 方法的Job,最终返回JobExecution对调用方,作为 下图显示:spring-doc.cadn.net.cn

作业Starters序列
图 2.作业Starters序列

该序列很简单,从调度程序启动时效果很好。然而 尝试从 HTTP 请求启动时会出现问题。在此方案中,启动 需要异步完成,以便TaskExecutorJobLauncher立即返回到其 访客。这是因为保持 HTTP 请求打开对于 长时间运行的进程(例如批处理作业)所需的时间量。下图显示 示例序列:spring-doc.cadn.net.cn

异步作业Starters序列
图 3.异步作业Starters序列

您可以配置TaskExecutorJobLauncher要通过配置TaskExecutor.spring-doc.cadn.net.cn

以下 XML 示例配置了TaskExecutorJobLauncher立即返回:spring-doc.cadn.net.cn

XML 配置
<bean id="jobLauncher"
      class="org.springframework.batch.core.launch.support.TaskExecutorJobLauncher">
    <property name="jobRepository" ref="jobRepository" />
    <property name="taskExecutor">
        <bean class="org.springframework.core.task.SimpleAsyncTaskExecutor" />
    </property>
</bean>

以下 Java 示例配置了TaskExecutorJobLauncher立即返回:spring-doc.cadn.net.cn

Java 配置
@Bean
public JobLauncher jobLauncher() {
	TaskExecutorJobLauncher jobLauncher = new TaskExecutorJobLauncher();
	jobLauncher.setJobRepository(jobRepository());
	jobLauncher.setTaskExecutor(new SimpleAsyncTaskExecutor());
	jobLauncher.afterPropertiesSet();
	return jobLauncher;
}

您可以使用弹簧的任何实现TaskExecutor接口来控制作业的异步方式 执行。spring-doc.cadn.net.cn

运行作业

启动批处理作业至少需要两件事:Job要启动,并JobLauncher.两者都可以包含在同一个 上下文或不同的上下文。例如,如果您从 命令行,则为每个Job.因此,每个 工作有自己的JobLauncher.但是,如果 您从一个 Web 容器中运行,该容器位于HttpRequest,通常有一个JobLauncher(配置为异步作业 launching),多个请求调用以启动其作业。spring-doc.cadn.net.cn

从命令行运行作业

如果要从企业运行作业 调度程序,命令行是主接口。这是因为 大多数调度器(Quartz 除外,除非使用NativeJob)直接与作系统配合使用 进程,主要从 shell 脚本开始。有很多种方法。 启动 shell 脚本之外的 Java 进程,例如 Perl、Ruby 或 甚至构建工具,例如 Ant 或 Maven。然而,由于大多数人 熟悉 shell 脚本,本示例重点介绍它们。spring-doc.cadn.net.cn

The CommandLineJobRunner

因为启动作业的脚本必须启动一个 Java Virtual Machine,需要有一个类,其中main行动方法 作为主要入口点。Spring Batch 提供了一个实现 用于此目的:CommandLineJobRunner.注意 这只是引导应用程序的一种方法。有 启动 Java 进程的多种方式,而此类绝不应该是 被视为确定的。这CommandLineJobRunner执行四项任务:spring-doc.cadn.net.cn

所有这些任务都只需传入参数即可完成。 下表描述了所需的参数:spring-doc.cadn.net.cn

表 1.CommandLineJobRunner 参数

jobPathspring-doc.cadn.net.cn

用于 创建一个ApplicationContext.此文件 应包含运行完整Job.spring-doc.cadn.net.cn

jobNamespring-doc.cadn.net.cn

要运行的作业的名称。spring-doc.cadn.net.cn

必须传入这些参数,路径在前,名称在后。所有参数 这些被认为是作业参数后,将转换为JobParameters对象 并且必须采用name=value.spring-doc.cadn.net.cn

以下示例显示了作为作业参数传递给 XML 中定义的作业的日期:spring-doc.cadn.net.cn

<bash$ java CommandLineJobRunner endOfDayJob.xml endOfDay schedule.date=2007-05-05,java.time.LocalDate

以下示例显示了作为作业参数传递给Java中定义的作业的日期:spring-doc.cadn.net.cn

<bash$ java CommandLineJobRunner io.spring.EndOfDayJobConfiguration endOfDay schedule.date=2007-05-05,java.time.LocalDate

默认情况下,CommandLineJobRunner使用DefaultJobParametersConverter隐式转换 键/值对来标识作业参数。但是,您可以显式指定 哪些作业参数是标识的,哪些不是通过后缀它们来识别的truefalse分别。spring-doc.cadn.net.cn

在以下示例中,schedule.date是标识作业参数,而vendor.id莫:spring-doc.cadn.net.cn

<bash$ java CommandLineJobRunner endOfDayJob.xml endOfDay \
                                 schedule.date=2007-05-05,java.time.LocalDate,true \
                                 vendor.id=123,java.lang.Long,false
<bash$ java CommandLineJobRunner io.spring.EndOfDayJobConfiguration endOfDay \
                                 schedule.date=2007-05-05,java.time.LocalDate,true \
                                 vendor.id=123,java.lang.Long,false

您可以使用自定义JobParametersConverter.spring-doc.cadn.net.cn

在大多数情况下,您希望使用清单来声明您的main类在罐子里。然而 为简单起见,直接使用该类。此示例使用EndOfDayBatch 的领域语言中的示例。第一个 参数是endOfDayJob.xml,这是包含Job.第二个参数endOfDay,表示作业名称。最后一个论点schedule.date=2007-05-05,java.time.LocalDate,被转换为JobParameter类型的对象java.time.LocalDate.spring-doc.cadn.net.cn

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

<job id="endOfDay">
    <step id="step1" parent="simpleStep" />
</job>

<!-- Launcher details removed for clarity -->
<beans:bean id="jobLauncher"
         class="org.springframework.batch.core.launch.support.TaskExecutorJobLauncher" />

在大多数情况下,您希望使用清单来声明您的main类在罐子里。然而 为简单起见,直接使用该类。此示例使用EndOfDayBatch 的领域语言中的示例。第一个 参数是io.spring.EndOfDayJobConfiguration,这是完全限定的类名 到包含作业的配置类。第二个参数endOfDay代表 作业名称。最后一个论点schedule.date=2007-05-05,java.time.LocalDate,被转换 变成一个JobParameter类型的对象java.time.LocalDate.spring-doc.cadn.net.cn

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

@Configuration
@EnableBatchProcessing
public class EndOfDayJobConfiguration {

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

    @Bean
    public Step step1(JobRepository jobRepository, PlatformTransactionManager transactionManager) {
        return new StepBuilder("step1", jobRepository)
    				.tasklet((contribution, chunkContext) -> null, transactionManager)
    				.build();
    }
}

前面的示例过于简单化,因为对 一般情况下,在 Spring Batch 中运行批处理作业,但它用于显示两个主要的 的要求CommandLineJobRunner:JobJobLauncher.spring-doc.cadn.net.cn

退出代码

从命令行启动批处理作业时,企业 经常使用调度程序。大多数调度程序都相当愚蠢,只能工作 在过程层面。这意味着他们只知道一些 作系统进程(例如它们调用的 shell 脚本)。 在这种情况下,与调度程序通信的唯一方法 关于作业的成功或失败是通过返回代码。一个 返回代码是进程返回给调度程序的数字 以指示运行结果。在最简单的情况下,0 是 成功,1 是失败。但是,可能还有更复杂的 方案,例如“如果作业 A 返回 4,则启动作业 B,如果返回 5,则启动 下班 C。这种类型的行为是在调度程序级别配置的, 但重要的是,像 Spring Batch 这样的处理框架 提供一种返回退出代码的数字表示形式的方法 对于特定的批处理作业。在 Spring Batch 中,这是封装的 在ExitStatus,其中涵盖了更多 详情见第 5 章。为了讨论退出代码,该 唯一需要知道的重要事情是ExitStatus具有 Exit Code 属性,该属性为 由框架(或开发人员)设置,并作为JobExecutionJobLauncher.这CommandLineJobRunner转换此字符串值 通过使用ExitCodeMapper接口:spring-doc.cadn.net.cn

public interface ExitCodeMapper {

    public int intValue(String exitCode);

}

基本契约ExitCodeMapper是,给定一个字符串出口 code,将返回一个数字表示。默认值 作业运行器使用的实现是SimpleJvmExitCodeMapper返回 0 表示完成,1 表示通用错误,2 表示任何作业 运行器错误,例如无法找到Job在提供的上下文中。如果还有的话 复杂度高于上述三个值,需要自定义 实现ExitCodeMapper接口 必须提供。因为CommandLineJobRunner是创建 一ApplicationContext因此,不可能 'wired together',任何需要覆盖的值都必须是 自动接线。这意味着,如果实现ExitCodeMapperBeanFactory, 在创建上下文后,它将注入到运行器中。都 需要这样做才能提供您自己的ExitCodeMapper是声明实现 作为根级 Bean,并确保它是ApplicationContext由 跑步者。spring-doc.cadn.net.cn

从 Web 容器内运行作业

从历史上看,脱机处理(例如批处理作业)一直是 如前所述,从命令行启动。但是,有 在许多情况下,从HttpRequest是 更好的选择。许多此类用例包括报告、临时作业 运行和 Web 应用程序支持。因为批处理作业(根据定义) 长期运行,最关心的是推出 异步作业:spring-doc.cadn.net.cn

来自 Web 容器的异步作业Starters序列
图 4.来自 Web 容器的异步作业Starters序列

本例中的控制器是 Spring MVC 控制器。请参阅 Spring Framework 参考指南,了解有关 Spring MVC 的更多信息。 控制器启动一个Job通过使用JobLauncher已配置为异步启动,该 立即返回一个JobExecution.这Job可能仍在运行。然而,这个 非阻塞行为允许控制器立即返回,这 在处理HttpRequest.以下列表 显示了一个示例:spring-doc.cadn.net.cn

@Controller
public class JobLauncherController {

    @Autowired
    JobLauncher jobLauncher;

    @Autowired
    Job job;

    @RequestMapping("/jobLauncher.html")
    public void handle() throws Exception{
        jobLauncher.run(job, new JobParameters());
    }
}

高级元数据使用

到目前为止,无论是JobLauncherJobRepository接口已被 讨论。它们共同代表了作业的简单启动和基本 批处理域对象的 CRUD作:spring-doc.cadn.net.cn

作业存储库
图 5.作业存储库

一个JobLauncher使用JobRepository创建新的JobExecution对象并运行它们。JobStep实现 以后使用相同的JobRepository对于基本更新 在运行期间的相同执行Job. 对于简单的场景,基本作就足够了。但是,在大批量 具有数百个批处理作业和复杂调度的环境 要求,需要对元数据进行更高级的访问:spring-doc.cadn.net.cn

作业存储库高级
图6.高级作业存储库访问

JobExplorerJobOperator接口,其中进行了讨论 在接下来的部分中,添加用于查询和控制元数据的其他功能。spring-doc.cadn.net.cn

查询存储库

在任何高级功能之前,最基本的需求是能够 查询存储库中的现有执行。此功能是 由JobExplorer接口:spring-doc.cadn.net.cn

public interface JobExplorer {

    List<JobInstance> getJobInstances(String jobName, int start, int count);

    JobExecution getJobExecution(Long executionId);

    StepExecution getStepExecution(Long jobExecutionId, Long stepExecutionId);

    JobInstance getJobInstance(Long instanceId);

    List<JobExecution> getJobExecutions(JobInstance jobInstance);

    Set<JobExecution> findRunningJobExecutions(String jobName);
}

从其方法签名中可以明显看出,JobExplorer是 这JobRepository,并且,就像JobRepository,可以使用 工厂豆。spring-doc.cadn.net.cn

以下示例演示如何配置JobExplorer在 XML 中:spring-doc.cadn.net.cn

XML 配置
<bean id="jobExplorer" class="org.spr...JobExplorerFactoryBean"
      p:dataSource-ref="dataSource" />

以下示例演示如何配置JobExplorer在 Java 中:spring-doc.cadn.net.cn

Java 配置
...
// This would reside in your DefaultBatchConfiguration extension
@Bean
public JobExplorer jobExplorer() throws Exception {
	JobExplorerFactoryBean factoryBean = new JobExplorerFactoryBean();
	factoryBean.setDataSource(this.dataSource);
	return factoryBean.getObject();
}
...

在本章前面,我们注意到您可以修改表前缀 的JobRepository以允许不同的版本或模式。因为 这JobExplorer适用于相同的表,它还需要能够设置前缀。spring-doc.cadn.net.cn

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

XML 配置
<bean id="jobExplorer" class="org.spr...JobExplorerFactoryBean"
		p:tablePrefix="SYSTEM."/>

以下示例演示如何为JobExplorer在 Java 中:spring-doc.cadn.net.cn

Java 配置
...
// This would reside in your DefaultBatchConfiguration extension
@Bean
public JobExplorer jobExplorer() throws Exception {
	JobExplorerFactoryBean factoryBean = new JobExplorerFactoryBean();
	factoryBean.setDataSource(this.dataSource);
	factoryBean.setTablePrefix("SYSTEM.");
	return factoryBean.getObject();
}
...

作业注册表

一个JobRegistry(及其父接口JobLocator) 不是强制性的,但可以是如果您想跟踪上下文中可用的作业,则很有用。它也是对于在创建作业时在应用程序上下文中集中收集作业很有用在其他地方(例如,在子上下文中)。您还可以使用 customJobRegistry实现 来作已注册作业的名称和其他属性。该框架仅提供一种实现,该实现基于简单的从作业名称映射到作业实例。spring-doc.cadn.net.cn

以下示例演示如何包含JobRegistry对于用 XML 定义的作业:spring-doc.cadn.net.cn

<bean id="jobRegistry" class="org.springframework.batch.core.configuration.support.MapJobRegistry" />

使用时@EnableBatchProcessing一个JobRegistry为您提供。以下示例显示如何配置您自己的JobRegistry:spring-doc.cadn.net.cn

...
// This is already provided via the @EnableBatchProcessing but can be customized via
// overriding the bean in the DefaultBatchConfiguration
@Override
@Bean
public JobRegistry jobRegistry() throws Exception {
	return new MapJobRegistry();
}
...

您可以填充JobRegistry以两种方式之一:使用bean 后处理器或使用 registrar 生命周期组件。即将推出的部分描述了这两种机制。spring-doc.cadn.net.cn

JobRegistryBean后处理器

这是一个 bean 后处理器,可以在创建作业时注册所有作业。spring-doc.cadn.net.cn

以下示例演示了如何包含JobRegistryBeanPostProcessor对于作业在 XML 中定义:spring-doc.cadn.net.cn

XML 配置
<bean id="jobRegistryBeanPostProcessor" class="org.spr...JobRegistryBeanPostProcessor">
    <property name="jobRegistry" ref="jobRegistry"/>
</bean>

以下示例演示了如何包含JobRegistryBeanPostProcessor对于作业在 Java 中定义:spring-doc.cadn.net.cn

Java 配置
@Bean
public JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor(JobRegistry jobRegistry) {
    JobRegistryBeanPostProcessor postProcessor = new JobRegistryBeanPostProcessor();
    postProcessor.setJobRegistry(jobRegistry);
    return postProcessor;
}

虽然这不是绝对必要的,但示例中的后处理器已被赋予一个id以便它可以包含在子上下文中(例如,作为父 Bean 定义)并导致所有创建的作业也会自动注册。spring-doc.cadn.net.cn

自动作业注册商

这是一个生命周期组件,用于创建子上下文并从这些上下文注册作业。这样做的一个好处是,虽然子上下文在注册表中仍然必须是全局唯一的,但它们的依赖项可以具有“自然”名称。因此,例如,您可以创建一组 XML 配置文件每个配置文件只有一个作业,但都有不同的定义ItemReader使用 相同的 bean 名称,例如reader.如果所有这些文件都导入到同一个上下文中, 读者定义会相互冲突并覆盖彼此,但是,使用 registrar 的 registrar 中,避免了这一点。这使得集成贡献的作业变得更加容易 应用程序的单独模块。spring-doc.cadn.net.cn

以下示例演示了如何包含AutomaticJobRegistrar对于已定义的作业 在 XML 中:spring-doc.cadn.net.cn

XML 配置
<bean class="org.spr...AutomaticJobRegistrar">
   <property name="applicationContextFactories">
      <bean class="org.spr...ClasspathXmlApplicationContextsFactoryBean">
         <property name="resources" value="classpath*:/config/job*.xml" />
      </bean>
   </property>
   <property name="jobLoader">
      <bean class="org.spr...DefaultJobLoader">
         <property name="jobRegistry" ref="jobRegistry" />
      </bean>
   </property>
</bean>

以下示例演示了如何包含AutomaticJobRegistrar对于已定义的作业 在 Java 中:spring-doc.cadn.net.cn

Java 配置
@Bean
public AutomaticJobRegistrar registrar() {

    AutomaticJobRegistrar registrar = new AutomaticJobRegistrar();
    registrar.setJobLoader(jobLoader());
    registrar.setApplicationContextFactories(applicationContextFactories());
    registrar.afterPropertiesSet();
    return registrar;

}

注册商有两个必需属性:数组ApplicationContextFactory(从 方便的工厂 bean)和JobLoader.这JobLoader负责管理子上下文的生命周期,并且 在JobRegistry.spring-doc.cadn.net.cn

ApplicationContextFactory是 负责创建子上下文。最常见的用法 is(如前面的示例所示)使用ClassPathXmlApplicationContextFactory.其中之一 这个工厂的特点是,默认情况下,它复制了一些 配置从父上下文向下到子上下文。所以,对于 实例,则无需重新定义PropertyPlaceholderConfigurer或 AOP 配置,前提是它应该与 父母。spring-doc.cadn.net.cn

您可以使用AutomaticJobRegistrar在 与JobRegistryBeanPostProcessor(只要你还使用DefaultJobLoader). 例如,如果有工作,这可能是可取的 在主父上下文和子上下文中定义 地点。spring-doc.cadn.net.cn

作业运算符

如前所述,该JobRepository提供对元数据的 CRUD作,并且JobExplorer在 元数据。但是,这些作一起使用时最有用 执行常见的监视任务,例如停止、重新启动或 总结作业,就像批处理运算符通常所做的那样。弹簧批次 在JobOperator接口:spring-doc.cadn.net.cn

public interface JobOperator {

    List<Long> getExecutions(long instanceId) throws NoSuchJobInstanceException;

    List<Long> getJobInstances(String jobName, int start, int count)
          throws NoSuchJobException;

    Set<Long> getRunningExecutions(String jobName) throws NoSuchJobException;

    String getParameters(long executionId) throws NoSuchJobExecutionException;

    Long start(String jobName, String parameters)
          throws NoSuchJobException, JobInstanceAlreadyExistsException;

    Long restart(long executionId)
          throws JobInstanceAlreadyCompleteException, NoSuchJobExecutionException,
                  NoSuchJobException, JobRestartException;

    Long startNextInstance(String jobName)
          throws NoSuchJobException, JobParametersNotFoundException, JobRestartException,
                 JobExecutionAlreadyRunningException, JobInstanceAlreadyCompleteException;

    boolean stop(long executionId)
          throws NoSuchJobExecutionException, JobExecutionNotRunningException;

    String getSummary(long executionId) throws NoSuchJobExecutionException;

    Map<Long, String> getStepExecutionSummaries(long executionId)
          throws NoSuchJobExecutionException;

    Set<String> getJobNames();

}

上述作表示来自许多不同接口的方法,例如JobLauncher,JobRepository,JobExplorerJobRegistry.因此, 提供了JobOperator (SimpleJobOperator)有许多依赖项。spring-doc.cadn.net.cn

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

<bean id="jobOperator" class="org.spr...SimpleJobOperator">
    <property name="jobExplorer">
        <bean class="org.spr...JobExplorerFactoryBean">
            <property name="dataSource" ref="dataSource" />
        </bean>
    </property>
    <property name="jobRepository" ref="jobRepository" />
    <property name="jobRegistry" ref="jobRegistry" />
    <property name="jobLauncher" ref="jobLauncher" />
</bean>

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

 /**
  * All injected dependencies for this bean are provided by the @EnableBatchProcessing
  * infrastructure out of the box.
  */
 @Bean
 public SimpleJobOperator jobOperator(JobExplorer jobExplorer,
                                JobRepository jobRepository,
                                JobRegistry jobRegistry,
                                JobLauncher jobLauncher) {

	SimpleJobOperator jobOperator = new SimpleJobOperator();
	jobOperator.setJobExplorer(jobExplorer);
	jobOperator.setJobRepository(jobRepository);
	jobOperator.setJobRegistry(jobRegistry);
	jobOperator.setJobLauncher(jobLauncher);

	return jobOperator;
 }

从 5.0 版本开始,@EnableBatchProcessing注释自动注册作业运算符 Bean 在应用程序上下文中。spring-doc.cadn.net.cn

如果您在作业存储库上设置表前缀,请不要忘记在作业资源管理器上也设置它。

JobParameters递增器

大多数方法JobOperator是 不言自明,您可以在接口的 Javadoc 中找到更详细的解释。但是,startNextInstance方法值得注意。这 方法总是启动一个新实例Job. 如果存在严重问题,这将非常有用JobExecutionJob需要从头开始。与JobLauncher(这需要一个新的JobParameters触发新JobInstance),如果参数与 任何先前的参数集,则startNextInstance方法使用JobParametersIncrementer绑定到Job强制Job设置为 新实例:spring-doc.cadn.net.cn

public interface JobParametersIncrementer {

    JobParameters getNext(JobParameters parameters);

}

合同JobParametersIncrementer是 给定一个 JobParameters 对象,它返回“next”JobParameters对象,通过递增它可能包含的任何必要值。这 策略很有用,因为框架无法知道什么 更改JobParameters让它成为“下一个” 实例。例如,如果JobParameters是一个日期和下一个实例 应该创建,该值应该增加一天还是一天 周(例如,如果作业是每周)?对于任何 有助于识别Job, 如以下示例所示:spring-doc.cadn.net.cn

public class SampleIncrementer implements JobParametersIncrementer {

    public JobParameters getNext(JobParameters parameters) {
        if (parameters==null || parameters.isEmpty()) {
            return new JobParametersBuilder().addLong("run.id", 1L).toJobParameters();
        }
        long id = parameters.getLong("run.id",1L) + 1;
        return new JobParametersBuilder().addLong("run.id", id).toJobParameters();
    }
}

在此示例中,键为run.id用于 区分JobInstances. 如果JobParameters传入为 null,则可以是假设Job以前从未运行过因此,可以返回其初始状态。但是,如果没有,则获取旧的值,递增 1,然后返回。spring-doc.cadn.net.cn

对于在 XML 中定义的作业,您可以将增量器与Job通过incrementer属性,如下所示:spring-doc.cadn.net.cn

<job id="footballJob" incrementer="sampleIncrementer">
    ...
</job>

对于用 Java 定义的作业,您可以将增量器与Job通过incrementer方法,如下所示:spring-doc.cadn.net.cn

@Bean
public Job footballJob(JobRepository jobRepository) {
    return new JobBuilder("footballJob", jobRepository)
    				 .incrementer(sampleIncrementer())
    				 ...
                     .build();
}

停止作业

最常见的用例之一JobOperator正在优雅地停止 工作:spring-doc.cadn.net.cn

Set<Long> executions = jobOperator.getRunningExecutions("sampleJob");
jobOperator.stop(executions.iterator().next());

关闭不是立即的,因为没有办法强制 立即关闭,特别是如果执行当前处于 框架无法控制的开发人员代码,例如 商业服务。但是,一旦控制权返回给 框架,它设置当前StepExecutionBatchStatus.STOPPED,保存它,并执行相同的作 对于JobExecution在完成之前。spring-doc.cadn.net.cn

中止工作

作业执行FAILED可以 restarted (如果Job是可重启的)。状态为ABANDONED框架无法重新启动。 这ABANDONEDstatus 也用于步骤 execution 以在重新启动的作业执行中将其标记为可跳过。如果 作业正在运行,遇到已标记的步骤ABANDONED在上一个失败的作业执行中,它 继续执行下一步(由作业流定义确定) 以及步骤执行退出状态)。spring-doc.cadn.net.cn

如果进程终止 (kill -9或服务器 failure),作业当然不是正在运行,而是JobRepository有 无从知道,因为在过程结束之前没有人告诉它。你 必须手动告诉它你知道执行失败了 或应被视为已中止(将其状态更改为FAILEDABANDONED).这是 一个业务决策,并且没有办法将其自动化。将 status 设置为FAILED仅当它是可重启的,并且你知道重启数据有效时。spring-doc.cadn.net.cn