TaskletStep

基于 Chunk 的处理 不是 `Step` 中唯一的处理方式。如果 `Step` 必须包含存储过程调用怎么办?您可以将该调用实现为 `ItemReader`,并在过程完成后返回 null。但这有点不自然,因为还需要一个无操作的 `ItemWriter`。Spring Batch 为这种情况提供了 `TaskletStep`。

`Tasklet` 接口有一个方法 `execute`,`TaskletStep` 会重复调用该方法,直到它返回 `RepeatStatus.FINISHED` 或抛出异常表示失败。每次对 `Tasklet` 的调用都包裹在一个事务中。`Tasklet` 实现者可以调用存储过程、脚本或 SQL update 语句。

  • Java

  • XML

要在 Java 中创建 `TaskletStep`,传递给构建器的 `tasklet` 方法的 bean 应实现 `Tasklet` 接口。构建 `TaskletStep` 时,不应调用 `chunk` 方法。以下示例展示了一个简单的 tasklet

@Bean
public Step step1(JobRepository jobRepository, PlatformTransactionManager transactionManager) {
    return new StepBuilder("step1", jobRepository)
    			.tasklet(myTasklet(), transactionManager)
    			.build();
}

要在 XML 中创建 `TaskletStep`,`` 元素的 `ref` 属性应引用一个定义了 `Tasklet` 对象的 bean。在 `` 中不应使用 `` 元素。以下示例展示了一个简单的 tasklet

<step id="step1">
    <tasklet ref="myTasklet"/>
</step>
如果 tasklet 实现了 `StepListener` 接口,`TaskletStep` 会自动将其注册为 `StepListener`。

`TaskletAdapter`

与 `ItemReader` 和 `ItemWriter` 接口的其他适配器类似,`Tasklet` 接口包含一个实现,允许将其自身适配到任何预先存在的类:`TaskletAdapter`。一个可能有用处的例子是用于更新一组记录上的标志的现有 DAO。您可以使用 `TaskletAdapter` 调用此类,而无需为 `Tasklet` 接口编写适配器。

  • Java

  • XML

以下示例展示了如何在 Java 中定义 `TaskletAdapter`

Java 配置
@Bean
public MethodInvokingTaskletAdapter myTasklet() {
	MethodInvokingTaskletAdapter adapter = new MethodInvokingTaskletAdapter();

	adapter.setTargetObject(fooDao());
	adapter.setTargetMethod("updateFoo");

	return adapter;
}

以下示例展示了如何在 XML 中定义 `TaskletAdapter`

XML 配置
<bean id="myTasklet" class="o.s.b.core.step.tasklet.MethodInvokingTaskletAdapter">
    <property name="targetObject">
        <bean class="org.mycompany.FooDao"/>
    </property>
    <property name="targetMethod" value="updateFoo" />
</bean>

`Tasklet` 实现示例

许多批处理 Job 包含需要在主处理开始前完成的步骤,用于设置各种资源;或在处理完成后用于清理这些资源。对于大量处理文件的 Job,通常需要在文件成功上传到另一个位置后在本地删除某些文件。以下示例(摘自 Spring Batch 示例项目)就是一个具有此职责的 `Tasklet` 实现

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(), "The resource must be a directory");

        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.state(directory != null, "Directory must be set");
    }
}

上述 `tasklet` 实现删除了给定目录中的所有文件。应该注意的是,`execute` 方法只被调用一次。剩下的就是从 `step` 中引用该 `tasklet`。

  • Java

  • XML

以下示例展示了如何在 Java 中从 `step` 中引用 `tasklet`

Java 配置
@Bean
public Job taskletJob(JobRepository jobRepository, Step deleteFilesInDir) {
	return new JobBuilder("taskletJob", jobRepository)
				.start(deleteFilesInDir)
				.build();
}

@Bean
public Step deleteFilesInDir(JobRepository jobRepository, PlatformTransactionManager transactionManager) {
	return new StepBuilder("deleteFilesInDir", jobRepository)
				.tasklet(fileDeletingTasklet(), transactionManager)
				.build();
}

@Bean
public FileDeletingTasklet fileDeletingTasklet() {
	FileDeletingTasklet tasklet = new FileDeletingTasklet();

	tasklet.setDirectoryResource(new FileSystemResource("target/test-outputs/test-dir"));

	return tasklet;
}

以下示例展示了如何在 XML 中从 `step` 中引用 `tasklet`

XML 配置
<job id="taskletJob">
    <step id="deleteFilesInDir">
       <tasklet ref="fileDeletingTasklet"/>
    </step>
</job>

<beans:bean id="fileDeletingTasklet"
            class="org.springframework.batch.samples.tasklet.FileDeletingTasklet">
    <beans:property name="directoryResource">
        <beans:bean id="directory"
                    class="org.springframework.core.io.FileSystemResource">
            <beans:constructor-arg value="target/test-outputs/test-dir" />
        </beans:bean>
    </beans:property>
</beans:bean>