配置和运行作业

配置和运行作业

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

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

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

配置 Job

Job 接口有多种实现。然而 构建器抽象出配置中的差异。 以下示例创建一个 :footballJobspring-doc.cn

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

A(通常还包括其中的任何一个)需要一个 .这 的配置通过 Java 配置进行处理。JobStepJobRepositoryJobRepositoryspring-doc.cn

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

Job 接口有多种实现。但是,命名空间抽象出了配置中的差异。它有 只有三个必需的依赖项:名称、 和实例列表。 以下示例创建一个 :JobRepositoryStepfootballJobspring-doc.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 为 的存储库,该存储库 是合理的默认值。但是,您可以显式覆盖它:jobRepositoryspring-doc.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.cn

可重启性

执行批处理作业时的一个关键问题涉及 重新 启动。如果特定 已存在 a,则启动 a 被视为“重新启动”。理想情况下,所有作业都应该能够启动 从他们离开的地方开始,但在某些情况下这是不可能的。在这种情况下,完全由开发人员来确保创建新的 JobInstance但是,Spring Batch 确实提供了一些帮助。如果 a 不应该是 重新启动,但应始终作为 new 的一部分运行,您可以设置 restartable 属性设置为 。JobJobJobExecutionJobInstanceJobJobInstancefalsespring-doc.cn

以下示例演示如何在 XML 中将字段设置为 :restartablefalsespring-doc.cn

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

以下示例演示如何在 Java 中将字段设置为 :restartablefalsespring-doc.cn

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

换句话说,设置为 to 意味着 “this does not support being started again”。重新启动 不是 restartable 会导致 被扔掉。 以下 Junit 代码会导致引发异常:restartablefalseJobJobJobRestartExceptionspring-doc.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
}

第一次尝试为 不可重启 job 不会引起任何问题。然而,第二个 attempt 会引发 .JobExecutionJobRestartExceptionspring-doc.cn

拦截任务执行

在执行 的过程中,通知各种 可能会很有用 事件,以便可以运行自定义代码。 通过在适当的时间调用 A 来允许此操作:JobSimpleJobJobListenerspring-doc.cn

public interface JobExecutionListener {

    void beforeJob(JobExecution jobExecution);

    void afterJob(JobExecution jobExecution);
}

您可以通过在作业上设置侦听器来添加到 。JobListenersSimpleJobspring-doc.cn

以下示例演示如何将侦听器元素添加到 XML 作业定义中:spring-doc.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.cn

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

请注意,无论成功还是 失败的 .如果需要确定成功或失败,可以获取该信息 从 :afterJobJobJobExecutionspring-doc.cn

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

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

从父作业继承

如果一组 Job 具有相似但不相似的 相同的配置,定义一个 “parent” 可能会有所帮助,具体实例可以从中继承属性。与 class 相似 继承,则“子”结合了 它的元素和属性与父级的 Elements 和 Attributes 一起使用。JobJobJobspring-doc.cn

在下面的示例中,是一个抽象定义,它只定义了 听众。() 是具体的 继承侦听器列表并合并的定义 it 替换为自己的侦听器列表,以生成具有两个侦听器和一个 () 的 a。baseJobJobJobjob1baseJobJobStepstep1spring-doc.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.cn

JobParametersValidator (作业参数验证器)

在 XML 命名空间中声明的作业或使用 的任何子类可以选择为 运行。例如,当您需要断言作业 启动其所有必需参数。有一个 可用于约束组合 简单的强制和可选参数。对于更复杂的 constraints,您可以自行实现接口。AbstractJobDefaultJobParametersValidatorspring-doc.cn

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

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

您可以将验证器指定为引用(如前所述)或嵌套 Bean 定义。beansspring-doc.cn

验证器的配置通过 Java 构建器支持:spring-doc.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 的配置有三个组件:注释和两个构建器。@EnableBatchProcessingspring-doc.cn

该注释的工作方式类似于 Spring 家族。在这种情况下,为 构建批处理作业。在此基本配置中,和 的实例是 created,此外还有许多 bean 可用于 autowired:@EnableBatchProcessing@Enable*@EnableBatchProcessingStepScopeJobScopespring-doc.cn

默认实现提供前面列表中提到的 bean,并要求 a 和 a 在上下文中作为 bean 提供。数据源和交易 manager 由 和 实例使用。默认情况下,将使用名为 的数据源 和 名为 的事务管理器。您可以使用 注释的属性。以下示例演示如何提供 自定义数据源和事务管理器:DataSourcePlatformTransactionManagerJobRepositoryJobExplorerdataSourcetransactionManager@EnableBatchProcessingspring-doc.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();
	}

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

从 v5.0 开始,配置基本 infrastrucutre bean 的另一种编程方式 通过类提供。此类提供相同的 bean 由 提供,并可用作配置批处理作业的基类。 以下代码片段是如何使用它的典型示例:DefaultBatchConfiguration@EnableBatchProcessingspring-doc.cn

@Configuration
class MyJobConfiguration extends DefaultBatchConfiguration {

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

}

数据源和事务管理器将从应用程序上下文中解析 并在 Job repository 和 Job Explorer 上进行设置。您可以自定义配置 通过覆盖所需的 setter 来获取。以下示例 演示如何自定义字符编码,例如:spring-doc.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@EnableBatchProcessingDefaultBatchConfiguration

配置 JobRepository

使用 时,将为您提供 a。 本节介绍如何配置您自己的@EnableBatchProcessingJobRepositoryspring-doc.cn

如前所述,JobRepository 用于各种持久化 Spring Batch 中的域对象,例如 和 。 许多主要框架功能(如 、 和 )都需要它。JobExecutionStepExecutionJobLauncherJobStepspring-doc.cn

batch 命名空间抽象出 implementations 及其协作者的许多实现细节。但是,仍然有一些 可用的配置选项,如下例所示:JobRepositoryspring-doc.cn

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

除 之外,前面列出的任何配置选项都不是必需的。如果他们是 未设置,则使用前面显示的默认值。 默认为 ,这是示例架构中长列的长度 脚本idmax-varchar-length2500VARCHARspring-doc.cn

除了 和 之外,前面列出的任何配置选项都不是必需的。 如果未设置,则前面显示的默认值 使用。这 max length 默认为 ,即 示例架构脚本中长列的长度dataSourcetransactionManagervarchar2500VARCHARspring-doc.cn

JobRepository 的事务配置

如果使用 namespace 或 provided ,则事务性建议为 围绕存储库自动创建。这是为了确保批处理元数据 包括失败后重新启动所需的状态将正确保留。 如果存储库方法不是 事务。指定 method 属性中的隔离级别 以确保在启动作业时,如果两个进程尝试启动 同一时间执行相同的作业,只有一个成功。该 方法是 ,这是相当激进的。 通常效果相同 井。 如果两个进程在此中不太可能发生冲突,则很好 道路。但是,由于对该方法的调用非常短,因此只要数据库平台支持它,就不太可能导致问题。但是,您 可以覆盖此设置。FactoryBeancreate*SERIALIZABLEREAD_COMMITTEDREAD_UNCOMMITTEDcreate*SERIALIZEDspring-doc.cn

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

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

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

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

   // job definition

}

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

以下示例显示如何配置存储库的事务行为 在 XML 中:spring-doc.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>

您可以几乎按原样使用前面的 fragment,几乎不需要任何更改。还要记住 包含适当的名称空间声明,并确保 and(或整个 Spring)在 Classpath 上。spring-txspring-aopspring-doc.cn

以下示例显示如何配置存储库的事务行为 在 Java 中:spring-doc.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;
}

更改表前缀

的另一个可修改属性是元数据的表前缀 表。默认情况下,它们都以 . 和 是两个例子。但是,有一些潜在的原因需要修改它 前缀。如果需要在表名前面加上架构名称,或者如果有多个 的元数据表集,则表前缀需要 被更改。JobRepositoryBATCH_BATCH_JOB_EXECUTIONBATCH_STEP_EXECUTIONspring-doc.cn

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

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

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

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

   // job definition

}

鉴于上述更改,对元数据表的每个查询都以 为前缀。 称为 。SYSTEM.TEST_BATCH_JOB_EXECUTIONSYSTEM.TEST_JOB_EXECUTIONspring-doc.cn

只有表前缀是可配置的。table 和 column name 不是。

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

如果您使用的数据库平台不在受支持平台列表中,则 如果 SQL 变体足够接近,则可能能够使用支持的类型之一。待办事项 这样,您可以使用 RAW 而不是命名空间快捷方式和 使用它来将 Database type (数据库类型) 设置为最接近的匹配项。JobRepositoryFactoryBeanspring-doc.cn

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

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

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

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

如果未指定数据库类型,则尝试 从 中自动检测数据库类型。 平台之间的主要区别是 主要由递增主键的策略负责,因此 通常还需要覆盖 (通过使用标准的 来自 Spring Framework 的实现)。JobRepositoryFactoryBeanDataSourceincrementerFactoryspring-doc.cn

如果这不起作用,或者您没有使用 RDBMS,则 唯一的选项可能是实现 on 上,并以正常的 Spring 方式手动连接一个。DaoSimpleJobRepositoryspring-doc.cn

配置 JobLauncher

当您使用 时,将为您提供 a。 本节介绍如何配置您自己的@EnableBatchProcessingJobRegistryspring-doc.cn

该接口最基本的实现是 . 它唯一需要的依赖项是 a (需要获取执行)。JobLauncherTaskExecutorJobLauncherJobRepositoryspring-doc.cn

以下示例显示了 XML 格式:TaskExecutorJobLauncherspring-doc.cn

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

以下示例显示了 Java 中的一个:TaskExecutorJobLauncherspring-doc.cn

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

获取 JobExecution 后,它会传递给 execute 方法,最终将 返回给调用者,作为 下图显示了:JobJobExecutionspring-doc.cn

Job Launcher 序列
图 2.Job Launcher 序列

该序列简单明了,从计划程序启动时效果很好。然而 尝试从 HTTP 请求启动时出现问题。在这种情况下,启动 需要异步完成,以便立即返回到其 访客。这是因为让 HTTP 请求保持打开状态并不是一个好的做法 长时间运行的进程(如批处理作业)所需的时间。下图显示了 示例序列:TaskExecutorJobLauncherspring-doc.cn

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

您可以通过配置 来配置 以允许此方案。TaskExecutorJobLauncherTaskExecutorspring-doc.cn

下面的 XML 示例将 a 配置为立即返回:TaskExecutorJobLauncherspring-doc.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 示例将 a 配置为立即返回:TaskExecutorJobLauncherspring-doc.cn

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

你可以使用 spring 接口的任何实现来控制 Job 的异步方式 执行。TaskExecutorspring-doc.cn

运行 Job

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

从命令行运行 Job

如果要从企业运行作业 scheduler 中,命令行是主界面。这是因为 大多数调度程序(Quartz 除外,除非使用 )直接与 os 一起工作 进程,主要由 shell 脚本启动。有很多方法 启动除 shell 脚本之外的 Java 进程,例如 Perl、Ruby 或 甚至构建工具,例如 Ant 或 Maven。但是,因为大多数人 熟悉 shell 脚本,本示例重点介绍它们。NativeJobspring-doc.cn

The CommandLineJobRunner

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

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

表 1.CommandLineJobRunner 参数

jobPathspring-doc.cn

XML 文件的位置,用于 创建一个 .此文件 应包含运行完整 .ApplicationContextJobspring-doc.cn

jobNamespring-doc.cn

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

必须传入这些参数,路径在前,名称在后。所有参数 在这些被认为是作业参数后,它们被转换为对象, ,并且必须采用 .JobParametersname=valuespring-doc.cn

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

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

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

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

默认情况下,它使用隐式转换 用于标识作业参数的键/值对。但是,您可以显式指定 哪些作业参数正在标识,哪些不是,分别通过给它们加上 或 后缀。CommandLineJobRunnerDefaultJobParametersConvertertruefalsespring-doc.cn

在以下示例中, is 是一个标识作业参数,while is not:schedule.datevendor.idspring-doc.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

您可以通过使用自定义 .JobParametersConverterspring-doc.cn

在大多数情况下,您需要使用 manifest 在 jar 中声明您的类。然而 为简单起见,该类被直接使用。此示例使用 Batch 的域语言中的示例。第一个 参数是 ,它是包含 .第二个参数表示作业名称。最后一个参数 , 将转换为 类型为 的对象。mainEndOfDayendOfDayJob.xmlJobendOfDay,schedule.date=2007-05-05,java.time.LocalDateJobParameterjava.time.LocalDatespring-doc.cn

以下示例显示了 in XML 的示例配置:endOfDayspring-doc.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" />

在大多数情况下,您需要使用 manifest 在 jar 中声明您的类。然而 为简单起见,该类被直接使用。此示例使用 Batch 的域语言中的示例。第一个 argument 是 ,这是完全限定的类名 添加到包含 Job 的配置类中。第二个参数 , 表示 作业名称。最后一个参数 , 被转换 转换为类型为 .mainEndOfDayio.spring.EndOfDayJobConfigurationendOfDayschedule.date=2007-05-05,java.time.LocalDateJobParameterjava.time.LocalDatespring-doc.cn

以下示例显示了 在 Java 中的配置示例:endOfDayspring-doc.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 中运行批处理作业,但它用于显示两个主要的 的要求 : 和 .CommandLineJobRunnerJobJobLauncherspring-doc.cn

退出代码

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

public interface ExitCodeMapper {

    public int intValue(String exitCode);

}

an 的基本协定是,给定一个字符串 exit code 时,将返回一个数字表示形式。默认的 Job Runner 使用的实现是返回 0 表示完成、1 表示一般错误和 2 表示任何作业的实现 运行器错误,例如无法在提供的上下文中找到 A。如果有更多 complex 的 3 个值,则自定义 接口的实现 必须提供。因为 是创建 因此,AN AND 不能是 'wired together' 时,任何需要覆盖的值都必须是 自动装配。这意味着,如果在 、 在创建上下文后,它会注入到 Runner 中。都 需要做的是提供你自己的 作为根级 Bean 进行加载,并确保它是 跑步者。ExitCodeMapperSimpleJvmExitCodeMapperJobExitCodeMapperCommandLineJobRunnerApplicationContextExitCodeMapperBeanFactoryExitCodeMapperApplicationContextspring-doc.cn

从 Web 容器中运行作业

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

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

在这种情况下,控制器是 Spring MVC 控制器。请参阅 Spring Framework 参考指南,了解有关 Spring MVC 的更多信息。 控制器使用已配置为异步启动的 来启动 一个,该 立即返回一个 .它可能仍在运行。但是,此 非阻塞行为允许控制器立即返回,这 在处理 .以下清单 显示了一个例子:JobJobLauncherJobExecutionJobHttpRequestspring-doc.cn

@Controller
public class JobLauncherController {

    @Autowired
    JobLauncher jobLauncher;

    @Autowired
    Job job;

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

高级元数据用法

到目前为止,和 接口都是 讨论。它们共同代表了 Job 和 Basic 的简单启动 批处理域对象的 CRUD 操作:JobLauncherJobRepositoryspring-doc.cn

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

A 使用 创建新对象并运行它们。 和实现 以后对基本更新使用相同的 在 . 基本操作足以满足简单的场景。但是,在大批量 具有数百个批处理作业和复杂调度的环境 要求,则需要对元数据进行更高级的访问:JobLauncherJobRepositoryJobExecutionJobStepJobRepositoryJobspring-doc.cn

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

和 接口,这些接口将进行讨论 在接下来的部分中,添加用于查询和控制元数据的其他功能。JobExplorerJobOperatorspring-doc.cn

查询存储库

在任何高级功能之前,最基本的需求是能够 查询存储库中的现有执行。此功能是 由 interface 提供:JobExplorerspring-doc.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);
}

从其方法签名中可以明显看出,是 的 ,并且与 一样,它可以通过使用 工厂 Bean 的 Bean 中。JobExplorerJobRepositoryJobRepositoryspring-doc.cn

以下示例显示了如何在 XML 中配置 a:JobExplorerspring-doc.cn

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

以下示例演示如何在 Java 中配置 a:JobExplorerspring-doc.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();
}
...

在本章的前面部分,我们注意到您可以修改表前缀 的允许不同的版本或架构。因为 对于相同的表,它还需要能够设置前缀。JobRepositoryJobExplorerspring-doc.cn

以下示例演示如何在 XML 中设置 a 的表前缀:JobExplorerspring-doc.cn

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

以下示例显示了如何在 Java 中为 a 设置表前缀:JobExplorerspring-doc.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 (作业注册表)

A(及其父接口 )不是必需的,但可以是 如果要跟踪上下文中可用的作业,则很有用。它也是 在创建作业时,用于在应用程序上下文中集中收集作业 其他位置(例如,在子上下文中)。您还可以使用自定义实施 操作已注册作业的名称和其他属性。 框架只提供了一个实现,它基于一个简单的 从 Job Name 映射到 Job Instance。JobRegistryJobLocatorJobRegistryspring-doc.cn

以下示例显示了如何包含 for a for a job defined in XML:JobRegistryspring-doc.cn

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

使用 时,将为您提供 a。 以下示例显示如何配置您自己的 :@EnableBatchProcessingJobRegistryJobRegistryspring-doc.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();
}
...

您可以通过以下两种方式之一填充 a:使用 Bean 后处理器或使用 registrar 生命周期组件。即将来临 各节介绍了这两种机制。JobRegistryspring-doc.cn

JobRegistryBeanPostProcessor

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

以下示例显示了如何为 job 添加 在 XML 中定义:JobRegistryBeanPostProcessorspring-doc.cn

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

以下示例显示了如何为 job 添加 在 Java 中定义:JobRegistryBeanPostProcessorspring-doc.cn

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

尽管并非绝对必要,但 example 已被赋予一个,以便它可以包含在 Child 中 contexts(例如,作为父 Bean 定义)并导致创建所有作业 那里也会自动注册。idspring-doc.cn

自动作业注册器

这是一个生命周期组件,用于创建子上下文并从中注册作业 上下文。这样做的一个好处是,虽然 子上下文在 Registry 中仍然必须是全局唯一的,它们的依赖项 可以有 “natural” 名称。因此,例如,您可以创建一组 XML 配置文件 每个 Job 只有一个 Job ,但 an 的定义不同,其中 相同的 Bean 名称,例如 .如果所有这些文件都导入到同一上下文中,则 Reader 定义会相互冲突并覆盖,但是,使用 Automatic registrar 的 intent 和未加密的 intent 的 x这使得集成 应用程序的单独模块。ItemReaderreaderspring-doc.cn

以下示例显示了如何包含定义的 for a job defined 在 XML 中:AutomaticJobRegistrarspring-doc.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>

以下示例显示了如何包含定义的 for a job defined 在 Java 中:AutomaticJobRegistrarspring-doc.cn

Java 配置
@Bean
public AutomaticJobRegistrar registrar() {

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

}

registrar 有两个必需属性:一个数组(从 方便的工厂 Bean)和 .负责管理子上下文的生命周期,并且 在 中注册作业。ApplicationContextFactoryJobLoaderJobLoaderJobRegistryspring-doc.cn

的 负责创建子上下文。最常见的用法 是(如前面的示例所示)来使用 .其中之一 这个工厂的特点是,默认情况下,它会复制一些 configuration down 从 parent context 到 child。因此,对于 实例,则无需重新定义 or AOP 配置,前提是它应与 父母。ApplicationContextFactoryClassPathXmlApplicationContextFactoryPropertyPlaceholderConfigurerspring-doc.cn

您可以在 与 a 结合使用(只要您也使用 )。 例如,如果有工作,这可能是可取的 在主父上下文和子上下文中定义 地点。AutomaticJobRegistrarJobRegistryBeanPostProcessorDefaultJobLoaderspring-doc.cn

JobOperator (作业操作员)

如前所述,它提供对元数据的 CRUD 操作,并且 提供对 元数据。但是,这些操作在一起使用时最有用 执行常见监控任务,例如停止、重新启动或 总结 Job,这通常是由 Batch Operator 完成的。Spring Batch 在界面中提供了以下类型的操作:JobRepositoryJobExplorerJobOperatorspring-doc.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();

}

上述操作表示来自许多不同接口的方法,例如 、 、 和 。因此, 提供的 () 实现具有许多依赖项。JobLauncherJobRepositoryJobExplorerJobRegistryJobOperatorSimpleJobOperatorspring-doc.cn

以下示例显示了 XML 中的典型 bean 定义:SimpleJobOperatorspring-doc.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>

以下示例显示了 Java 中的典型 bean 定义:SimpleJobOperatorspring-doc.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 开始,注解会自动注册一个作业操作员 Bean 在应用程序上下文中。@EnableBatchProcessingspring-doc.cn

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

JobParametersIncrementer (作业参数增量器)

上的大多数方法都是 一目了然,您可以在界面的 Javadoc 中找到更详细的解释。但是,该方法值得注意。这 方法始终启动 . 如果 a 中存在严重问题并且需要从头开始,这将非常有用。与(需要一个触发 new 的新对象)不同,如果参数与 任何前面的参数集,该方法都使用 Tied to 来强制 to a 新建实例:JobOperatorstartNextInstanceJobJobExecutionJobJobLauncherJobParametersJobInstancestartNextInstanceJobParametersIncrementerJobJobspring-doc.cn

public interface JobParametersIncrementer {

    JobParameters getNext(JobParameters parameters);

}

的合约是 给定一个 JobParameters 对象,它通过增加它可能包含的任何必要值来返回 “next” 对象。这 策略很有用,因为框架无法知道 将其设为“下一个”的更改 实例。例如,如果 中的唯一值是 date 和 next 实例 应该创建,该值应增加 1 天或 1 周(例如,如果工作是每周)?任何 有助于识别 、 的数值 如下例所示:JobParametersIncrementerJobParametersJobParametersJobParametersJobspring-doc.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();
    }
}

在此示例中,键为 区分。如果传入的值为 null,则可以为 假设 以前从未运行 因此,可以返回其初始状态。但是,如果不是,则旧的 value 被获取,递增 1 并返回。run.idJobInstancesJobParametersJobspring-doc.cn

对于在 XML 中定义的作业,您可以通过命名空间中的属性将 incrementer 与 a 关联,如下所示:Jobincrementerspring-doc.cn

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

对于在 Java 中定义的作业,您可以通过构建器中提供的方法将 incrementer 与 关联,如下所示:Jobincrementerspring-doc.cn

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

停止作业

最常见的用例之一是正常停止 工作:JobOperatorspring-doc.cn

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

关闭不是立即的,因为没有办法强制 立即关闭,尤其是在执行当前处于 开发人员代码,例如 商业服务。但是,一旦控制权返回到 框架中,它将当前状态设置为 ,保存它,并执行相同的操作 对于整理前。StepExecutionBatchStatus.STOPPEDJobExecutionspring-doc.cn

中止作业

任务执行可以是 restarted (如果 是可重启的)。状态为 的任务执行 Framework 无法重新启动。 状态也用于步骤 executions 在重新启动的任务执行中将其标记为可跳过。如果 作业正在运行,并遇到在上一个失败的作业执行中标记的步骤,则 继续执行下一步(由 Job Flow 定义确定 和步骤执行退出状态)。FAILEDJobABANDONEDABANDONEDABANDONEDspring-doc.cn

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