Chunk-Oriented Processing不是處理 step 的唯一方法。
考慮下面的一個場景,如果你僅僅需要調用一個存儲過程,你可以在 ItemReader
中實現這個調用,然后在存儲過程完成調用后返回 null。這種設計看起來不是那么自然也不是非常優美,因為你的批量設計中甚至都不需要實現 ItemWriter。針對這種情況,Spring Batch 為你提供了 TaskletStep 選項。
TaskletStep
是一個簡單的接口,這個接口只需要實現一個方法execute
,這個方法將會被TaskletStep多次重復的調用,直到這個方法返回 RepeatStatus.FINISHED
或者拋出異常來表示調用失敗。
Tasklet
的每一次調用都會包含在事務中(Transaction)。Tasklet 的實現(implementors)可以調用一個存儲過程,一個腳本或者一個簡單的 SQL 更新腳本。
針對我們的實踐中,我們可以使用 Tasklet 來執行一個 FTP 的任務。
將我們產生的中間文件上傳到不同的 FTP 服務器上,你可以在實現中指定不同的服務器配置參數,這樣更加有利於代碼的重用。
為了能夠創建一個 TaskletStep
,Bean 需要傳遞一個 tasklet 方法到構造器(builder),這個 tasklet 方法需要實現 Tasklet 接口。
當你構建 TaskletStep 的時候不要調用 chunk
。
下面的示例代碼顯示了一個在 Step build 中構建一個簡單的 tasklet。
@Bean
public
Step step1() {
return
this
.stepBuilderFactory.get(
"step1"
)
.tasklet(myTasklet())
.build();
}
|
如果你的 tasklet
實現了 StepListener
接口的話,TaskletStep
將會自動將 tasklet
注冊成為一個 StepListener
。
TaskletAdapter
與 ItemReader
和 ItemWriter
接口的 adapters
一樣。Tasklet
接口包含的實現也允許能夠通過已經存在的類使用 TaskletAdapter
來將自己進行注冊。
例如,你希望使用一個已經存在的 DAO 來更新記錄集上的標記的時候,你可以使用 TaskletAdapter
來進行實現。
使用 TaskletAdapter
能夠讓你的 DAO 可以被 Spring Batch 的 TaskletStep
調用而不需要讓你的 DAO 都實現 Tasklet 的接口。
如下面的示例代碼:
@Bean
public
MethodInvokingTaskletAdapter myTasklet() {
MethodInvokingTaskletAdapter adapter =
new
MethodInvokingTaskletAdapter();
adapter.setTargetObject(fooDao());
adapter.setTargetMethod(
"updateFoo"
);
return
adapter;
}
|
Tasklet 實現(Implementation)示例
在主批量作業開始之前,可能需要很多其他的批量作業必須完成,這樣以便於主批量作業能夠獲得必要的資源和在完成后釋放資源或者進行清理。
例如我們遇到下面的使用場景,一個批量作業需要大量的對文件進行交互和使用,通常來說需要在文件被上傳到其他服務器上后刪除本地產生的臨時文件。
下面的示例就是一個 Tasklet 的實現,這個Tasklet
的實現能夠完成上面的交互要求(文件來自 Spring Batch samples project 示例程序)。
public
class
FileDeletingTasklet
implements
Tasklet, InitializingBean {
private
Resource directory;
public
RepeatStatus execute(StepContribution contribution,
ChunkContext chunkContext)
throws
Exception {
File dir = directory.getFile();
Assert.state(dir.isDirectory());
File[] files = dir.listFiles();
for
(
int
i =
0
; i < files.length; i++) {
boolean
deleted = files[i].delete();
if
(!deleted) {
throw
new
UnexpectedJobExecutionException(
"Could not delete file "
+
files[i].getPath());
}
}
return
RepeatStatus.FINISHED;
}
public
void
setDirectoryResource(Resource directory) {
this
.directory = directory;
}
public
void
afterPropertiesSet()
throws
Exception {
Assert.notNull(directory,
"directory must be set"
);
}
}
|
Tasklet
處理程序實現了將給定目錄中的所有文件進行刪除。我們應該通知 execute
方法,這個 Tasklet 應該只被執行一次。
所有相關執行的操作需要在 Step
中進行設置,請參考下面有關這個 Tasklet
的設置:
Java 配置
@Bean
public
Job taskletJob() {
return
this
.jobBuilderFactory.get(
"taskletJob"
)
.start(deleteFilesInDir())
.build();
}
@Bean
public
Step deleteFilesInDir() {
return
this
.stepBuilderFactory.get(
"deleteFilesInDir"
)
.tasklet(fileDeletingTasklet())
.build();
}
@Bean
public
FileDeletingTasklet fileDeletingTasklet() {
FileDeletingTasklet tasklet =
new
FileDeletingTasklet();
tasklet.setDirectoryResource(
new
FileSystemResource(
"target/test-outputs/test-dir"
));
return
tasklet;
}
|