高级元数据使用

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

作业存储库
图 1.作业存储库

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

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

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

查询存储库

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

public interface JobExplorer {

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

    JobExecution getJobExecution(Long executionId);

    StepExecution getStepExecution(Long jobExecutionId, Long stepExecutionId);

    JobInstance getJobInstance(Long instanceId);

    List<JobExecution> getJobExecutions(JobInstance jobInstance);

    Set<JobExecution> findRunningJobExecutions(String jobName);
}

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

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

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

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

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

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

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

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

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

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

作业注册表

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

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

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

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

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

您可以填充JobRegistry通过以下方式之一:通过使用 Bean 后处理器,或者通过使用智能初始化单例或使用 注册商生命周期组件。接下来的部分将介绍这些机制。spring-doc.cadn.net.cn

JobRegistryBean后处理器

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

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

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

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

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

虽然不是绝对必要的,但 示例已给出一个id这样就可以包含在子 上下文(例如,作为父 Bean 定义)并导致创建的所有作业 那里也会自动注册。spring-doc.cadn.net.cn

折旧

从 5.2 版本开始,JobRegistryBeanPostProcessor类被弃用,取而代之的是JobRegistrySmartInitializingSingleton,请参阅 JobRegistrySmartInitializingSingletonspring-doc.cadn.net.cn

JobRegistrySmart初始化单例

这是一个SmartInitializingSingleton在作业注册表中注册所有单例作业。spring-doc.cadn.net.cn

以下示例显示如何定义JobRegistrySmartInitializingSingleton在 Java 中:spring-doc.cadn.net.cn

Java 配置
@Bean
public JobRegistrySmartInitializingSingleton jobRegistrySmartInitializingSingleton(JobRegistry jobRegistry) {
    return new JobRegistrySmartInitializingSingleton(jobRegistry);
}

以下示例显示如何定义JobRegistrySmartInitializingSingleton在 XML 中:spring-doc.cadn.net.cn

XML 配置
<bean class="org.springframework.batch.core.configuration.support.JobRegistrySmartInitializingSingleton">
    <property name="jobRegistry" ref="jobRegistry" />
</bean>

自动作业注册商

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

以下示例演示了如何包含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;

}

以下示例演示了如何包含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>

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

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

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

作业运算符

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

public interface JobOperator {

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

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

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

    String getParameters(long executionId) throws NoSuchJobExecutionException;

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

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

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

    boolean stop(long executionId)
          throws NoSuchJobExecutionException, JobExecutionNotRunningException;

    String getSummary(long executionId) throws NoSuchJobExecutionException;

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

    Set<String> getJobNames();

}

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

以下示例显示了SimpleJobOperator在 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;
 }

以下示例显示了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>

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

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

JobParameters递增器

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

public interface JobParametersIncrementer {

    JobParameters getNext(JobParameters parameters);

}

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

public class SampleIncrementer implements JobParametersIncrementer {

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

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

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

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

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

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

停止作业

最常见的用例之一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 也用于步骤executions 以将它们标记为在重新启动的作业执行中可跳过。如果作业正在运行并遇到已标记的步骤ABANDONED在上一个失败的作业执行中,它继续执行下一步(由作业流定义和步骤执行退出状态确定)。spring-doc.cadn.net.cn

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