SpringBatch系列文章-作业配置和运行-文章翻译


官网地址:
https://docs.spring.io/spring-batch/docs/4.2.x/reference/html/job.html#configureJob,网页翻译工具翻译,官网有XML和JAVA配置两个版本,这里主要是JAVA版本。

批处理总体模型:
在这里插入图片描述
后面介绍Job的一些配置项。

作业配置Configuring a Job

该Job接口有多种实现,但是通过构建器可以消除配置差异。

这里用到了设计模式-构建模式

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

上面的示例说明创建了一个Id为footballJob的Job,由三个Step(playerLoad,gameLoad,playerSummarization)实例组成的。

jobBuilderFactory还可以有其他的配置项,包括parallelisation (Split), declarative flow control (Decision) and externalization of flow definitions (Flow).

Job(并且典型地任何Step在其内)需要一个JobRepository的配置JobRepository通过进行处理BatchConfigurer。

XML配置:

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

可重启性Restartability

执行批处理作业时的一个关键问题与Job重新启动作业时的行为有关。理想情况下,所有作业都应该能够从中断的地方开始,但是在某些情况下这是不可能的。开发人员完全有责任确保JobInstance在这种情况下创建一个新文件。但是,Spring Batch确实提供了一些帮助。如果a Job绝不应该重新启动,而应始终作为new的一部分运行JobInstance,则可重新启动属性可以设置为’false’:

Java配置
@Bean
public Job footballJob() {
    return this.jobBuilderFactory.get("footballJob")
                     .preventRestart()
                     ...
                     .build();
}
XML配置:
<job id="footballJob" restartable="false">
    ...
</job>

换句话说,将restartable设置为false意味着“这 Job不支持再次启动”。重新启动Job无法重新启动的会导致JobRestartException抛出:

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为不可重新启动的作业首次创建 不会造成任何问题。但是,第二次尝试将抛出JobRestartException。

拦截作业执行Intercepting Job Execution

在执行作业的过程中,通知其生命周期中的各种事件可能很有用,以便可以执行自定义代码。在 SimpleJob允许通过调用一个 JobListener在适当的时候:

public interface JobExecutionListener {

    void beforeJob(JobExecution jobExecution);

    void afterJob(JobExecution jobExecution);

}

JobListeners可以SimpleJob通过作业上的listeners元素添加到中 :

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

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>

应该注意的是,afterJob无论作业成功与否,都会调用。如果需要确定成功或失败,可以从以下位置获得JobExecution:

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

与此接口对应的注释为:

@BeforeJob

@AfterJob

JobParametersValidator

在XML名称空间中声明的作业或使用的任何子类 AbstractJob可以选择在运行时声明作业参数的验证器。例如,当您需要断言一个作业使用其所有必填参数启动时,此功能很有用。有一个 DefaultJobParametersValidator可以用来约束简单的强制性和可选参数的组合,对于更复杂的约束,您可以自己实现接口。

通过Java构建器支持验证器的配置,例如:

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

XML配置:
<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>
<job id="job1" parent="baseJob3">
    <step id="step1" parent="standaloneStep"/>
    <validator ref="parametersValidator"/>
</job>

除了XML,Spring 3还提供了通过Java配置应用程序的功能。从Spring Batch 2.2.0开始,可以使用相同的Java配置来配置批处理作业。基于Java的配置有两个组件:@EnableBatchProcessing批注和两个构建器。

这些@EnableBatchProcessing作品与Spring系列中的其他@ Enable *注释相似。在这种情况下, @EnableBatchProcessing提供用于构建批处理作业的基本配置。在此基本配置中,StepScope除了提供许多可自动装配的bean之外,还创建了的实例 :

  • JobRepository - bean name “jobRepository”

  • JobLauncher - bean name “jobLauncher”

  • JobRegistry - bean name “jobRegistry”

  • PlatformTransactionManager - bean name “transactionManager”

  • JobBuilderFactory - bean name “jobBuilders”

  • StepBuilderFactory - bean name “stepBuilders”

此配置的核心接口是BatchConfigurer。默认实现提供上面提到的bean,并且需要 DataSource在上下文中提供一个bean。JobRepository将使用此数据源。您可以通过创建BatchConfigurer接口的自定义实现来自定义这些bean中的任何一个。通常,扩展DefaultBatchConfigurer(如果BatchConfigurer未找到a时提供 )并覆盖所需的吸气剂就足够了。但是,可能需要从头开始实现。以下示例显示如何提供自定义事务管理器:

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

只有一个配置类需要具有 @EnableBatchProcessing注释。在为课程加上注释后,您将可以使用上述所有内容。

使用基本配置后,用户可以使用提供的构建器工厂来配置作业。以下是通过JobBuilderFactory和配置的两步作业的示例 StepBuilderFactory。

@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开箱即用的是为您提供的。本节介绍配置您自己的内容。

如前所述,JobRepository用于Spring Batch中各种持久化域对象(例如JobExecution和)的 基本CRUD操作 StepExecution。它是由许多主要的框架功能要求,如JobLauncher, Job和Step。

使用Java配置时,JobRepository会为您提供a 。如果提供了a,DataSource则直接提供Map基于JDBC的JDBC ,否则不提供基于JDBC的JDBC 。但是,您可以JobRepository通过BatchConfigurer接口的实现来自定义过孔的 配置。

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();
}
...

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

除了dataSource和transactionManager外,不需要上面列出的配置选项。如果未设置,将使用上面显示的默认值。出于显示目的,上面显示了它们。varchar的最大长度默认为2500,这是示例架构脚本中长VARCHAR列 的长度

JobRepository的事务配置

如果使用名称空间或提供的名称空间FactoryBean,则会在存储库周围自动创建事务建议。这是为了确保批次元数据(包括故障后重新启动所必需的状态)正确保留。如果存储库方法不是事务性的,则框架的行为无法很好地定义。create方法属性中的隔离级别是分别指定的,以确保启动作业时,如果两个进程试图同时启动同一作业,则只有一个成功。该方法的默认隔离级别为SERIALIZABLE,这非常激进:READ_COMMITTED也可以工作;如果两个进程不太可能以这种方式冲突,则READ_UNCOMMITTED会很好。但是,由于 create该方法很短,只要数据库平台支持,SERIALIZED不太可能引起问题。但是,可以重写:

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配置存储库的事务行为也很重要:

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_EXECUTION和BATCH_STEP_EXECUTION是两个示例。但是,存在修改此前缀的潜在原因。如果模式名称需要在表名称之前,或者在同一模式内需要多个元数据表,则需要更改表前缀:

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批处理提供了作业存储库的内存Map版本:

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。因此,只要需要这些功能,就可以使用存储库的数据库版本。

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

存储库中的非标准数据库类型
如果使用的数据库平台不在受支持的平台列表中,并且SQL变量足够接近,则可以使用一种受支持的类型。为此,您可以使用raw JobRepositoryFactoryBean代替名称空间快捷方式,并使用它来将数据库类型设置为最接近的匹配项:

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框架的标准实现)。

如果甚至不起作用,或者您没有使用RDBMS,那么唯一的选择可能是实现Dao 所SimpleJobRepository依赖的各种接口,并以正常的Spring方式手动将其连接起来。

配置JobLauncher

使用时@EnableBatchProcessing,JobRegistry开箱即用的是为您提供的。本节介绍配置您自己的内容。

JobLauncher接口的最基本实现 是 SimpleJobLauncher。它唯一需要的依赖项是JobRepository,以获得执行:

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,它将被传递给Job的execute方法,最终将其返回 JobExecution给调用者:

作业启动器序列

在这里插入图片描述

图2. Job Launcher序列
该序列很简单,从调度程序启动时效果很好。但是,尝试从HTTP请求启动时会出现问题。在这种情况下,启动需要异步完成,以便SimpleJobLauncher立即返回到其调用方。这是因为在长时间运行的进程(例如批处理)所需的时间内保持打开HTTP请求的时间不是一种好习惯。下面是一个示例序列:

异步作业启动器序列

图3.异步作业启动器序列
将SimpleJobLauncher可以很容易地配置为允许这种情况下通过配置 TaskExecutor:

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

spring TaskExecutor 接口的任何实现都可以用来控制如何异步执行作业。

运行工作

至少,启动批处理作业需要两件事: Job要启动的和 JobLauncher。两者都可以包含在相同或不同的上下文中。例如,如果从命令行启动作业,则将为每个作业实例化一个新的JVM,因此每个作业都有自己的JobLauncher。但是,如果从范围内的Web容器中运行 HttpRequest,通常将有一个 JobLauncher配置为异步作业启动的应用程序,将调用多个请求来启动其作业。

从命令行运行作业

对于想要从企业计划程序运行其作业的用户,命令行是主要界面。这是因为大多数调度程序(除非是Quartz,否则除非使用NativeJob)都是直接与操作系统进程一起工作的,这些进程主要是由Shell脚本启动的。除了Shell脚本(例如Perl,Ruby)甚至“构建工具”(例如ant或maven)之外,还有许多启动Java进程的方法。但是,由于大多数人都熟悉Shell脚本,因此本示例将重点介绍它们。

CommandLineJobRunner

因为启动作业的脚本必须启动Java虚拟机,所以需要一个具有main方法的类作为主要入口点。Spring Batch提供了一个实现此目的的实现: CommandLineJobRunner。重要的是要注意,这只是引导您的应用程序的一种方法,但是有许多方法可以启动Java进程,并且绝对不应将此类视为权威。在CommandLineJobRunner 执行四项任务:

  • 加载适当的 ApplicationContext
  • 将命令行参数解析为 JobParameters
  • 根据参数找到合适的工作

使用JobLauncher应用程序上下文中提供的启动工作。

所有这些任务仅使用传入的参数即可完成。以下是必填参数:

表1. CommandLineJobRunner参数

jobPath将用于创建XML文件的XML文件的位置ApplicationContext。该文件应包含运行完整作业所需的所有内容
jobName要运行的作业的名称。

这些参数必须首先以路径传递,然后以名称传递。这些之后的所有参数都被认为是 JobParameters并且必须采用’name = value’的格式:

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

在大多数情况下,您可能希望使用清单文件在jar中声明您的主类,但为简单起见,直接使用了该类。此示例使用domainLanguageOfBatch中的相同“ EndOfDay”示例。第一个参数是“ io.spring.EndOfDayJobConfiguration”,它是包含Job的配置类的完全限定类名。第二个参数’endOfDay’表示作业名称。最后一个参数’schedule.date(date)= 2007/05/05’将转换为JobParameters。以下是Java配置的示例:

@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: Job和 两个主要要求。 JobLauncher

退出码

从命令行启动批处理作业时,通常使用企业计划程序。大多数调度程序都相当笨拙,只能在过程级别上工作。这意味着他们只知道他们正在调用的某些操作系统进程,例如shell脚本。在这种情况下,向作业人员传达关于作业成功或失败的唯一方法是通过返回码。返回码是由进程返回到调度程序的数字,指示运行结果。在最简单的情况下:0是成功,1是失败。但是,可能会有更复杂的情况:如果作业A返回4个启动作业B,并且如果作业5返回5个启动作业C。这种类型的行为是在调度程序级别配置的,但是重要的是,诸如Spring Batch之类的处理框架必须提供一种方法来返回特定批处理作业的“退出代码”的数字表示形式。在Spring Batch中,它封装在ExitStatus,这将在第5章中更详细地介绍。为了讨论退出代码,要知道的唯一重要的事情是,它 ExitStatus具有退出代码属性,该属性由框架(或开发人员)设置,并作为一部分返回。从 JobExecution退回的款项 JobLauncher。该 CommandLineJobRunner转换此字符串值使用了一些ExitCodeMapper 接口:

public interface ExitCodeMapper {

    public int intValue(String exitCode);

}

an的基本约定 ExitCodeMapper是,给定字符串退出代码,将返回数字表示形式。作业运行程序使用的默认实现是,SimpleJvmExitCodeMapper 它返回0表示完成,返回1表示一般错误,返回2表示任何作业运行程序错误,例如无法Job在提供的上下文中找到 。如果需要比上述3个值更复杂的东西,则ExitCodeMapper必须提供该接口的自定义实现。由于the CommandLineJobRunner是创建的类,ApplicationContext因此无法“连接在一起”,因此必须自动连接任何需要覆盖的值。这意味着,如果 ExitCodeMapper在BeanFactory,它将在创建上下文后注入到运行器中。提供您自己的所有操作, ExitCodeMapper就是将实现声明为根级Bean,并确保它是运行ApplicationContext程序加载的Bean的一部分 。

从Web容器中运行作业

从历史上看,如上所述,已从命令行启动了诸如批处理作业之类的脱机处理。但是,在许多情况下,从中启动HttpRequest是更好的选择。许多此类用例包括报告,临时作业运行和Web应用程序支持。因为按定义,批处理作业是长期运行的,所以最重要的问题是确保异步启动该作业:

Web容器中的异步作业启动器序列
图4. Web容器中的异步作业启动器序列

在这种情况下,控制器是Spring MVC控制器。关于Spring MVC的更多信息可以在这里找到:https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc。控制器Job使用 JobLauncher已配置为异步启动的来启动 ,然后立即返回JobExecution。在 Job可能仍在运行,但是,这种无阻塞行为允许控制器立即返回,该处理的时候需要HttpRequest。下面是一个示例:

@Controller
public class JobLauncherController {

    @Autowired
    JobLauncher jobLauncher;

    @Autowired
    Job job;

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

高级元数据使用
到目前为止,已经讨论了JobLauncher和JobRepository接口。它们一起代表了作业的简单启动和批处理域对象的基本CRUD操作:

作业库

在这里插入图片描述
图5.作业存储库

A JobLauncher使用 JobRepository创建新 JobExecution对象并运行它们。 Job并且Step以后的实现会将相同的代码JobRepository用于在Job运行期间对相同执行的基本更新。基本操作足以满足简单的场景,但是在具有成百上千个批处理作业和复杂的调度要求的大型批处理环境中,需要对元数据进行更高级的访问:

在这里插入图片描述
图6.高级作业存储库访问

下面讨论JobExplorer和 JobOperator接口,用于查询和控制元数据添加附加功能。

查询存储库

在使用任何高级功能之前,最基本的需求是能够查询存储库中现有的执行情况。该功能由JobExplorer接口提供:

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,可以通过工厂bean轻松配置:

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();
}

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

在本章的前面,提到了JobRepository可以修改表的前缀 以允许使用不同的版本或模式。由于的 JobExplorer使用相同的表,它也需要设置前缀的能力:

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();
}
...
<bean id="jobExplorer" class="org.spr...JobExplorerFactoryBean"
		p:tablePrefix="SYSTEM."/>

JobRegistry

一个JobRegistry(和它的父接口JobLocator)是不是强制性的,但如果你想跟踪哪些作业的情况下可用它可能是有用的。当作业已在其他位置(例如,在子上下文中)创建时,对于在应用程序上下文中集中收集作业也很有用。定制JobRegistry实现也可以用于操纵已注册作业的名称和其他属性。框架仅提供一种实现,该实现基于从作业名称到作业实例的简单映射。

使用时@EnableBatchProcessing,JobRegistry开箱即用的是为您提供的。如果要配置自己的:

...
// 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();
}
...
<bean id="jobRegistry" class="org.springframework.batch.core.configuration.support.MapJobRegistry" />

有两种JobRegistry自动填充方法:使用bean后处理器和使用注册商生命周期组件。以下各节将介绍这两种机制。

JobRegistryBeanPostProcessor
这是一个bean后处理器,可以在创建所有作业时注册它们:

Java配置
@Bean
public JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor() {
    JobRegistryBeanPostProcessor postProcessor = new JobRegistryBeanPostProcessor();
    postProcessor.setJobRegistry(jobRegistry());
    return postProcessor;
}
XML配置
<bean id="jobRegistryBeanPostProcessor" class="org.spr...JobRegistryBeanPostProcessor">
    <property name="jobRegistry" ref="jobRegistry"/>
</bean>

尽管不是严格必需的,但是示例中的后处理器已被赋予一个ID,以便可以将其包含在子上下文中(例如,作为父bean定义),并使在那里创建的所有作业也自动注册。

自动工作注册器

这是一个生命周期组件,可创建子上下文并在创建这些上下文时注册这些上下文。这样做的优点之一是,尽管子上下文中的作业名称在注册表中仍必须是全局唯一的,但它们的依存关系可以具有“自然”名称。因此,例如,您可以创建一组XML配置文件,每个XML配置文件仅具有一个Job,但是都具有具有ItemReader相同bean名称(例如“ reader”)的的不同定义 。如果将所有这些文件都导入到同一上下文中,那么阅读器定义将相互冲突并覆盖,但是使用自动注册器可以避免这种情况。这使得集成由应用程序的单独模块贡献的作业变得更加容易。

Java配置
@Bean
public AutomaticJobRegistrar registrar() {

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

}

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>

注册服务商具有两个必填属性,一个是的数组 ApplicationContextFactory(在此是从便利的工厂bean创建的),另一个是 JobLoader。该JobLoader 负责管理孩子上下文的生命周期和注册的工作JobRegistry。

该ApplicationContextFactory负责创建子上下文和最常见的用法是如上使用 ClassPathXmlApplicationContextFactory。此工厂的功能之一是,默认情况下,它将某些配置从父上下文复制到子级。因此,例如PropertyPlaceholderConfigurer,如果子项或AOP配置应与父项相同,则不必重新定义 它。

所述AutomaticJobRegistrar可以结合使用与JobRegistryBeanPostProcessor 如果需要的话(只要DefaultJobLoader被用作孔)。例如,如果在主要父级上下文以及子级位置中定义了作业,则可能需要这样做。

JobOperator

如前所述,JobRepository 提供对元数据的CRUD操作,并JobExplorer提供对元数据的 只读操作。但是,与批处理操作员通常一起使用时,这些操作一起用于执行常见的监视任务(如停止,重新启动或汇总作业)时最有用。Spring Batch通过JobOperator接口提供了以下类型的操作 :

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, JobExplorer,和 JobRegistry。为此,所提供的实施JobOperator, SimpleJobOperator具有很多依赖:

 /**
  * 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) {

	SimpleJobOperator jobOperator = new SimpleJobOperator();

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

	return jobOperator;
 }
XML:
<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>

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

JobParametersIncrementer

上的大多数方法JobOperator都是不言自明的,可以在接口的javadoc上找到更详细的说明 。但是,该 startNextInstance方法值得注意。此方法将始终启动Job的新实例。如果a中存在严重问题,JobExecution并且需要从头开始重新启动作业,这将非常有用 。JobLauncher不过,与之不同的是,如果参数与先前的任何参数集不同 ,它都需要一个新JobParameters对象来触发新 对象 JobInstance,该 startNextInstance方法将使用 JobParametersIncrementer绑定到来 Job将强制Job到新实例:

public interface JobParametersIncrementer {

    JobParameters getNext(JobParameters parameters);

}

的约定JobParametersIncrementer是,给定一个JobParameters 对象,它将通过增加可能包含的任何必要值来返回“下一个” JobParameters对象。该策略很有用,因为该框架无法知道JobParameters对其进行“下一个”实例的更改。例如,如果其中唯一的值 JobParameters是日期,并且应该创建下一个实例,那么该值应该增加一天吗?还是一周(例如,如果工作是每周一次)?对于有助于识别Job的任何数值,可以这样说,如下所示:

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之前从未运行过,因此可以返回其初始状态。但是,如果不是,则将获得旧值,将其增加一并返回。

增量器可以通过incrementer构建器中提供的方法与“作业”相关联:

@Bean
public Job footballJob() {
    return this.jobBuilderFactory.get("footballJob")
    				 .incrementer(sampleIncrementer())
    				 ...
                     .build();
}

停止工作

的最常见用例之一 JobOperator是正常停止Job:

Set executions = jobOperator.getRunningExecutions(“sampleJob”);
jobOperator.stop(executions.iterator().next());
关闭不是立即关闭的,因为没有办法强制立即关闭,特别是如果当前执行是在框架无法控制的开发人员代码中,例如业务服务。但是,一旦控制权返回到框架,它将把当前状态设置 StepExecution为 BatchStatus.STOPPED,保存它,然后对JobExecution结束之前进行相同的操作。

中止工作

FAILED可以重新启动的作业执行(如果可以重新Job启动)。状态为的作业执行 ABANDONED不会被框架重新启动。该ABANDONED状态还用于步骤执行中,以在重新启动的作业执行中将其标记为可跳过:如果作业正在执行并且遇到ABANDONED在先前的失败作业执行中已标记的步骤, 它将继续进行下一步(确定作业流程定义和步骤执行退出状态)。

如果该进程终止("kill -9"或服务器故障),则该作业当然不会运行,但是JobRepository无法知道,因为在进程终止之前没有人告诉过它。您必须手动告诉它,您知道执行失败或应被视为中止(将其状态更改为 FAILED或ABANDONED)-这是一项业务决策,无法自动执行。仅FAILED当不可重启或知道重启数据有效时,才将状态更改为。Spring Batch Admin中有一个实用程序JobService来中止作业执行。

版本4.2.4.RELEASE 最后更新时间2020-06-11 07:52:24 UTC

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值