TaskletStep
面向块的处理 并不是在 Step 中进行处理的唯一方式。如果 Step 必须包含一个存储过程调用怎么办?您可以将该调用实现为 ItemReader,并在过程结束后返回 null。然而,这样做有些不够自然,因为需要提供一个无操作的 ItemWriter。Spring Batch 为此场景提供了 TaskletStep。
Tasklet 接口有一个方法 execute,该方法会被 TaskletStep 反复调用,直到其返回 RepeatStatus.FINISHED 或抛出异常以表示失败。每次对 Tasklet 的调用都会包裹在一个事务中。Tasklet 的实现者可以调用存储过程、脚本或 SQL 更新语句。
-
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,<tasklet/> 元素的 ref 属性应引用一个定义了 Tasklet 对象的 Bean。在 <tasklet/> 内部不应使用 <chunk/> 元素。以下示例展示了一个简单的任务片段:
<step id="step1">
<tasklet ref="myTasklet"/>
</step>
如果它实现了 StepListener 接口,TaskletStep 会自动将 tasklet 注册为 StepListener。 |
TaskletAdapter
与其他针对 ItemReader 和 ItemWriter 接口的适配器一样,Tasklet 接口包含一个实现,允许其适配任何现有类:TaskletAdapter。一个可能有用的例子是现有的 DAO,它用于更新一组记录上的标志。您可以使用 TaskletAdapter 来调用此类,而无需为 Tasklet 接口编写适配器。
-
Java
-
XML
以下示例展示了如何在 Java 中定义一个 TaskletAdapter:
@Bean
public MethodInvokingTaskletAdapter myTasklet() {
MethodInvokingTaskletAdapter adapter = new MethodInvokingTaskletAdapter();
adapter.setTargetObject(fooDao());
adapter.setTargetMethod("updateFoo");
return adapter;
}
以下示例展示了如何在 XML 中定义一个 TaskletAdapter:
<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实施
许多批处理作业包含必须在主处理开始之前执行的步骤,
以设置各种资源,或在处理完成后清理这些
资源。对于大量处理文件的作业,通常在文件成功上传到其他位置后,
有必要在本地删除某些文件。以下示例(取自
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:
@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:
<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>