進階中繼資料使用
到目前為止,已討論過 JobLauncher
和 JobRepository
介面。它們共同代表了 Job 的簡單啟動和批次網域物件的基本 CRUD 操作

JobLauncher
使用 JobRepository
建立新的 JobExecution
物件並執行它們。Job
和 Step
實作稍後會在 Job
執行期間使用相同的 JobRepository
進行相同執行的基本更新。基本操作足以應付簡單的場景。但是,在具有數百個批次 Job 和複雜排程需求的大型批次環境中,需要更進階的中繼資料存取

即將章節中討論的 JobExplorer
和 JobOperator
介面,增加了查詢和控制中繼資料的其他功能。
查詢 Repository
在任何進階功能之前,最基本的需求是能夠查詢 Repository 以取得現有的執行。此功能由 JobExplorer
介面提供
public interface JobExplorer {
List<JobInstance> getJobInstances(String jobName, int start, int count);
JobExecution getJobExecution(Long executionId);
StepExecution getStepExecution(Long jobExecutionId, Long stepExecutionId);
JobInstance getJobInstance(Long instanceId);
List<JobExecution> getJobExecutions(JobInstance jobInstance);
Set<JobExecution> findRunningJobExecutions(String jobName);
}
從其方法簽名顯而易見,JobExplorer
是 JobRepository
的唯讀版本,並且與 JobRepository
一樣,可以使用 factory bean 輕鬆設定。
-
Java
-
XML
以下範例顯示如何在 Java 中設定 JobExplorer
...
// This would reside in your DefaultBatchConfiguration extension
@Bean
public JobExplorer jobExplorer() throws Exception {
JobExplorerFactoryBean factoryBean = new JobExplorerFactoryBean();
factoryBean.setDataSource(this.dataSource);
return factoryBean.getObject();
}
...
以下範例顯示如何在 XML 中設定 JobExplorer
<bean id="jobExplorer" class="org.spr...JobExplorerFactoryBean"
p:dataSource-ref="dataSource" />
在本章稍早,我們注意到您可以修改 JobRepository
的資料表前綴,以允許不同的版本或 Schema。由於 JobExplorer
使用相同的資料表,因此它也需要能夠設定前綴。
-
Java
-
XML
以下範例顯示如何在 Java 中設定 JobExplorer
的資料表前綴
...
// This would reside in your DefaultBatchConfiguration extension
@Bean
public JobExplorer jobExplorer() throws Exception {
JobExplorerFactoryBean factoryBean = new JobExplorerFactoryBean();
factoryBean.setDataSource(this.dataSource);
factoryBean.setTablePrefix("SYSTEM.");
return factoryBean.getObject();
}
...
以下範例顯示如何在 XML 中設定 JobExplorer
的資料表前綴
<bean id="jobExplorer" class="org.spr...JobExplorerFactoryBean"
p:tablePrefix="SYSTEM."/>
JobRegistry
JobRegistry
(及其父介面 JobLocator
)不是強制性的,但如果您想追蹤內容中可用的 Job,它可能會很有用。當 Job 在其他地方(例如,在子內容中)建立時,它對於在應用程式內容中集中收集 Job 也很有用。您也可以使用自訂 JobRegistry
實作來操作已註冊 Job 的名稱和其他屬性。框架僅提供一個實作,它基於從 Job 名稱到 Job 實例的簡單映射。
-
Java
-
XML
當使用 @EnableBatchProcessing
時,會為您提供 JobRegistry
。以下範例顯示如何設定您自己的 JobRegistry
...
// This is already provided via the @EnableBatchProcessing but can be customized via
// overriding the bean in the DefaultBatchConfiguration
@Override
@Bean
public JobRegistry jobRegistry() throws Exception {
return new MapJobRegistry();
}
...
以下範例顯示如何為 XML 中定義的 Job 包含 JobRegistry
<bean id="jobRegistry" class="org.springframework.batch.core.configuration.support.MapJobRegistry" />
您可以使用以下其中一種方式來填入 JobRegistry
:使用 bean 後處理器,或使用 smart initializing singleton,或使用 registrar lifecycle component。接下來的章節將說明這些機制。
JobRegistryBeanPostProcessor
這是一個 bean 後處理器,可以在建立所有 Job 時註冊它們。
-
Java
-
XML
以下範例顯示如何為 Java 中定義的 Job 包含 JobRegistryBeanPostProcessor
@Bean
public JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor(JobRegistry jobRegistry) {
JobRegistryBeanPostProcessor postProcessor = new JobRegistryBeanPostProcessor();
postProcessor.setJobRegistry(jobRegistry);
return postProcessor;
}
以下範例顯示如何為 XML 中定義的 Job 包含 JobRegistryBeanPostProcessor
<bean id="jobRegistryBeanPostProcessor" class="org.spr...JobRegistryBeanPostProcessor">
<property name="jobRegistry" ref="jobRegistry"/>
</bean>
雖然不是絕對必要,但範例中的後處理器已給定一個 id
,以便它可以包含在子內容中(例如,作為父 bean 定義),並使在那裡建立的所有 Job 也自動註冊。
從 5.1 版開始,@EnableBatchProcessing
注釋會自動在應用程式內容中註冊 jobRegistryBeanPostProcessor
bean。
JobRegistrySmartInitializingSingleton
這是一個 SmartInitializingSingleton
,可在 Job 註冊表中註冊所有 singleton Job。
-
Java
-
XML
以下範例顯示如何在 Java 中定義 JobRegistrySmartInitializingSingleton
@Bean
public JobRegistrySmartInitializingSingleton jobRegistrySmartInitializingSingleton(JobRegistry jobRegistry) {
return new JobRegistrySmartInitializingSingleton(jobRegistry);
}
以下範例顯示如何在 XML 中定義 JobRegistrySmartInitializingSingleton
<bean class="org.springframework.batch.core.configuration.support.JobRegistrySmartInitializingSingleton">
<property name="jobRegistry" ref="jobRegistry" />
</bean>
AutomaticJobRegistrar
這是一個生命週期元件,可建立子內容並從這些內容中註冊 Job(在建立時)。這樣做的一個優點是,雖然子內容中的 Job 名稱仍然必須在註冊表中全域唯一,但它們的依賴項可以具有「自然」名稱。因此,例如,您可以建立一組 XML 設定檔,每個檔案都只有一個 Job,但都具有相同 bean 名稱(例如 reader
)的 ItemReader
的不同定義。如果所有這些檔案都匯入到相同的內容中,則 reader 定義會衝突並覆蓋彼此,但使用自動註冊器,則可以避免這種情況。這使得整合從應用程式的不同模組貢獻的 Job 變得更容易。
-
Java
-
XML
以下範例顯示如何為 Java 中定義的 Job 包含 AutomaticJobRegistrar
@Bean
public AutomaticJobRegistrar registrar() {
AutomaticJobRegistrar registrar = new AutomaticJobRegistrar();
registrar.setJobLoader(jobLoader());
registrar.setApplicationContextFactories(applicationContextFactories());
registrar.afterPropertiesSet();
return registrar;
}
以下範例顯示如何為 XML 中定義的 Job 包含 AutomaticJobRegistrar
<bean class="org.spr...AutomaticJobRegistrar">
<property name="applicationContextFactories">
<bean class="org.spr...ClasspathXmlApplicationContextsFactoryBean">
<property name="resources" value="classpath*:/config/job*.xml" />
</bean>
</property>
<property name="jobLoader">
<bean class="org.spr...DefaultJobLoader">
<property name="jobRegistry" ref="jobRegistry" />
</bean>
</property>
</bean>
註冊器具有兩個強制屬性:ApplicationContextFactory
陣列(從前面範例中的方便 factory bean 建立)和 JobLoader
。JobLoader
負責管理子內容的生命週期並在 JobRegistry
中註冊 Job。
ApplicationContextFactory
負責建立子內容。最常見的用法是(如前面的範例中)使用 ClassPathXmlApplicationContextFactory
。此 factory 的其中一個功能是,預設情況下,它會將一些設定從父內容複製到子內容。因此,例如,您不需要在子內容中重新定義 PropertyPlaceholderConfigurer
或 AOP 設定,前提是它應該與父內容相同。
您可以將 AutomaticJobRegistrar
與 JobRegistryBeanPostProcessor
結合使用(只要您也使用 DefaultJobLoader
)。例如,如果主要父內容以及子位置中都定義了 Job,則可能需要這樣做。
JobOperator
如先前所討論,JobRepository
提供對中繼資料的 CRUD 操作,而 JobExplorer
提供對中繼資料的唯讀操作。但是,當它們一起使用以執行常見的監控任務(例如停止、重新啟動或摘要 Job)時,這些操作最有用,批次操作員通常會這樣做。Spring Batch 在 JobOperator
介面中提供這些類型的操作
public interface JobOperator {
List<Long> getExecutions(long instanceId) throws NoSuchJobInstanceException;
List<Long> getJobInstances(String jobName, int start, int count)
throws NoSuchJobException;
Set<Long> getRunningExecutions(String jobName) throws NoSuchJobException;
String getParameters(long executionId) throws NoSuchJobExecutionException;
Long start(String jobName, String parameters)
throws NoSuchJobException, JobInstanceAlreadyExistsException;
Long restart(long executionId)
throws JobInstanceAlreadyCompleteException, NoSuchJobExecutionException,
NoSuchJobException, JobRestartException;
Long startNextInstance(String jobName)
throws NoSuchJobException, JobParametersNotFoundException, JobRestartException,
JobExecutionAlreadyRunningException, JobInstanceAlreadyCompleteException;
boolean stop(long executionId)
throws NoSuchJobExecutionException, JobExecutionNotRunningException;
String getSummary(long executionId) throws NoSuchJobExecutionException;
Map<Long, String> getStepExecutionSummaries(long executionId)
throws NoSuchJobExecutionException;
Set<String> getJobNames();
}
先前的操作代表來自許多不同介面的方法,例如 JobLauncher
、JobRepository
、JobExplorer
和 JobRegistry
。因此,提供的 JobOperator
實作 (SimpleJobOperator
) 有許多依賴項。
-
Java
-
XML
以下範例顯示 Java 中 SimpleJobOperator
的典型 bean 定義
/**
* All injected dependencies for this bean are provided by the @EnableBatchProcessing
* infrastructure out of the box.
*/
@Bean
public SimpleJobOperator jobOperator(JobExplorer jobExplorer,
JobRepository jobRepository,
JobRegistry jobRegistry,
JobLauncher jobLauncher) {
SimpleJobOperator jobOperator = new SimpleJobOperator();
jobOperator.setJobExplorer(jobExplorer);
jobOperator.setJobRepository(jobRepository);
jobOperator.setJobRegistry(jobRegistry);
jobOperator.setJobLauncher(jobLauncher);
return jobOperator;
}
以下範例顯示 XML 中 SimpleJobOperator
的典型 bean 定義
<bean id="jobOperator" class="org.spr...SimpleJobOperator">
<property name="jobExplorer">
<bean class="org.spr...JobExplorerFactoryBean">
<property name="dataSource" ref="dataSource" />
</bean>
</property>
<property name="jobRepository" ref="jobRepository" />
<property name="jobRegistry" ref="jobRegistry" />
<property name="jobLauncher" ref="jobLauncher" />
</bean>
從 5.0 版開始,@EnableBatchProcessing
注釋會自動在應用程式內容中註冊 job operator bean。
如果您在 job repository 上設定資料表前綴,請不要忘記在 job explorer 上也設定它。 |
JobParametersIncrementer
JobOperator
上的大多數方法都是不言自明的,您可以在 介面的 Javadoc 中找到更詳細的說明。但是,startNextInstance
方法值得注意。此方法始終啟動 Job
的新實例。如果 JobExecution
中存在嚴重問題,並且需要從頭開始重新啟動 Job
,這可能非常有用。與 JobLauncher
(需要新的 JobParameters
物件來觸發新的 JobInstance
)不同,如果參數與任何先前的參數集不同,則 startNextInstance
方法會使用繫結到 Job
的 JobParametersIncrementer
強制 Job
成為新實例
public interface JobParametersIncrementer {
JobParameters getNext(JobParameters parameters);
}
JobParametersIncrementer
的合約是,給定一個 JobParameters 物件,它會透過遞增它可能包含的任何必要值來傳回「下一個」JobParameters
物件。此策略很有用,因為框架無法知道對 JobParameters
的哪些變更使其成為「下一個」實例。例如,如果 JobParameters
中唯一的 value 是日期,並且應該建立下一個實例,那麼該 value 應該遞增一天還是 一週(如果 Job 是每週的,例如)?對於任何有助於識別 Job
的數值也是如此,如下例所示
public class SampleIncrementer implements JobParametersIncrementer {
public JobParameters getNext(JobParameters parameters) {
if (parameters==null || parameters.isEmpty()) {
return new JobParametersBuilder().addLong("run.id", 1L).toJobParameters();
}
long id = parameters.getLong("run.id",1L) + 1;
return new JobParametersBuilder().addLong("run.id", id).toJobParameters();
}
}
在此範例中,鍵為 run.id
的 value 用於區分 JobInstances
。如果傳入的 JobParameters
為 null,則可以假設 Job
從未執行過,因此可以傳回其初始狀態。但是,如果不是,則會取得舊 value,遞增 1,然後傳回。
-
Java
-
XML
對於在 Java 中定義的 Job,您可以透過 builders 中提供的 incrementer
方法將 incrementer 與 Job
關聯,如下所示
@Bean
public Job footballJob(JobRepository jobRepository) {
return new JobBuilder("footballJob", jobRepository)
.incrementer(sampleIncrementer())
...
.build();
}
對於在 XML 中定義的 Job,您可以透過命名空間中的 incrementer
屬性將 incrementer 與 Job
關聯,如下所示
<job id="footballJob" incrementer="sampleIncrementer">
...
</job>
停止 Job
JobOperator
最常見的用例之一是優雅地停止 Job
Set<Long> executions = jobOperator.getRunningExecutions("sampleJob");
jobOperator.stop(executions.iterator().next());
關閉不是立即的,因為沒有辦法強制立即關閉,特別是如果執行目前位於框架無法控制的開發人員程式碼中,例如業務服務。但是,一旦控制權返回到框架,它就會將目前 StepExecution
的狀態設定為 BatchStatus.STOPPED
,儲存它,並在完成之前對 JobExecution
執行相同的操作。
中止 Job
FAILED
的 Job 執行可以重新啟動(如果 Job
是可重新啟動的)。狀態為 ABANDONED
的 Job 執行無法由框架重新啟動。ABANDONED
狀態也用於 step 執行中,以將它們標記為在重新啟動的 Job 執行中可跳過的。如果 Job 正在執行並遇到在先前失敗的 Job 執行中標記為 ABANDONED
的 step,它會繼續執行下一個 step(由 Job 流程定義和 step 執行結束狀態決定)。
如果程序死掉 (kill -9
或伺服器故障),Job 當然不會執行,但 JobRepository
無法知道,因為在程序死掉之前沒有人告訴它。您必須手動告訴它您知道執行失敗或應視為中止(將其狀態變更為 FAILED
或 ABANDONED
)。這是一個業務決策,沒有辦法自動化。僅當它是可重新啟動的並且您知道重新啟動資料有效時,才將狀態變更為 FAILED
。