執行作業

最起碼,啟動一個批處理作業需要兩樣東西:待啟動的 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,它是包含 Job 的配置類的完全限定類名。第二個引數 endOfDay 代表作業名稱。最後一個引數 schedule.date=2007-05-05,java.time.LocalDate 被轉換為型別為 java.time.LocalDateJobParameter 物件。

以下示例展示了 Java 中 endOfDay 的示例配置

@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 物件。

以下示例展示了 XML 中 endOfDay 的示例配置

<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;如果作業 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 Framework 參考指南。控制器使用配置為非同步啟動的 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());
    }
}