配置和运行作业

配置和运行作业

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

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

虽然Jobobject 可能看起来像一个简单的 容器中,有许多配置选项,其中 开发人员必须知道。此外,还有许多考虑因素 如何Job将运行,以及其元数据将如何 在该运行期间存储。本章将解释各种配置 options 和 runtime 关注点Job.spring-doc.cadn.net.cn

配置 Job

有多种Job接口。然而 构建器抽象出配置中的差异。spring-doc.cadn.net.cn

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

一个Job(通常为任何Step)需要一个JobRepository.这 的配置JobRepository通过BatchConfigurer.spring-doc.cadn.net.cn

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

无论您使用 Java 还是 XML,都有Job接口。但是,命名空间抽象出了配置中的差异。它有 只有三个必需的依赖项:名称、JobRepositoryStep实例。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永远不应该是 重新启动,但应始终作为新的JobInstance,则 restartable 属性可以设置为 'false'。spring-doc.cadn.net.cn

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

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

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

Java 配置
@Bean
public Job footballJob() {
    return this.jobBuilderFactory.get("footballJob")
                     .preventRestart()
                     ...
                     .build();
}

换句话说,将 restartable 设置为 false 意味着 “thisJob不支持再次启动”。重新启动Job那不是 restartable 会导致JobRestartException自 被扔掉。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
}

此 JUnit 代码片段展示了如何尝试创建JobExecution第一次对于 non restartable job 不会造成任何问题。然而,第二个 attempt 将抛出一个JobRestartException.spring-doc.cadn.net.cn

拦截任务执行

在执行 工作,通知各种可能很有用 事件,以便可以执行自定义代码。这SimpleJob允许通过调用JobListener在适当的时候:spring-doc.cadn.net.cn

public interface JobExecutionListener {

    void beforeJob(JobExecution jobExecution);

    void afterJob(JobExecution jobExecution);

}

JobListeners可以添加到SimpleJob通过在作业上设置 listeners 来执行。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() {
    return this.jobBuilderFactory.get("footballJob")
                     .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 具有相似但不是 相同,则定义 “parent” 可能会有所帮助Job从中得到混凝土 Job 可以继承 properties。与 class 相似 继承,即 “child”Job将合并 它的元素和属性与父级的 Elements 和 Attributes 一起使用。spring-doc.cadn.net.cn

在下面的示例中,“baseJob” 是一个抽象Job定义,仅定义 听众。这Job“job1” 是一个具体的 从 “baseJob” 继承侦听器列表并合并的定义 it 使用自己的侦听器列表来生成Job具有 2 个侦听器和 1 个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

JobParametersValidator (作业参数验证器)

在 XML 命名空间中声明的作业或使用AbstractJob可以选择性地为 Job 参数声明一个验证器 运行。例如,当您需要断言作业 启动时,使用其所有必需参数。有一个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 定义。spring-doc.cadn.net.cn

验证器的配置通过 java 构建器支持,如 以下示例:spring-doc.cadn.net.cn

@Bean
public Job job1() {
    return this.jobBuilderFactory.get("job1")
                     .validator(parametersValidator())
                     ...
                     .build();
}

Java 配置

Spring 3 带来了通过 Java 而不是 XML 配置应用程序的能力。截至 Spring Batch 2.2.0 中,可以使用相同的 java 配置来配置批处理作业。 基于 java 的配置有两个组件:@EnableBatchProcessingannotation 和 two builders。spring-doc.cadn.net.cn

@EnableBatchProcessing的工作方式@Enable类似于 Spring 家族。在这种情况下,@EnableBatchProcessing提供 构建批处理作业。在此基本配置中,StepScope是 除了许多可用于自动装配的 bean 之外,还创建了:spring-doc.cadn.net.cn

此配置的核心接口是BatchConfigurer.默认的 implementation 提供了上面提到的 bean,并且需要一个DataSource作为 Bean 在要提供的上下文中。此数据源由 JobRepository 使用。 您可以自定义这些 bean 中的任何一个 通过创建BatchConfigurer接口。 通常,扩展DefaultBatchConfigurer(如果BatchConfigurer未找到),并且覆盖所需的 getter 就足够了。 但是,可能需要从头开始实现自己的 API。以下内容 示例展示了如何提供自定义事务管理器:spring-doc.cadn.net.cn

@Bean
public BatchConfigurer batchConfigurer(DataSource dataSource) {
	return new DefaultBatchConfigurer(dataSource) {
		@Override
		public PlatformTransactionManager getTransactionManager() {
			return new MyTransactionManager();
		}
	};
}

只有一个配置类需要具有@EnableBatchProcessing注解。一次 你有一个用它注释的类,你将拥有以上所有内容。spring-doc.cadn.net.cn

有了基本配置,用户就可以使用提供的构建器工厂来 配置作业。以下示例显示了一个配置了JobBuilderFactoryStepBuilderFactory:spring-doc.cadn.net.cn

@Configuration
@EnableBatchProcessing
@Import(DataSourceConfiguration.class)
public class AppConfig {

    @Autowired
    private JobBuilderFactory jobs;

    @Autowired
    private StepBuilderFactory steps;

    @Bean
    public Job job(@Qualifier("step1") Step step1, @Qualifier("step2") Step step2) {
        return jobs.get("myJob").start(step1).next(step2).build();
    }

    @Bean
    protected Step step1(ItemReader<Person> reader,
                         ItemProcessor<Person, Person> processor,
                         ItemWriter<Person> writer) {
        return steps.get("step1")
            .<Person, Person> chunk(10)
            .reader(reader)
            .processor(processor)
            .writer(writer)
            .build();
    }

    @Bean
    protected Step step2(Tasklet tasklet) {
        return steps.get("step2")
            .tasklet(tasklet)
            .build();
    }
}

配置 JobRepository

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

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

batch 命名空间抽象出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,即 long 的长度VARCHAR示例架构中的列 脚本spring-doc.cadn.net.cn

使用 java 配置时,一个JobRepository为您提供。基于 JDBC 的 API 是 如果DataSource,则Map如果没有,则基于一个。然而 您可以自定义JobRepository通过BatchConfigurer接口。spring-doc.cadn.net.cn

Java 配置
...
// This would reside in your BatchConfigurer implementation
@Override
protected JobRepository createJobRepository() throws Exception {
    JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
    factory.setDataSource(dataSource);
    factory.setTransactionManager(transactionManager);
    factory.setIsolationLevelForCreate("ISOLATION_SERIALIZABLE");
    factory.setTablePrefix("BATCH_");
    factory.setMaxVarCharLength(1000);
    return factory.getObject();
}
...

上面列出的任何配置选项都不是必需的,但 dataSource 和 transactionManager.如果未设置,则使用上面显示的默认值 将被使用。它们在上面显示是为了提高认识目的。这 Max Varchar Length 默认为 2500,即 长VARCHAR示例架构脚本中的列spring-doc.cadn.net.cn

JobRepository 的事务配置

如果命名空间或提供的FactoryBean,交易建议是 围绕存储库自动创建。这是为了确保批量元数据 包括失败后重新启动所需的状态将正确保留。 如果存储库方法不是 事务。的create*method attributes 的 以确保在启动作业时,如果两个进程尝试启动 同一时间执行相同的作业,只有一个成功。该 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 配置
// This would reside in your BatchConfigurer implementation
@Override
protected JobRepository createJobRepository() throws Exception {
    JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
    factory.setDataSource(dataSource);
    factory.setTransactionManager(transactionManager);
    factory.setIsolationLevelForCreate("ISOLATION_REPEATABLE_READ");
    return factory.getObject();
}

如果未使用名称空间或工厂 bean,则还必须配置 使用 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-tx 和 spring-aop (或整个 Spring)都在 Classpath 上。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是两个例子。但是,有一些潜在的原因需要修改它 前缀。如果需要将架构名称添加到表名称前面,或者如果有多个 的 meta data tables 集,则表前缀需要 被更改: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 配置
// This would reside in your BatchConfigurer implementation
@Override
protected JobRepository createJobRepository() throws Exception {
    JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
    factory.setDataSource(dataSource);
    factory.setTransactionManager(transactionManager);
    factory.setTablePrefix("SYSTEM.TEST_");
    return factory.getObject();
}

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

只有表前缀是可配置的。table 和 column name 不是。spring-doc.cadn.net.cn

内存存储库

在某些情况下,您可能不希望将域对象持久化到 数据库。一个原因可能是速度;在每个提交点存储域对象需要额外的 时间。另一个原因可能是您不需要为特定 工作。因此, Spring Batch 提供了内存中的Map作业的版本 存储 库。spring-doc.cadn.net.cn

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

XML 配置
<bean id="jobRepository"
  class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean">
    <property name="transactionManager" ref="transactionManager"/>
</bean>

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

Java 配置
// This would reside in your BatchConfigurer implementation
@Override
protected JobRepository createJobRepository() throws Exception {
    MapJobRepositoryFactoryBean factory = new MapJobRepositoryFactoryBean();
    factory.setTransactionManager(transactionManager);
    return factory.getObject();
}

请注意,内存存储库是可变的,因此不允许在 JVM 之间重新启动 实例。它也不能保证具有相同参数的两个作业实例是 同时启动,不适合在多线程 Job 中使用,也不适合在本地 分区Step.因此,在您需要的地方使用存储库的数据库版本 特征。spring-doc.cadn.net.cn

但是,它确实需要定义事务管理器,因为存在回滚 语义,并且因为业务逻辑可能仍是 事务性(如 RDBMS 访问)。出于测试目的,许多人发现ResourcelessTransactionManager有用。spring-doc.cadn.net.cn

MapJobRepositoryFactoryBean和相关类已在 v4 中弃用,并已安排 在 v5 中删除。如果要使用内存中作业存储库,可以使用嵌入式数据库 如 H2、Apache Derby 或 HSQLDB。有几种方法可以创建嵌入式数据库并在 您的 Spring Batch 应用程序。一种方法是使用 Spring JDBC 中的 API:spring-doc.cadn.net.cn

@Bean
public DataSource dataSource() {
    return new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.H2)
            .addScript("/org/springframework/batch/core/schema-drop-h2.sql")
            .addScript("/org/springframework/batch/core/schema-h2.sql")
            .build();
}

在应用程序上下文中将嵌入式数据源定义为 bean 后,应选择它 如果您使用@EnableBatchProcessing.否则,您可以使用 基于 JDBCJobRepositoryFactoryBeanConfiguring a JobRepository 部分所示。spring-doc.cadn.net.cn

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

如果您使用的数据库平台不在受支持平台列表中,则 如果 SQL 变体足够接近,则可能能够使用支持的类型之一。待办事项 this,您可以使用 RAWJobRepositoryFactoryBean而不是命名空间快捷方式和 使用它来将 Database type (数据库类型) 设置为最接近的匹配项。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 配置
// This would reside in your BatchConfigurer implementation
@Override
protected JobRepository createJobRepository() 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取决于 on 上,并以正常的 Spring 方式手动连接一个。spring-doc.cadn.net.cn

配置 JobLauncher

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

最基本的JobLauncherinterface 是SimpleJobLauncher. 它唯一需要的依赖项是JobRepository,以便执行。spring-doc.cadn.net.cn

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

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

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

Java 配置
...
// This would reside in your BatchConfigurer implementation
@Override
protected JobLauncher createJobLauncher() throws Exception {
	SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
	jobLauncher.setJobRepository(jobRepository);
	jobLauncher.afterPropertiesSet();
	return jobLauncher;
}
...

获取 JobExecution 后,它会传递给 execute 方法Job,最终返回JobExecution到调用方,如图所示 在下图中:spring-doc.cadn.net.cn

Job Launcher 序列
图 2.Job Launcher 序列

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

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

SimpleJobLauncher可以通过配置TaskExecutor.spring-doc.cadn.net.cn

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

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

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

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

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

运行 Job

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

从命令行运行 Job

对于希望从企业运行其作业的用户 scheduler 中,命令行是主界面。这是因为 大多数调度器(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 参数

jobPath (作业路径)spring-doc.cadn.net.cn

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

jobName (作业名称)spring-doc.cadn.net.cn

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

这些参数必须与路径 first 和名称 second 一起传入。所有参数 在这些参数被视为作业参数后,将转换为 JobParameters 对象, 并且必须采用 'name=value' 格式。spring-doc.cadn.net.cn

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

<bash$ java CommandLineJobRunner endOfDayJob.xml endOfDay schedule.date(date)=2007/05/05

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

<bash$ java CommandLineJobRunner io.spring.EndOfDayJobConfiguration endOfDay schedule.date(date)=2007/05/05

默认情况下,CommandLineJobRunner使用DefaultJobParametersConverter它隐式地转换 用于标识作业参数的键/值对。但是,可以显式指定 哪些作业参数正在标识,哪些不是,分别在它们前面加上 或 前缀。+-spring-doc.cadn.net.cn

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

<bash$ java CommandLineJobRunner endOfDayJob.xml endOfDay \
                                 +schedule.date(date)=2007/05/05 -vendor.id=123
<bash$ java CommandLineJobRunner io.spring.EndOfDayJobConfiguration endOfDay \
                                 +schedule.date(date)=2007/05/05 -vendor.id=123

这个行为可以通过使用自定义的JobParametersConverter.spring-doc.cadn.net.cn

在大多数情况下,您可能希望使用 manifest 在 jar 中声明您的主类,但是, 为简单起见,该类被直接使用。此示例使用相同的 'EndOfDay' 来自 domainLanguageOfBatch 的示例。第一个 参数是 'endOfDayJob.xml',它是包含Job.第二个参数 'endOfDay' 表示作业名称。最后一个参数 'schedule.date(date)=2007/05/05')转换为 JobParameters 对象。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.SimpleJobLauncher" />

在大多数情况下,您希望使用 manifest 在 jar 中声明您的主类,但是, 为简单起见,该类被直接使用。此示例使用相同的 'EndOfDay' 来自 domainLanguageOfBatch 的示例。第一个 参数是 'io.spring.EndOfDayJobConfiguration',这是完全限定的类名 添加到包含 Job 的配置类中。第二个参数 'endOfDay' 表示 作业名称。最后一个参数 'schedule.date(date)=2007/05/05' 被转换为JobParameters对象。java 配置的示例如下:spring-doc.cadn.net.cn

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

@Configuration
@EnableBatchProcessing
public class EndOfDayJobConfiguration {

    @Autowired
    private JobBuilderFactory jobBuilderFactory;

    @Autowired
    private StepBuilderFactory stepBuilderFactory;

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

    @Bean
    public Step step1() {
        return this.stepBuilderFactory.get("step1")
    				.tasklet((contribution, chunkContext) -> null)
    				.build();
    }
}

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

退出代码

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

public interface ExitCodeMapper {

    public int intValue(String exitCode);

}

一个ExitCodeMapper是那个,给定一个字符串 exit code 时,将返回一个数字表示形式。默认的 Job Runner 使用的实现是SimpleJvmExitCodeMapper,则返回 0 表示完成,1 表示一般错误,2 表示任何作业 运行器错误,例如找不到Job在提供的上下文中。如果有更多 complex 的 3 个值,则自定义 实现ExitCodeMapper接口 必须提供。因为CommandLineJobRunner是创建 一ApplicationContext,因此不能 'wired together' 时,任何需要覆盖的值都必须是 自动装配。这意味着,如果ExitCodeMapper位于BeanFactory, 在创建上下文后,它将被注入到 runner 中。都 这需要提供你自己的ExitCodeMapper是声明 implementation 作为根级 Bean 进行,并确保它是ApplicationContext由 跑步者。spring-doc.cadn.net.cn

从 Web 容器中运行作业

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

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

在这种情况下,控制器是 Spring MVC 控制器。更多 有关 Spring MVC 的信息,请访问:https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#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());
    }
}

高级元数据使用

到目前为止,JobLauncherJobRepositoryinterfaces 已被 讨论。它们共同表示 Simple Launch of a Job 和 basic 批处理域对象的 CRUD作:spring-doc.cadn.net.cn

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

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

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

JobExplorerJobOperator接口,这将进行讨论 下面,添加了用于查询和控制 meta 的附加功能 数据。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是 的只读版本 这JobRepositoryJobRepository,它可以通过使用 工厂 Bean: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 BatchConfigurer implementation
@Override
public JobExplorer getJobExplorer() 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 BatchConfigurer implementation
@Override
public JobExplorer getJobExplorer() throws Exception {
	JobExplorerFactoryBean factoryBean = new JobExplorerFactoryBean();
	factoryBean.setDataSource(this.dataSource);
	factoryBean.setTablePrefix("SYSTEM.");
	return factoryBean.getObject();
}
...

JobRegistry (作业注册表)

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

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

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

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

使用@EnableBatchProcessing一个JobRegistry为您提供开箱即用的服务。 如果要配置自己的:spring-doc.cadn.net.cn

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

有两种方法可以填充JobRegistry自动:使用 Bean 后处理器并使用 registrar 生命周期组件。这些 以下各节介绍了两种机制。spring-doc.cadn.net.cn

JobRegistryBeanPostProcessor

这是一个 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() {
    JobRegistryBeanPostProcessor postProcessor = new JobRegistryBeanPostProcessor();
    postProcessor.setJobRegistry(jobRegistry());
    return postProcessor;
}

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

AutomaticJobRegistrar

这是一个生命周期组件,用于创建子上下文并从中注册作业 上下文。这样做的一个好处是,虽然 子上下文在 Registry 中仍然必须是全局唯一的,它们的依赖项 可以有 “natural” 名称。例如,您可以创建一组 XML 配置文件 每个 Job 只有一个 Job,但都具有不同的ItemReader使用 相同的 bean 名称,例如 “reader”。如果所有这些文件都导入到同一上下文中,则 Reader 定义会相互冲突并覆盖,但使用 Automatic registrar 这是避免的。这使得集成 Job 贡献 应用程序的单独模块。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;

}

registrar 有两个必需属性,一个是ApplicationContextFactory(此处从 方便的 Factory Bean),另一个是JobLoader.这JobLoader负责管理子上下文的生命周期,并且 在JobRegistry.spring-doc.cadn.net.cn

ApplicationContextFactory是 负责创建子上下文和最常见的用法 将如上所述,使用ClassPathXmlApplicationContextFactory.其中之一 这个工厂的特点是,默认情况下它会复制一些 configuration down 从 parent context 到 child。所以对于 实例中,您不必重新定义PropertyPlaceholderConfigurer或 AOP 配置,如果它应该与 父母。spring-doc.cadn.net.cn

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

JobOperator (作业作员)

如前所述,JobRepository提供对元数据的 CRUD作,并且JobExplorer在 元数据。但是,这些作在一起使用时最有用 执行常见监控任务,例如停止、重新启动或 总结 Job,这通常是由 Batch Operator 完成的。Spring Batch 通过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;
 }

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

JobParametersIncrementer (作业参数增量器)

大多数JobOperator是 不言自明,更详细的解释可以在界面的 Javadoc 上找到。但是,startNextInstance方法值得注意。这 method 将始终启动 Job 的新实例。 如果JobExecution和工作 需要从头开始。与JobLauncher虽然,这需要一个新的JobParameters对象,该对象将触发新的JobInstance如果参数不同于 任何先前的参数集,则startNextInstance方法将使用JobParametersIncrementer绑定到Job以强制Job更改为 新建实例:spring-doc.cadn.net.cn

public interface JobParametersIncrementer {

    JobParameters getNext(JobParameters parameters);

}

的合同JobParametersIncrementer是 给定一个 JobParameters 对象,它将返回“下一个”JobParameters object 来增加它可能包含的任何必要值。这 策略很有用,因为框架无法知道 对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以前从未运行 因此可以返回其初始状态。但是,如果不是,则旧的 value 被获取,递增 1 并返回。spring-doc.cadn.net.cn

对于以 XML 定义的作业,可以将 Incrementer 与Job通过 'incrementer' 属性,如下所示:spring-doc.cadn.net.cn

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

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

@Bean
public Job footballJob() {
    return this.jobBuilderFactory.get("footballJob")
    				 .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 也用于 STEP 中 执行以在重新启动的任务执行中将其标记为可跳过:如果 作业正在执行并遇到已标记的步骤ABANDONED在上一个失败的任务执行中,它 将进入下一步(由 Job Flow 定义确定 和步骤执行退出状态)。spring-doc.cadn.net.cn

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