当前版本仍在开发中,尚不被视为稳定版本。最新稳定版请使用 Spring Batch 文档 6.0.2!

运行 Job

启动一个批处理作业,至少需要两样东西:要启动的 Job,以及一个 JobOperator。这两者既可以位于同一个上下文中,也可以位于不同的上下文中。 例如,如果你从命令行启动作业,那么每个 Job 都会创建一个新的 JVM, 因此每个作业都会拥有自己的 JobOperator。而如果你是在 Web 容器中、 并且处于某个 HttpRequest 的作用域内运行,那么通常会有一个 JobOperator(被配置为异步启动作业),由多个请求共同调用来启动各自的作业。

从命令行运行 Job

如果你希望通过企业级调度器来运行作业,那么命令行通常是最主要的接口。 这是因为大多数调度器(Quartz 除外,除非使用 NativeJob)都是直接与操作系统进程打交道, 而这些进程通常由 shell 脚本触发。除了 shell 脚本之外,启动 Java 进程的方式还有很多, 比如使用 Perl、Ruby,甚至 Ant 或 Maven 这样的构建工具。不过,由于大多数人都更熟悉 shell 脚本, 这里的示例主要围绕它展开。

CommandLineJobOperator

由于启动作业的脚本必须拉起一个 Java 虚拟机,因此需要有一个带有 main 方法的类作为主入口。 Spring Batch 提供了一个专门用于此目的的实现:CommandLineJobOperator。 需要注意的是,这只是应用引导方式中的一种。启动 Java 进程的方法有很多, 因此不应把这个类视为唯一或标准答案。CommandLineJobOperator 主要完成四项任务:

  • 加载合适的 ApplicationContext

  • 将命令行参数解析为 JobParameters

  • 根据参数定位要执行的作业。

  • 使用应用上下文中提供的 JobOperator 来启动作业。

上述所有任务都只依赖传入的参数来完成。 下表说明了必需的参数:

表 1. CommandLineJobOperator 参数

jobClass

用于创建 ApplicationContext 的作业配置类的完整限定名。 这个类应包含运行完整 Job 所需的一切内容,包括 JobOperatorJobRepository,以及一个已经注册好可操作作业的 JobRegistry

operation

要对作业执行的操作名称。可选值包括 [startstartNextInstancestoprestartabandon]

jobName or jobExecutionId

取决于具体操作,它可以是要启动的作业名称,也可以是要停止、重启、放弃或恢复的作业执行 ID。

在启动作业时,后续所有参数都会被视为作业参数,并被转换为一个 JobParameters 对象, 格式必须为 name=value,type,identifying。而在停止、重启、放弃或恢复作业时, 第 4 个参数应当是 jobExecutionId,其余参数都会被忽略。

下面的示例展示了如何把一个日期作为作业参数传给用 Java 定义的作业:

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

默认情况下,CommandLineJobOperator 使用 DefaultJobParametersConverter, 它会把键值对隐式转换为“标识性”的作业参数。不过,你也可以显式指定哪些参数是标识性的、哪些不是, 方法是在参数末尾分别追加 truefalse

在下面的示例中,schedule.date 是标识性作业参数,而 vendor.id 不是:

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

你可以通过在 CommandLineJobOperator 上设置自定义的 JobParametersConverter, 来覆盖这一默认行为。

退出码

当你从命令行启动批处理作业时,通常会配合企业级调度器一起使用。 大多数调度器都比较“简单”,它们只工作在进程级别,也就是说,它们只知道某个操作系统进程 (例如它所调用的 shell 脚本)。在这种情况下,作业成功还是失败,唯一能反馈给调度器的方式就是返回码。 返回码是进程返回给调度器的一个数字,用来表示本次运行结果。最简单的场景里,0 表示成功,1 表示失败。 但也可能存在更复杂的情况,比如“如果作业 A 返回 4,就触发作业 B;如果返回 5,就触发作业 C”。 这种行为通常是在调度器层面配置的,但像 Spring Batch 这样的处理框架必须提供一种机制, 把某个批处理作业的退出结果转换成数字形式返回。在 Spring Batch 中,这一能力由 ExitStatus 封装, 第 5 章会更详细地介绍。就退出码而言,你只需要知道:ExitStatus 有一个 exit code 属性, 它由框架(或开发者)设置,并作为 JobOperator 返回的 JobExecution 的一部分被带回。 CommandLineJobOperator 会通过 ExitCodeMapper 接口把这个字符串值转换成数字:

public interface ExitCodeMapper {

    int intValue(String exitCode);

}

ExitCodeMapper 的核心约定是:给定一个字符串形式的退出码,返回其数字表示。 作业运行器默认使用的实现是 SimpleJvmExitCodeMapper, 它会在作业完成时返回 0,在发生通用错误时返回 1,在出现作业运行器错误 (例如无法在提供的上下文中找到某个 Job)时返回 2。 如果你需要比这三个值更复杂的映射规则,就必须提供一个自定义的 ExitCodeMapper 实现, 并将其设置到 CommandLineJobOperator 上。

在 Web 容器中运行 Job

从历史上看,离线处理任务(例如批处理作业)通常都是通过命令行启动的,前面已经介绍过这一方式。 不过,在很多场景下,从 HttpRequest 发起启动会是更好的选择,例如报表生成、 临时作业执行以及 Web 应用支持等。由于批处理作业天然就是长时间运行的, 这里最重要的关注点就是要以异步方式启动作业:

来自 Web 容器的异步 Job 启动顺序
图 1. 来自 Web 容器的异步 Job 启动顺序

这里的控制器是一个 Spring MVC 控制器。关于 Spring MVC 的更多内容, 可参见 Spring Framework 参考文档。该控制器通过一个已经配置为 异步 启动的 JobOperator 来触发 Job, 它会立即返回一个 JobExecution。此时 Job 很可能仍在运行中。 不过,这种非阻塞行为使控制器能够立刻返回,而这正是处理 HttpRequest 时所需要的。 下面的代码片段给出了一个示例:

@Controller
public class JobOperatorController {

    @Autowired
    JobOperator jobOperator;

    @Autowired
    Job job;

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