Spring Batch 小任務(Tasklet)步驟

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;
}

 

https://www.cwiki.us/display/SpringBatchZH/TaskletStep


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章