运行作业

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

从命令行运行作业

如果您想从企业调度程序运行作业,命令行是主要接口。这是因为大多数调度程序(除了 Quartz,除非使用 `NativeJob`)直接与操作系统进程交互,主要通过 shell 脚本启动。除了 shell 脚本之外,还有很多方法可以启动 Java 进程,例如 Perl、Ruby,甚至构建工具,例如 Ant 或 Maven。但是,由于大多数人熟悉 shell 脚本,因此本示例重点介绍它们。

CommandLineJobRunner

由于启动作业的脚本必须启动一个 Java 虚拟机,因此需要一个包含 `main` 方法的类作为主要入口点。Spring Batch 提供了一个实现来满足此目的:`CommandLineJobRunner`。请注意,这只是启动应用程序的一种方法。启动 Java 进程的方法有很多,这个类绝不应被视为最终的解决方案。`CommandLineJobRunner` 执行四个任务

  • 加载适当的 `ApplicationContext`。

  • 将命令行参数解析为 `JobParameters`。

  • 根据参数定位适当的作业。

  • 使用应用程序上下文提供的 `JobLauncher` 启动作业。

所有这些任务都只需传入的参数即可完成。下表描述了必需的参数

表 1. CommandLineJobRunner 参数

jobPath

用于创建 `ApplicationContext` 的 XML 文件的位置。此文件应包含运行完整 `Job` 所需的一切。

jobName

要运行的作业的名称。

这些参数必须传入,路径在前,名称在后。这些参数之后的所有参数都被视为作业参数,将转换为 `JobParameters` 对象,并且必须采用 `name=value` 的格式。

  • Java

  • XML

以下示例显示将日期作为作业参数传递给在 Java 中定义的作业

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

以下示例显示将日期作为作业参数传递给在 XML 中定义的作业

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

默认情况下,CommandLineJobRunner 使用 DefaultJobParametersConverter,它会隐式地将键值对转换为标识作业参数。但是,您可以通过在参数后缀添加 truefalse 来显式指定哪些作业参数是标识参数,哪些不是。

在以下示例中,schedule.date 是一个标识作业参数,而 vendor.id 不是。

<bash$ java CommandLineJobRunner endOfDayJob.xml endOfDay \
                                 schedule.date=2007-05-05,java.time.LocalDate,true \
                                 vendor.id=123,java.lang.Long,false
<bash$ java CommandLineJobRunner io.spring.EndOfDayJobConfiguration endOfDay \
                                 schedule.date=2007-05-05,java.time.LocalDate,true \
                                 vendor.id=123,java.lang.Long,false

您可以通过使用自定义 JobParametersConverter 来覆盖此行为。

  • Java

  • XML

在大多数情况下,您需要使用清单文件在 jar 中声明您的 main 类。但是,为了简单起见,这里直接使用了该类。此示例使用来自 批处理域语言EndOfDay 示例。第一个参数是 io.spring.EndOfDayJobConfiguration,它是包含作业的配置类的完全限定类名。第二个参数 endOfDay 代表作业名称。最后一个参数 schedule.date=2007-05-05,java.time.LocalDate 被转换为类型为 java.time.LocalDateJobParameter 对象。

以下示例显示了 endOfDay 的 Java 配置示例。

@Configuration
@EnableBatchProcessing
public class EndOfDayJobConfiguration {

    @Bean
    public Job endOfDay(JobRepository jobRepository, Step step1) {
        return new JobBuilder("endOfDay", jobRepository)
    				.start(step1)
    				.build();
    }

    @Bean
    public Step step1(JobRepository jobRepository, PlatformTransactionManager transactionManager) {
        return new StepBuilder("step1", jobRepository)
    				.tasklet((contribution, chunkContext) -> null, transactionManager)
    				.build();
    }
}

在大多数情况下,您需要使用清单文件在 jar 中声明您的 main 类。但是,为了简单起见,这里直接使用了该类。此示例使用来自 批处理域语言EndOfDay 示例。第一个参数是 endOfDayJob.xml,它是包含 Job 的 Spring ApplicationContext。第二个参数 endOfDay 代表作业名称。最后一个参数 schedule.date=2007-05-05,java.time.LocalDate 被转换为类型为 java.time.LocalDateJobParameter 对象。

以下示例显示了 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.TaskExecutorJobLauncher" />

前面的示例过于简单,因为在一般情况下,在 Spring Batch 中运行批处理作业还有许多其他要求,但它展示了 CommandLineJobRunner 的两个主要要求:JobJobLauncher

退出代码

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

public interface ExitCodeMapper {

    public int intValue(String exitCode);

}

ExitCodeMapper 的核心契约是,给定一个字符串退出代码,它将返回一个数字表示。作业运行器使用的默认实现是 SimpleJvmExitCodeMapper,它在完成时返回 0,在一般错误时返回 1,在任何作业运行器错误(例如无法在提供的上下文中找到 Job)时返回 2。如果需要比上面三个值更复杂的内容,则必须提供 ExitCodeMapper 接口的自定义实现。由于 CommandLineJobRunner 是创建 ApplicationContext 的类,因此无法“连接在一起”,因此需要覆盖的任何值都必须自动装配。这意味着如果在 BeanFactory 中找到 ExitCodeMapper 的实现,它将在上下文创建后注入到运行器中。要提供您自己的 ExitCodeMapper,只需将实现声明为根级 bean 并确保它是运行器加载的 ApplicationContext 的一部分。

从 Web 容器中运行作业

从历史上看,离线处理(如批处理作业)是从命令行启动的,如前所述。但是,在许多情况下,从 HttpRequest 启动是更好的选择。许多此类用例包括报告、临时作业运行和 Web 应用程序支持。由于批处理作业(根据定义)是长时间运行的,因此最重要的是异步启动作业。

Async Job Launcher Sequence from web container
图 1. 从 Web 容器异步启动作业的顺序

在这种情况下,控制器是 Spring MVC 控制器。有关 Spring MVC 的更多信息,请参阅 Spring 框架参考指南。控制器通过使用已配置为 异步 启动的 JobLauncher 来启动 Job,该启动器会立即返回 JobExecutionJob 可能仍在运行。但是,这种非阻塞行为允许控制器立即返回,这在处理 HttpRequest 时是必需的。以下清单显示了一个示例

@Controller
public class JobLauncherController {

    @Autowired
    JobLauncher jobLauncher;

    @Autowired
    Job job;

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