对于最新稳定版本,请使用 Spring Batch 文档 6.0.3spring-doc.cadn.net.cn

运行任务

至少,启动一个批处理作业需要两件事:要启动的Job和一个JobLauncher。两者可以包含在同一个上下文中,也可以包含在不同的上下文中。例如,如果您从命令行启动作业,则每个Job都会实例化一个新的 JVM。因此,每个作业都有自己独立的JobLauncher。然而,如果您在位于HttpRequest作用域内的 Web 容器中运行,则通常只有一个JobLauncher(配置用于异步启动作业),多个请求会调用它来启动各自的作业。spring-doc.cadn.net.cn

从命令行运行作业

如果您想从企业级调度器运行作业,命令行是主要接口。这是因为大多数调度器(除了 Quartz,除非使用 NativeJob)直接操作系统进程,通常通过 shell 脚本启动。除了 shell 脚本外,还有许多方式可以启动 Java 进程,例如使用 Perl、Ruby,甚至构建工具如 Ant 或 Maven。然而,由于大多数人熟悉 shell 脚本,本示例将重点介绍它们。spring-doc.cadn.net.cn

CommandLineJobRunner

由于启动作业的脚本必须启动一个 Java 虚拟机,因此需要一个包含 main 方法的类作为主要入口点。Spring Batch 提供了一个实现来满足此目的:CommandLineJobRunner。请注意,这只是引导应用程序的一种方式。启动 Java 进程的方法有很多,绝不应将此类视为唯一标准。CommandLineJobRunner 执行以下四项任务:spring-doc.cadn.net.cn

所有这些任务仅通过传入的参数即可完成。 下表描述了所需的参数:spring-doc.cadn.net.cn

表 1. CommandLineJobRunner 参数

jobPathspring-doc.cadn.net.cn

用于创建ApplicationContext的 XML 文件位置。该文件应包含运行完整Job所需的所有内容。spring-doc.cadn.net.cn

jobNamespring-doc.cadn.net.cn

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

这些参数必须传入,首先是路径,其次是名称。在此之后的所有参数都被视为作业参数,会被转换为 JobParameters 对象,并且必须符合 name=value 格式。spring-doc.cadn.net.cn

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

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

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

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

默认情况下,CommandLineJobRunner 使用 DefaultJobParametersConverter 隐式地将键/值对转换为标识性作业参数。但是,您可以通过分别后缀 truefalse 来显式指定哪些作业参数是标识性的,哪些不是。spring-doc.cadn.net.cn

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

<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 来覆盖此行为。spring-doc.cadn.net.cn

在大多数情况下,您会希望使用清单文件在 jar 中声明您的 main 类。然而,为了简化,此处直接使用了该类。本示例采用了来自 批处理的领域语言EndOfDay 示例。第一个参数是 io.spring.EndOfDayJobConfiguration,即包含作业(Job)的配置类的完全限定类名。第二个参数 endOfDay 代表作业名称。最后一个参数 schedule.date=2007-05-05,java.time.LocalDate 将被转换为类型为 java.time.LocalDateJobParameter 对象。spring-doc.cadn.net.cn

以下示例展示了在 Java 中为 endOfDay 配置的样例:spring-doc.cadn.net.cn

@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 对象。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.TaskExecutorJobLauncher" />

前面的示例过于简单,因为在 Spring Batch 中运行批处理作业通常还有许多其他要求,但它足以展示 CommandLineJobRunner 的两个主要需求:JobJobLauncherspring-doc.cadn.net.cn

退出代码

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

public interface ExitCodeMapper {

    public int intValue(String exitCode);

}

ExitCodeMapper 的核心契约是:给定一个字符串形式的退出码,将返回其数字表示。作业运行器使用的默认实现是 SimpleJvmExitCodeMapper,它在完成时返回 0,在发生通用错误时返回 1,在发生任何作业运行器错误(例如在提供的上下文中找不到 Job)时返回 2。如果需要比上述三个值更复杂的内容,则必须提供 ExitCodeMapper 接口的自定义实现。因为 CommandLineJobRunner 是创建 ApplicationContext 的类,因此无法被“装配在一起”,任何需要被覆盖的值都必须通过自动装配注入。这意味着如果在 BeanFactory 中找到了 ExitCodeMapper 的实现, 它将在上下文创建后被注入到运行器中。要提供您自己的ExitCodeMapper,所需做的全部工作就是将该实现声明为根级别的 Bean,并确保它是运行器加载的ApplicationContext的一部分。spring-doc.cadn.net.cn

在 Web 容器内运行作业

历史上,离线处理(例如批处理作业)如前所述是从命令行启动的。然而,在许多情况下,从 HttpRequest 启动是更好的选择。许多此类用例包括报表生成、临时作业运行以及 Web 应用程序支持。由于批处理作业(根据其定义)运行时间较长,因此最重要的关注点是异步启动该作业:spring-doc.cadn.net.cn

Async Job Launcher Sequence from web container
图 1. 来自 Web 容器的异步作业Starters序列

此情况下的控制器是一个 Spring MVC 控制器。有关 Spring MVC 的更多信息,请参阅 Spring Framework 参考指南。 该控制器通过使用已配置为 异步 启动的 JobLauncher 来启动一个 Job,它会立即返回一个 JobExecutionJob 可能仍在运行。然而,这种非阻塞行为允许控制器立即返回,这在处理 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());
    }
}