Running a Job
至少,启动一个批处理作业需要两件事:要启动的 Job 和 JobLauncher。两者都可以包含在同一个上下文或不同的上下文中。例如,如果你从命令行启动作业,则为每个 Job 实例化一个新的 JVM。因此,每个作业都有自己的 JobLauncher。但是,如果你从 HttpRequest 范围内的 Web 容器内运行,则通常还有一个 JobLauncher(配置用于异步作业启动),多个请求调用该 JobLauncher 来启动它们的作业。
Running Jobs from the Command Line
如果你想从企业计划程序运行作业,则命令行是主要接口。这是因为大多数计划程序(Quartz 除外,除非使用 NativeJob)直接与操作系统进程一起工作,主要是用 shell 脚本启动的。除了 shell 脚本之外,还有许多方法可以启动 Java 进程,例如 Perl、Ruby,甚至构建工具,例如 Ant 或 Maven。然而,由于大多数人熟悉 shell 脚本,所以本示例重点关注它们。
The CommandLineJobRunner
由于启动作业的脚本必须启动 Java Virtual Machine,因此需要有一个带有 main 方法的类充当主要入口点。Spring Batch 提供了一个实现来实现此目的:CommandLineJobRunner。请注意,这只是引导应用程序的一种方式。有许多方法可以启动 Java 进程,此类决不应被视为明确的。CommandLineJobRunner 执行四项任务:
-
Load the appropriate
ApplicationContext. -
将命令行参数解析为
JobParameters。 -
根据参数找到适当的工作。
-
使用应用程序上下文中提供的
JobLauncher来启动作业。
所有这些任务仅使用传入的参数完成。下表描述了必需的参数:
|
用于创建 |
|
要运行作业的名称。 |
必须传入这些参数,先输入路径,然后再输入名称。这些之后的所有参数都被视为作业参数,变为一个 JobParameters 对象,并且必须采用 name=value 格式。
- Java
-
以下示例显示了作为作业参数传递给在 Java 中定义的作业的日期:
<bash$ java CommandLineJobRunner io.spring.EndOfDayJobConfiguration endOfDay schedule.date=2007-05-05,java.time.LocalDate
- XML
-
以下范例显示一个日期传递为作业参数到 XML 中定义的作业:
<bash$ java CommandLineJobRunner endOfDayJob.xml endOfDay schedule.date=2007-05-05,java.time.LocalDate
|
默认情况下,
你可以使用自定义 |
- Java
-
在大多数情况下,你都希望使用清单在 jar 中声明
main类。但是,为简单起见,直接使用了该类。此示例从 The Domain Language of Batch 中使用了EndOfDay示例。第一个参数是io.spring.EndOfDayJobConfiguration,它是包含 Job 的配置类中完全限定的类名称。第二个参数endOfDay表示 job 名称。最终参数schedule.date=2007-05-05,java.time.LocalDate转换为类型java.time.LocalDate的JobParameter对象。 以下范例显示一个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();
}
}
- XML
-
在大多数情况下,你都希望使用清单在 jar 中声明
main类。但是,由于简单起见,直接使用了该类。此示例从 The Domain Language of Batch 中使用了EndOfDay示例。第一个参数是endOfDayJob.xml,它是包含Job的 Spring ApplicationContext。第二个参数endOfDay,表示 job 名称。最终参数schedule.date=2007-05-05,java.time.LocalDate转换为类型为java.time.LocalDate的JobParameter对象。 以下范例显示一个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 的两个主要需求:Job 和 JobLauncher。
Exit Codes
从命令行启动批处理作业的时候,通常会使用企业调度程序。大多数调度程序相当愚蠢,并且仅在进程级别工作。这意味着它们只了解某些操作系统进程(如它们调用的 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 的一部分。
Running Jobs from within a Web Container
从历史上看,离线处理(例如批处理作业)已经从命令行启动,如前所述。然而,在许多情况下,从 HttpRequest 启动是一个更好的选择。许多这样的用例包括报告、即席作业运行和 Web 应用程序支持。因为批处理作业(根据定义)是长时间运行的,所以最重要的考虑是异步地启动作业:
在这种情况下,控制器是 Spring MVC 控制器。有关 Spring MVC 的更多信息,请参阅 Spring 框架参考指南。该控制器使用已配置为启动 asynchronously 的 JobLauncher 启动 Job,从而立即返回 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());
}
}