控制步驟流程
藉由將步驟群組在一個擁有的 job 中,也產生了控制 job 如何從一個步驟「流動」到另一個步驟的需求。Step
的失敗不一定意味著 Job
應該失敗。此外,可能有多種類型的「成功」來決定接下來應該執行哪個 Step
。根據一組 Steps
的配置方式,某些步驟甚至可能完全不會被處理。
流程定義中的步驟 bean 方法代理
在流程定義中,步驟實例必須是唯一的。當一個步驟在流程定義中有多個結果時,重要的是將同一個步驟實例傳遞給流程定義方法 ( 在以下範例中,步驟以參數形式注入到流程或 job bean 定義方法中。這種依賴注入風格保證了流程定義中步驟的唯一性。然而,如果流程是透過呼叫以 關於 Spring Framework 中 bean 方法代理的更多詳細資訊,請參考 使用 @Configuration 註解 章節。 |
循序流程
最簡單的流程情境是一個 job,其中所有步驟都依序執行,如下圖所示

這可以透過在 step
中使用 next
來實現。
-
Java
-
XML
以下範例展示如何在 Java 中使用 next()
方法
@Bean
public Job job(JobRepository jobRepository, Step stepA, Step stepB, Step stepC) {
return new JobBuilder("job", jobRepository)
.start(stepA)
.next(stepB)
.next(stepC)
.build();
}
以下範例展示如何在 XML 中使用 next
屬性
<job id="job">
<step id="stepA" parent="s1" next="stepB" />
<step id="stepB" parent="s2" next="stepC"/>
<step id="stepC" parent="s3" />
</job>
在上述情境中,stepA
首先執行,因為它是第一個列出的 Step
。如果 stepA
正常完成,則 stepB
執行,依此類推。但是,如果 step A
失敗,則整個 Job
失敗,且 stepB
不會執行。
使用 Spring Batch XML 命名空間,配置中列出的第一個步驟總是 Job 執行的第一個步驟。其他步驟元素的順序並不重要,但第一個步驟必須始終在 XML 中首先出現。 |
條件流程
在前面的範例中,只有兩種可能性
-
step
成功,且應該執行下一個step
。 -
step
失敗,因此,job
應該失敗。
在許多情況下,這可能就足夠了。然而,如果 step
的失敗應該觸發不同的 step
,而不是導致失敗,該怎麼辦? 下圖顯示了這樣一個流程

-
Java
-
XML
Java API 提供了一組流暢的方法,讓您可以指定流程以及步驟失敗時該怎麼做。以下範例展示如何指定一個步驟 (stepA
),然後根據 stepA
是否成功,繼續執行兩個不同的步驟 (stepB
或 stepC
) 之一
@Bean
public Job job(JobRepository jobRepository, Step stepA, Step stepB, Step stepC) {
return new JobBuilder("job", jobRepository)
.start(stepA)
.on("*").to(stepB)
.from(stepA).on("FAILED").to(stepC)
.end()
.build();
}
為了處理更複雜的情境,Spring Batch XML 命名空間允許您在 step 元素中定義 transitions 元素。其中一個 transition 是 next
元素。與 next
屬性類似,next
元素告訴 Job
接下來要執行哪個 Step
。然而,與屬性不同的是,允許在給定的 Step
上使用任意數量的 next
元素,並且在失敗的情況下沒有預設行為。這意味著,如果使用 transition 元素,則必須明確定義 Step
transitions 的所有行為。另請注意,單個步驟不能同時具有 next
屬性和 transition
元素。
next
元素指定要匹配的模式和接下來要執行的步驟,如下例所示
<job id="job">
<step id="stepA" parent="s1">
<next on="*" to="stepB" />
<next on="FAILED" to="stepC" />
</step>
<step id="stepB" parent="s2" next="stepC" />
<step id="stepC" parent="s3" />
</job>
-
Java
-
XML
當使用 Java 配置時,on()
方法使用簡單的模式匹配方案來匹配從 Step
執行產生的 ExitStatus
。
當使用 XML 配置時,transition 元素的 on
屬性使用簡單的模式匹配方案來匹配從 Step
執行產生的 ExitStatus
。
模式中只允許使用兩個特殊字元
-
*
匹配零個或多個字元 -
?
匹配恰好一個字元
例如,c*t
匹配 cat
和 count
,而 c?t
匹配 cat
但不匹配 count
。
雖然 Step
上的 transition 元素數量沒有限制,但如果 Step
執行產生的 ExitStatus
未被元素涵蓋,則框架會拋出異常,且 Job
會失敗。框架會自動將 transitions 從最特定到最不特定排序。這意味著,即使在前面的範例中交換了 stepA
的順序,FAILED
的 ExitStatus
仍然會轉到 stepC
。
Batch Status 與 Exit Status 的比較
在為條件流程配置 Job
時,了解 BatchStatus
和 ExitStatus
之間的區別非常重要。BatchStatus
是一個列舉,它是 JobExecution
和 StepExecution
的屬性,並由框架用於記錄 Job
或 Step
的狀態。它可以是以下值之一:COMPLETED
、STARTING
、STARTED
、STOPPING
、STOPPED
、FAILED
、ABANDONED
或 UNKNOWN
。它們大多數都是不言自明的:COMPLETED
是步驟或 job 成功完成時設定的狀態,FAILED
是在失敗時設定的狀態,依此類推。
-
Java
-
XML
以下範例包含使用 Java 配置時的 on
元素
...
.from(stepA).on("FAILED").to(stepB)
...
以下範例包含使用 XML 配置時的 next
元素
<next on="FAILED" to="stepB" />
乍看之下,on
似乎引用了它所屬的 Step
的 BatchStatus
。然而,它實際上引用了 Step
的 ExitStatus
。顧名思義,ExitStatus
代表 Step
完成執行後的狀態。
-
Java
-
XML
當使用 Java 配置時,前面 Java 配置範例中顯示的 on()
方法引用了 ExitStatus
的退出代碼。
更具體地說,當使用 XML 配置時,前面 XML 配置範例中顯示的 next
元素引用了 ExitStatus
的退出代碼。
用英文來說,它表示:「如果退出代碼為 FAILED,則轉到 stepB」。預設情況下,退出代碼始終與 Step
的 BatchStatus
相同,這就是為什麼前面的條目有效的原因。但是,如果需要不同的退出代碼怎麼辦?一個很好的範例來自 samples 專案中的 skip 範例 job
-
Java
-
XML
以下範例展示如何在 Java 中使用不同的退出代碼
@Bean
public Job job(JobRepository jobRepository, Step step1, Step step2, Step errorPrint1) {
return new JobBuilder("job", jobRepository)
.start(step1).on("FAILED").end()
.from(step1).on("COMPLETED WITH SKIPS").to(errorPrint1)
.from(step1).on("*").to(step2)
.end()
.build();
}
以下範例展示如何在 XML 中使用不同的退出代碼
<step id="step1" parent="s1">
<end on="FAILED" />
<next on="COMPLETED WITH SKIPS" to="errorPrint1" />
<next on="*" to="step2" />
</step>
step1
有三種可能性
-
Step
失敗,在這種情況下,job 應該失敗。 -
Step
成功完成。 -
Step
成功完成,但退出代碼為COMPLETED WITH SKIPS
。在這種情況下,應該執行不同的步驟來處理錯誤。
先前的配置有效。但是,需要根據執行是否跳過記錄的條件來變更退出代碼,如下例所示
public class SkipCheckingListener implements StepExecutionListener {
@Override
public ExitStatus afterStep(StepExecution stepExecution) {
String exitCode = stepExecution.getExitStatus().getExitCode();
if (!exitCode.equals(ExitStatus.FAILED.getExitCode()) &&
stepExecution.getSkipCount() > 0) {
return new ExitStatus("COMPLETED WITH SKIPS");
} else {
return null;
}
}
}
先前的程式碼是一個 StepExecutionListener
,它首先檢查以確保 Step
成功,然後檢查 StepExecution
上的跳過計數是否大於 0。如果兩個條件都滿足,則會傳回退出代碼為 COMPLETED WITH SKIPS
的新 ExitStatus
。
設定停止
在討論 BatchStatus
和 ExitStatus
之後,人們可能會想知道 Job
的 BatchStatus
和 ExitStatus
是如何確定的。雖然這些狀態是由執行的程式碼為 Step
確定的,但 Job
的狀態是根據配置確定的。
到目前為止,討論的所有 job 配置都至少有一個沒有 transitions 的最終 Step
。
-
Java
-
XML
在以下 Java 範例中,在 step
執行後,Job
結束
@Bean
public Job job(JobRepository jobRepository, Step step1) {
return new JobBuilder("job", jobRepository)
.start(step1)
.build();
}
在以下 XML 範例中,在 step
執行後,Job
結束
<step id="step1" parent="s3"/>
如果沒有為 Step
定義 transitions,則 Job
的狀態定義如下
-
如果
Step
以FAILED
的ExitStatus
結束,則Job
的BatchStatus
和ExitStatus
都是FAILED
。 -
否則,
Job
的BatchStatus
和ExitStatus
都是COMPLETED
。
雖然這種終止批次 job 的方法對於某些批次 job 來說已經足夠了,例如簡單的循序步驟 job,但可能需要自訂定義的 job 停止情境。為此,Spring Batch 提供了三個 transition 元素來停止 Job
(除了我們之前討論過的 next
元素 之外)。這些停止元素中的每一個都會以特定的 BatchStatus
停止 Job
。重要的是要注意,停止 transition 元素對 Job
中任何 Steps
的 BatchStatus
或 ExitStatus
都沒有影響。這些元素僅影響 Job
的最終狀態。例如,job 中的每個步驟都可能具有 FAILED
狀態,但 job 可能具有 COMPLETED
狀態。
在步驟結束
配置步驟結束指示 Job
以 COMPLETED
的 BatchStatus
停止。以 COMPLETED
狀態完成的 Job
無法重新啟動(框架會拋出 JobInstanceAlreadyCompleteException
)。
-
Java
-
XML
當使用 Java 配置時,end
方法用於此任務。end
方法還允許使用可選的 exitStatus
參數,您可以使用它來自訂 Job
的 ExitStatus
。如果沒有提供 exitStatus
值,則 ExitStatus
預設為 COMPLETED
,以匹配 BatchStatus
。
當使用 XML 配置時,您可以使用 end
元素來完成此任務。end
元素還允許使用可選的 exit-code
屬性,您可以使用它來自訂 Job
的 ExitStatus
。如果未給定 exit-code
屬性,則 ExitStatus
預設為 COMPLETED
,以匹配 BatchStatus
。
考慮以下情境:如果 step2
失敗,則 Job
以 COMPLETED
的 BatchStatus
和 COMPLETED
的 ExitStatus
停止,且 step3
不會執行。否則,執行將移至 step3
。請注意,如果 step2
失敗,則 Job
不可重新啟動(因為狀態為 COMPLETED
)。
-
Java
-
XML
以下範例顯示 Java 中的情境
@Bean
public Job job(JobRepository jobRepository, Step step1, Step step2, Step step3) {
return new JobBuilder("job", jobRepository)
.start(step1)
.next(step2)
.on("FAILED").end()
.from(step2).on("*").to(step3)
.end()
.build();
}
以下範例顯示 XML 中的情境
<step id="step1" parent="s1" next="step2">
<step id="step2" parent="s2">
<end on="FAILED"/>
<next on="*" to="step3"/>
</step>
<step id="step3" parent="s3">
使步驟失敗
配置步驟在給定點失敗會指示 Job
以 FAILED
的 BatchStatus
停止。與 end 不同,Job
的失敗不會阻止 Job
被重新啟動。
當使用 XML 配置時,fail
元素還允許使用可選的 exit-code
屬性,可用於自訂 Job
的 ExitStatus
。如果未給定 exit-code
屬性,則 ExitStatus
預設為 FAILED
,以匹配 BatchStatus
。
考慮以下情境:如果 step2
失敗,則 Job
以 FAILED
的 BatchStatus
和 EARLY TERMINATION
的 ExitStatus
停止,且 step3
不會執行。否則,執行將移至 step3
。此外,如果 step2
失敗且 Job
重新啟動,則執行將再次從 step2
開始。
-
Java
-
XML
以下範例顯示 Java 中的情境
@Bean
public Job job(JobRepository jobRepository, Step step1, Step step2, Step step3) {
return new JobBuilder("job", jobRepository)
.start(step1)
.next(step2).on("FAILED").fail()
.from(step2).on("*").to(step3)
.end()
.build();
}
以下範例顯示 XML 中的情境
<step id="step1" parent="s1" next="step2">
<step id="step2" parent="s2">
<fail on="FAILED" exit-code="EARLY TERMINATION"/>
<next on="*" to="step3"/>
</step>
<step id="step3" parent="s3">
在給定步驟停止 Job
配置 job 在特定步驟停止會指示 Job
以 STOPPED
的 BatchStatus
停止。停止 Job
可以提供處理過程中的暫時中斷,以便操作員可以在重新啟動 Job
之前採取一些操作。
-
Java
-
XML
當使用 Java 配置時,stopAndRestart
方法需要一個 restart
屬性,該屬性指定在重新啟動 Job 時應從哪個步驟恢復執行。
當使用 XML 配置時,stop
元素需要一個 restart
屬性,該屬性指定在重新啟動 Job
時應從哪個步驟恢復執行。
考慮以下情境:如果 step1
以 COMPLETE
結束,則 job 會停止。一旦重新啟動,執行將從 step2
開始。
-
Java
-
XML
以下範例顯示 Java 中的情境
@Bean
public Job job(JobRepository jobRepository, Step step1, Step step2) {
return new JobBuilder("job", jobRepository)
.start(step1).on("COMPLETED").stopAndRestart(step2)
.end()
.build();
}
以下列表顯示 XML 中的情境
<step id="step1" parent="s1">
<stop on="COMPLETED" restart="step2"/>
</step>
<step id="step2" parent="s2"/>
程式化的流程決策
在某些情況下,可能需要比 ExitStatus
更多的資訊來決定接下來要執行哪個步驟。在這種情況下,可以使用 JobExecutionDecider
來協助決策,如下例所示
public class MyDecider implements JobExecutionDecider {
public FlowExecutionStatus decide(JobExecution jobExecution, StepExecution stepExecution) {
String status;
if (someCondition()) {
status = "FAILED";
}
else {
status = "COMPLETED";
}
return new FlowExecutionStatus(status);
}
}
-
Java
-
XML
在以下範例中,當使用 Java 配置時,實作 JobExecutionDecider
的 bean 會直接傳遞給 next
呼叫
@Bean
public Job job(JobRepository jobRepository, MyDecider decider, Step step1, Step step2, Step step3) {
return new JobBuilder("job", jobRepository)
.start(step1)
.next(decider).on("FAILED").to(step2)
.from(decider).on("COMPLETED").to(step3)
.end()
.build();
}
在以下範例 job 配置中,decision
指定要使用的 decider 以及所有 transitions
<job id="job">
<step id="step1" parent="s1" next="decision" />
<decision id="decision" decider="decider">
<next on="FAILED" to="step2" />
<next on="COMPLETED" to="step3" />
</decision>
<step id="step2" parent="s2" next="step3"/>
<step id="step3" parent="s3" />
</job>
<beans:bean id="decider" class="com.MyDecider"/>
分割流程
到目前為止描述的每個情境都涉及一個 Job
,該 Job
以線性方式一次執行一個步驟。除了這種典型風格之外,Spring Batch 還允許使用平行流程配置 job。
-
Java
-
XML
基於 Java 的配置允許您透過提供的建構器配置 splits。如下例所示,split
元素包含一個或多個 flow
元素,在其中可以定義整個單獨的流程。split
元素還可以包含任何先前討論過的 transition 元素,例如 next
屬性或 next
、end
或 fail
元素。
@Bean
public Flow flow1(Step step1, Step step2) {
return new FlowBuilder<SimpleFlow>("flow1")
.start(step1)
.next(step2)
.build();
}
@Bean
public Flow flow2(Step step3) {
return new FlowBuilder<SimpleFlow>("flow2")
.start(step3)
.build();
}
@Bean
public Job job(JobRepository jobRepository, Flow flow1, Flow flow2, Step step4) {
return new JobBuilder("job", jobRepository)
.start(flow1)
.split(new SimpleAsyncTaskExecutor())
.add(flow2)
.next(step4)
.end()
.build();
}
XML 命名空間允許您使用 split
元素。如下例所示,split
元素包含一個或多個 flow
元素,在其中可以定義整個單獨的流程。split
元素還可以包含任何先前討論過的 transition 元素,例如 next
屬性或 next
、end
或 fail
元素。
<split id="split1" next="step4">
<flow>
<step id="step1" parent="s1" next="step2"/>
<step id="step2" parent="s2"/>
</flow>
<flow>
<step id="step3" parent="s3"/>
</flow>
</split>
<step id="step4" parent="s4"/>
外部化流程定義和 Job 之間的依賴關係
job 中的一部分流程可以外部化為單獨的 bean 定義,然後重新使用。有兩種方法可以做到這一點。第一種是將流程宣告為對其他地方定義的流程的引用。
-
Java
-
XML
以下 Java 範例展示如何將流程宣告為對其他地方定義的流程的引用
@Bean
public Job job(JobRepository jobRepository, Flow flow1, Step step3) {
return new JobBuilder("job", jobRepository)
.start(flow1)
.next(step3)
.end()
.build();
}
@Bean
public Flow flow1(Step step1, Step step2) {
return new FlowBuilder<SimpleFlow>("flow1")
.start(step1)
.next(step2)
.build();
}
以下 XML 範例展示如何將流程宣告為對其他地方定義的流程的引用
<job id="job">
<flow id="job1.flow1" parent="flow1" next="step3"/>
<step id="step3" parent="s3"/>
</job>
<flow id="flow1">
<step id="step1" parent="s1" next="step2"/>
<step id="step2" parent="s2"/>
</flow>
定義外部流程的效果(如先前範例所示)是將外部流程中的步驟插入到 job 中,就像它們是內聯宣告的一樣。透過這種方式,許多 job 可以參考相同的範本流程,並將這些範本組合成不同的邏輯流程。這也是分離個別流程的整合測試的好方法。
外部化流程的另一種形式是使用 JobStep
。JobStep
類似於 FlowStep
,但實際上會為指定流程中的步驟建立並啟動單獨的 job 執行。
-
Java
-
XML
以下範例顯示 Java 中 JobStep
的範例
@Bean
public Job jobStepJob(JobRepository jobRepository, Step jobStepJobStep1) {
return new JobBuilder("jobStepJob", jobRepository)
.start(jobStepJobStep1)
.build();
}
@Bean
public Step jobStepJobStep1(JobRepository jobRepository, JobLauncher jobLauncher, Job job, JobParametersExtractor jobParametersExtractor) {
return new StepBuilder("jobStepJobStep1", jobRepository)
.job(job)
.launcher(jobLauncher)
.parametersExtractor(jobParametersExtractor)
.build();
}
@Bean
public Job job(JobRepository jobRepository) {
return new JobBuilder("job", jobRepository)
// ...
.build();
}
@Bean
public DefaultJobParametersExtractor jobParametersExtractor() {
DefaultJobParametersExtractor extractor = new DefaultJobParametersExtractor();
extractor.setKeys(new String[]{"input.file"});
return extractor;
}
以下範例顯示 XML 中 JobStep
的範例
<job id="jobStepJob" restartable="true">
<step id="jobStepJob.step1">
<job ref="job" job-launcher="jobLauncher"
job-parameters-extractor="jobParametersExtractor"/>
</step>
</job>
<job id="job" restartable="true">...</job>
<bean id="jobParametersExtractor" class="org.spr...DefaultJobParametersExtractor">
<property name="keys" value="input.file"/>
</bean>
job 參數提取器是一種策略,用於確定如何將 Step
的 ExecutionContext
轉換為要執行的 Job
的 JobParameters
。當您希望針對 job 和步驟的監控和報告擁有更精細的選項時,JobStep
非常有用。使用 JobStep
通常也是回答問題:「我如何在 job 之間建立依賴關係?」的好方法。這是將大型系統分解為較小的模組並控制 job 流程的好方法。