配置和运行作业
配置和运行作业
在 domain 部分 ,总体 讨论了架构设计,使用下图作为 指导:

虽然Job
object 可能看起来像一个简单的
容器中,有许多配置选项,其中
开发人员必须知道。此外,还有许多考虑因素
如何Job
将运行,以及其元数据将如何
在该运行期间存储。本章将解释各种配置
options 和 runtime 关注点Job
.
配置 Job
有多种Job
接口。然而
构建器抽象出配置中的差异。
@Bean
public Job footballJob() {
return this.jobBuilderFactory.get("footballJob")
.start(playerLoad())
.next(gameLoad())
.next(playerSummarization())
.build();
}
一个Job
(通常为任何Step
)需要一个JobRepository
.这
的配置JobRepository
通过BatchConfigurer
.
上面的示例说明了Job
由三个Step
实例。工作相关
构建器还可以包含其他有助于并行化的元素 (Split
),
声明式流控制 (Decision
) 和流定义外部化 (Flow
).
无论您使用 Java 还是 XML,都有Job
接口。但是,命名空间抽象出了配置中的差异。它有
只有三个必需的依赖项:名称、JobRepository
和Step
实例。
<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' 的存储库,该 是合理的默认值。但是,这可以显式覆盖:
<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/>
).
可重启性
执行批处理作业时的一个关键问题涉及Job
当它是
重新 启动。推出Job
如果JobExecution
已存在特定的JobInstance
.理想情况下,所有作业都应该能够启动
从他们离开的地方开始,但在某些情况下这是不可能的。是的
完全由开发人员来确保新的JobInstance
在此中创建
场景。但是,Spring Batch 确实提供了一些帮助。如果Job
永远不应该是
重新启动,但应始终作为新的JobInstance
,则
restartable 属性可以设置为 'false'。
以下示例演示如何设置restartable
field 设置为false
在 XML 中:
<job id="footballJob" restartable="false">
...
</job>
以下示例演示如何设置restartable
field 设置为false
在 Java 中:
@Bean
public Job footballJob() {
return this.jobBuilderFactory.get("footballJob")
.preventRestart()
...
.build();
}
换句话说,将 restartable 设置为 false 意味着 “thisJob
不支持再次启动”。重新启动Job
那不是
restartable 会导致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
第一次对于 non restartable
job 不会造成任何问题。然而,第二个
attempt 将抛出一个JobRestartException
.
拦截任务执行
在执行
工作,通知各种可能很有用
事件,以便可以执行自定义代码。这SimpleJob
允许通过调用JobListener
在适当的时候:
public interface JobExecutionListener {
void beforeJob(JobExecution jobExecution);
void afterJob(JobExecution jobExecution);
}
JobListeners
可以添加到SimpleJob
通过在作业上设置 listeners 来执行。
以下示例演示如何将侦听器元素添加到 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 作业定义中:
@Bean
public Job footballJob() {
return this.jobBuilderFactory.get("footballJob")
.listener(sampleListener())
...
.build();
}
应该注意的是,afterJob
无论成功与
失败Job
.如果需要确定成功或失败,可以获取
从JobExecution
如下:
public void afterJob(JobExecution jobExecution){
if (jobExecution.getStatus() == BatchStatus.COMPLETED ) {
//job success
}
else if (jobExecution.getStatus() == BatchStatus.FAILED) {
//job failure
}
}
与此接口对应的注解是:
-
@BeforeJob
-
@AfterJob
从父作业继承
如果一组 Job 具有相似但不是
相同,则定义 “parent” 可能会有所帮助Job
从中得到混凝土
Job 可以继承 properties。与 class 相似
继承,即 “child”Job
将合并
它的元素和属性与父级的 Elements 和 Attributes 一起使用。
在下面的示例中,“baseJob” 是一个抽象Job
定义,仅定义
听众。这Job
“job1” 是一个具体的
从 “baseJob” 继承侦听器列表并合并的定义
it 使用自己的侦听器列表来生成Job
具有 2 个侦听器和 1 个Step
、“step1”中。
<job id="baseJob" abstract="true">
<listeners>
<listener ref="listenerOne"/>
<listeners>
</job>
<job id="job1" parent="baseJob">
<step id="step1" parent="standaloneStep"/>
<listeners merge="true">
<listener ref="listenerTwo"/>
<listeners>
</job>
有关更多详细信息,请参阅从父步骤继承部分。
JobParametersValidator (作业参数验证器)
在 XML 命名空间中声明的作业或使用AbstractJob
可以选择性地为 Job 参数声明一个验证器
运行。例如,当您需要断言作业
启动时,使用其所有必需参数。有一个DefaultJobParametersValidator
可用于约束组合
简单的强制和可选参数,以及更复杂的
constraints 中,您可以自行实现接口。
验证器的配置是通过 XML 命名空间通过子 元素,如以下示例所示:
<job id="job1" parent="baseJob3">
<step id="step1" parent="standaloneStep"/>
<validator ref="parametersValidator"/>
</job>
验证器可以指定为引用(如前所述)或嵌套 bean 定义。
验证器的配置通过 java 构建器支持,如 以下示例:
@Bean
public Job job1() {
return this.jobBuilderFactory.get("job1")
.validator(parametersValidator())
...
.build();
}
Java 配置
Spring 3 带来了通过 Java 而不是 XML 配置应用程序的能力。截至
Spring Batch 2.2.0 中,可以使用相同的 java 配置来配置批处理作业。
基于 java 的配置有两个组件:@EnableBatchProcessing
annotation 和 two builders。
这@EnableBatchProcessing
的工作方式@Enable类似于
Spring 家族。在这种情况下,@EnableBatchProcessing
提供
构建批处理作业。在此基本配置中,StepScope
是
除了许多可用于自动装配的 bean 之外,还创建了:
-
JobRepository
:Bean 名称 “jobRepository” -
JobLauncher
: bean 名称 “jobLauncher” -
JobRegistry
: Bean 名称 “jobRegistry” -
PlatformTransactionManager
: Bean 名称 “transactionManager” -
JobBuilderFactory
:bean 名称 “jobBuilders” -
StepBuilderFactory
:Bean 名称 “stepBuilders”
此配置的核心接口是BatchConfigurer
.默认的
implementation 提供了上面提到的 bean,并且需要一个DataSource
作为 Bean
在要提供的上下文中。此数据源由 JobRepository 使用。
您可以自定义这些 bean 中的任何一个
通过创建BatchConfigurer
接口。
通常,扩展DefaultBatchConfigurer
(如果BatchConfigurer
未找到),并且覆盖所需的 getter 就足够了。
但是,可能需要从头开始实现自己的 API。以下内容
示例展示了如何提供自定义事务管理器:
@Bean
public BatchConfigurer batchConfigurer(DataSource dataSource) {
return new DefaultBatchConfigurer(dataSource) {
@Override
public PlatformTransactionManager getTransactionManager() {
return new MyTransactionManager();
}
};
}
只有一个配置类需要具有 |
有了基本配置,用户就可以使用提供的构建器工厂来
配置作业。以下示例显示了一个配置了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
用于各种持久化
domain 对象,例如JobExecution
和StepExecution
.许多主要都需要它
框架功能,例如JobLauncher
,Job
和Step
.
batch 命名空间抽象出JobRepository
实施及其协作者。但是,仍然有一些
可用的配置选项,如以下示例所示:
<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
示例架构中的列
脚本。
使用 java 配置时,一个JobRepository
为您提供。基于 JDBC 的 API 是
如果DataSource
,则Map
如果没有,则基于一个。然而
您可以自定义JobRepository
通过BatchConfigurer
接口。
...
// 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
示例架构脚本中的列
JobRepository 的事务配置
如果命名空间或提供的FactoryBean
,交易建议是
围绕存储库自动创建。这是为了确保批量元数据
包括失败后重新启动所需的状态将正确保留。
如果存储库方法不是
事务。的create*
method attributes 的
以确保在启动作业时,如果两个进程尝试启动
同一时间执行相同的作业,只有一个成功。该
method 为SERIALIZABLE
,这是相当激进的。READ_COMMITTED
将正常工作
井。READ_UNCOMMITTED
如果两个进程不太可能在此发生冲突,那就很好了
道路。但是,由于对create*
方法相当短,则不太可能SERIALIZED
会导致问题,只要数据库平台支持它。但是,此
可以覆盖。
以下示例演示如何在 XML 中覆盖隔离级别:
<job-repository id="jobRepository"
isolation-level-for-create="REPEATABLE_READ" />
以下示例演示如何在 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 的存储库的事务行为。
以下示例显示如何配置存储库的事务行为 在 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 上。
以下示例显示如何配置存储库的事务行为 在 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
是两个例子。但是,有一些潜在的原因需要修改它
前缀。如果需要将架构名称添加到表名称前面,或者如果有多个
的 meta data tables 集,则表前缀需要
被更改:
以下示例演示如何更改 XML 中的表前缀:
<job-repository id="jobRepository"
table-prefix="SYSTEM.TEST_" />
以下示例演示如何在 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
.
只有表前缀是可配置的。table 和 column name 不是。 |
内存存储库
在某些情况下,您可能不希望将域对象持久化到
数据库。一个原因可能是速度;在每个提交点存储域对象需要额外的
时间。另一个原因可能是您不需要为特定
工作。因此, Spring Batch 提供了内存中的Map
作业的版本
存储 库。
以下示例显示了包含MapJobRepositoryFactoryBean
在 XML 中:
<bean id="jobRepository"
class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean">
<property name="transactionManager" ref="transactionManager"/>
</bean>
以下示例显示了包含MapJobRepositoryFactoryBean
在 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
有用。
这
在应用程序上下文中将嵌入式数据源定义为 bean 后,应选择它
如果您使用 |
存储库中的非标准数据库类型
如果您使用的数据库平台不在受支持平台列表中,则
如果 SQL 变体足够接近,则可能能够使用支持的类型之一。待办事项
this,您可以使用 RAWJobRepositoryFactoryBean
而不是命名空间快捷方式和
使用它来将 Database type (数据库类型) 设置为最接近的匹配项。
以下示例演示如何使用JobRepositoryFactoryBean
设置数据库类型
到 XML 中最接近的匹配项:
<bean id="jobRepository" class="org...JobRepositoryFactoryBean">
<property name="databaseType" value="db2"/>
<property name="dataSource" ref="dataSource"/>
</bean>
以下示例演示如何使用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 Framework 的实现)。
如果连这都不起作用,或者您没有使用 RDBMS,那么
唯一的选项可能是实现各种Dao
接口,该接口的SimpleJobRepository
取决于
on 上,并以正常的 Spring 方式手动连接一个。
配置 JobLauncher
使用@EnableBatchProcessing
一个JobRegistry
为您提供开箱即用的服务。
本节介绍如何配置您自己的。
最基本的JobLauncher
interface 是SimpleJobLauncher
.
它唯一需要的依赖项是JobRepository
,以便执行。
以下示例显示了SimpleJobLauncher
在 XML 中:
<bean id="jobLauncher"
class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
</bean>
以下示例显示了SimpleJobLauncher
在 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
到调用方,如图所示
在下图中:

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

这SimpleJobLauncher
可以通过配置TaskExecutor
.
以下 XML 示例显示了SimpleJobLauncher
配置为立即返回:
<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
配置为立即返回:
@Bean
public JobLauncher jobLauncher() {
SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
jobLauncher.setJobRepository(jobRepository());
jobLauncher.setTaskExecutor(new SimpleAsyncTaskExecutor());
jobLauncher.afterPropertiesSet();
return jobLauncher;
}
spring 的任何实现TaskExecutor
接口可用于控制作业的异步方式
执行。
运行 Job
启动批处理作业至少需要两件事:Job
启动,并且JobLauncher
.两者都可以包含在同一个
context 或不同的上下文。例如,如果从
命令行中,将为每个 Job 实例化一个新的 JVM,因此每个
job 将有自己的JobLauncher
.但是,如果
从 Web 容器内运行的HttpRequest
,通常会有一个JobLauncher
,配置为异步作业
launching,则多个请求将调用该请求来启动其作业。
从命令行运行 Job
对于希望从企业运行其作业的用户 scheduler 中,命令行是主界面。这是因为 大多数调度器(Quartz 除外,除非使用 NativeJob)直接与作系统一起使用 进程,主要由 shell 脚本启动。有很多方法 启动除 shell 脚本之外的 Java 进程,例如 Perl、Ruby 或 甚至是 Ant 或 Maven 等“构建工具”。但是,因为大多数人 熟悉 shell 脚本,本示例将重点介绍它们。
The CommandLineJobRunner
因为启动作业的脚本必须启动 Java
Virtual Machine 中,需要有一个具有 main 方法的类
作为主要入口点。Spring Batch 提供了一个实现
,它的作用就是这个:CommandLineJobRunner
.需要注意的是
这只是引导应用程序的一种方式,但有
启动 Java 进程的方法有很多种,这个类绝不应该是
被视为确定的。这CommandLineJobRunner
执行四项任务:
-
加载适当的
ApplicationContext
-
将命令行参数解析为
JobParameters
-
根据参数找到合适的作业
-
使用
JobLauncher
在 application context 启动作业。
所有这些任务都是仅使用参数完成的 传入。以下是必需的参数:
jobPath (作业路径) |
将用于
创建一个 |
jobName (作业名称) |
要运行的作业的名称。 |
这些参数必须与路径 first 和名称 second 一起传入。所有参数 在这些参数被视为作业参数后,将转换为 JobParameters 对象, 并且必须采用 'name=value' 格式。
以下示例显示了作为作业参数传递给在 XML 中定义的作业的日期:
<bash$ java CommandLineJobRunner endOfDayJob.xml endOfDay schedule.date(date)=2007/05/05
以下示例显示了作为作业参数传递给 Java 中定义的作业的日期:
<bash$ java CommandLineJobRunner io.spring.EndOfDayJobConfiguration endOfDay schedule.date(date)=2007/05/05
默认情况下, 在以下示例中,
这个行为可以通过使用自定义的 |
在大多数情况下,您可能希望使用 manifest 在 jar 中声明您的主类,但是,
为简单起见,该类被直接使用。此示例使用相同的 'EndOfDay'
来自 domainLanguageOfBatch 的示例。第一个
参数是 'endOfDayJob.xml',它是包含Job
.第二个参数 'endOfDay' 表示作业名称。最后一个参数
'schedule.date(date)=2007/05/05')转换为 JobParameters 对象。
以下示例显示了endOfDay
在 XML 中:
<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 配置的示例如下:
以下示例显示了endOfDay
在 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
.
退出代码
从命令行启动批处理作业时,企业
经常使用 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 属性,该属性为
设置,并作为JobExecution
从JobLauncher
.这CommandLineJobRunner
转换此字符串值
替换为一个数字,使用ExitCodeMapper
接口:
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
由
跑步者。
从 Web 容器中运行作业
从历史上看,离线处理(例如批处理作业)一直是
从命令行启动,如上所述。但是,有
在许多情况下,从HttpRequest
是
一个更好的选择。许多此类用例包括报告、临时作业
running 和 Web 应用程序支持。因为根据定义,批处理作业
运行时间长,但最关心的是确保启动
job 异步:

在这种情况下,控制器是 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
interfaces 已被
讨论。它们共同表示 Simple Launch of a Job 和 basic
批处理域对象的 CRUD作:

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

这JobExplorer
和JobOperator
接口,这将进行讨论
下面,添加了用于查询和控制 meta 的附加功能
数据。
查询存储库
在任何高级功能之前,最基本的需求是能够
查询存储库中的现有执行。此功能是
由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:
以下示例显示如何配置JobExplorer
在 XML 中:
<bean id="jobExplorer" class="org.spr...JobExplorerFactoryBean"
p:dataSource-ref="dataSource" />
以下示例显示如何配置JobExplorer
在 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
适用于相同的表,它也需要能够设置前缀。
以下示例显示如何为JobExplorer
在 XML 中:
<bean id="jobExplorer" class="org.spr...JobExplorerFactoryBean"
p:tablePrefix="SYSTEM."/>
以下示例显示如何为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();
}
...
JobRegistry (作业注册表)
一个JobRegistry
(及其父接口JobLocator
) 不是必需的,但可以是
如果要跟踪上下文中可用的作业,则很有用。它也是
在创建作业时,用于在应用程序上下文中集中收集作业
其他位置(例如,在子上下文中)。习惯JobRegistry
implementations 还可以
用于作已注册作业的名称和其他属性。
框架只提供了一个实现,它基于一个简单的
从 Job Name 映射到 Job Instance。
以下示例显示了如何包含JobRegistry
对于在 XML 中定义的作业:
<bean id="jobRegistry" class="org.springframework.batch.core.configuration.support.MapJobRegistry" />
以下示例显示了如何包含JobRegistry
对于在 Java 中定义的作业:
使用@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();
}
...
有两种方法可以填充JobRegistry
自动:使用
Bean 后处理器并使用 registrar 生命周期组件。这些
以下各节介绍了两种机制。
JobRegistryBeanPostProcessor
这是一个 bean 后处理器,可以在创建所有作业时注册它们。
以下示例显示了如何包含JobRegistryBeanPostProcessor
对于作业
在 XML 中定义:
<bean id="jobRegistryBeanPostProcessor" class="org.spr...JobRegistryBeanPostProcessor">
<property name="jobRegistry" ref="jobRegistry"/>
</bean>
以下示例显示了如何包含JobRegistryBeanPostProcessor
对于作业
在 Java 中定义:
@Bean
public JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor() {
JobRegistryBeanPostProcessor postProcessor = new JobRegistryBeanPostProcessor();
postProcessor.setJobRegistry(jobRegistry());
return postProcessor;
}
尽管并非绝对必要,但 example 已被赋予一个 ID,以便它可以包含在 child 中 上下文(例如,作为父 bean 定义)并导致创建所有作业 那里也会自动注册。
AutomaticJobRegistrar
这是一个生命周期组件,用于创建子上下文并从中注册作业
上下文。这样做的一个好处是,虽然
子上下文在 Registry 中仍然必须是全局唯一的,它们的依赖项
可以有 “natural” 名称。例如,您可以创建一组 XML 配置文件
每个 Job 只有一个 Job,但都具有不同的ItemReader
使用
相同的 bean 名称,例如 “reader”。如果所有这些文件都导入到同一上下文中,则
Reader 定义会相互冲突并覆盖,但使用 Automatic
registrar 这是避免的。这使得集成 Job 贡献
应用程序的单独模块。
以下示例显示了如何包含AutomaticJobRegistrar
对于定义的作业
在 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 中:
@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
.
这ApplicationContextFactory
是
负责创建子上下文和最常见的用法
将如上所述,使用ClassPathXmlApplicationContextFactory
.其中之一
这个工厂的特点是,默认情况下它会复制一些
configuration down 从 parent context 到 child。所以对于
实例中,您不必重新定义PropertyPlaceholderConfigurer
或 AOP
配置,如果它应该与
父母。
这AutomaticJobRegistrar
可用于
与JobRegistryBeanPostProcessor
如果需要(只要DefaultJobLoader
是
使用)。例如,如果有工作,这可能是可取的
在主父上下文和子上下文中定义
地点。
JobOperator (作业作员)
如前所述,JobRepository
提供对元数据的 CRUD作,并且JobExplorer
在
元数据。但是,这些作在一起使用时最有用
执行常见监控任务,例如停止、重新启动或
总结 Job,这通常是由 Batch Operator 完成的。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
具有许多依赖项。
以下示例显示了SimpleJobOperator
在 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>
以下示例显示了SimpleJobOperator
在 Java 中:
/**
* 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;
}
如果您在作业存储库上设置了表前缀,请不要忘记在作业资源管理器中也设置它。 |
JobParametersIncrementer (作业参数增量器)
大多数JobOperator
是
不言自明,更详细的解释可以在界面的 Javadoc 上找到。但是,startNextInstance
方法值得注意。这
method 将始终启动 Job 的新实例。
如果JobExecution
和工作
需要从头开始。与JobLauncher
虽然,这需要一个新的JobParameters
对象,该对象将触发新的JobInstance
如果参数不同于
任何先前的参数集,则startNextInstance
方法将使用JobParametersIncrementer
绑定到Job
以强制Job
更改为
新建实例:
public interface JobParametersIncrementer {
JobParameters getNext(JobParameters parameters);
}
的合同JobParametersIncrementer
是
给定一个 JobParameters 对象,它将返回“下一个”JobParameters
object 来增加它可能包含的任何必要值。这
策略很有用,因为框架无法知道
对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
以前从未运行
因此可以返回其初始状态。但是,如果不是,则旧的
value 被获取,递增 1 并返回。
对于以 XML 定义的作业,可以将 Incrementer 与Job
通过
'incrementer' 属性,如下所示:
<job id="footballJob" incrementer="sampleIncrementer">
...
</job>
对于用 Java 定义的作业,可以通过incrementer
方法,如下所示:
@Bean
public Job footballJob() {
return this.jobBuilderFactory.get("footballJob")
.incrementer(sampleIncrementer())
...
.build();
}
停止作业
最常见的用例之一JobOperator
会正常停止
工作:
Set<Long> executions = jobOperator.getRunningExecutions("sampleJob");
jobOperator.stop(executions.iterator().next());
关闭不是立即的,因为没有办法强制
立即关闭,尤其是在执行当前处于
开发人员代码,例如
商业服务。但是,一旦控制权返回到
框架中,它将设置当前StepExecution
自BatchStatus.STOPPED
,保存它,然后执行相同的作
对于JobExecution
在完成之前。
中止作业
任务执行FAILED
可以是
restarted(如果Job
是可重新启动的)。状态为ABANDONED
不会被框架重新启动。
这ABANDONED
status 也用于 STEP 中
执行以在重新启动的任务执行中将其标记为可跳过:如果
作业正在执行并遇到已标记的步骤ABANDONED
在上一个失败的任务执行中,它
将进入下一步(由 Job Flow 定义确定
和步骤执行退出状态)。
如果进程宕机 (kill -9
或 server
failure),则作业当然不会运行,但JobRepository
具有
无法知道,因为在过程结束之前没有人告诉它。你
必须手动告诉它您知道执行失败
或应被视为已中止(将其状态更改为FAILED
或ABANDONED
).这是
一个商业决策,没有办法自动化它。更改
status 设置为FAILED
仅当它是可重新启动的并且您知道重新启动数据有效时。