|
当前版本仍在开发中,尚不被视为稳定版本。最新稳定版请使用 Spring Batch 文档 6.0.2! |
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/> 元素。下面的示例展示了一个简单的 tasklet:
<step id="step1">
<tasklet ref="myTasklet"/>
</step>
如果 tasklet 实现了 StepListener 接口,TaskletStep 会自动把它注册为一个 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 实现示例
很多批处理作业都包含这样一些 step:它们需要在主处理开始前执行,用于准备各种资源;
或者在处理完成后执行,用于清理这些资源。对于大量处理文件的作业来说,
通常需要在文件成功上传到其他位置之后,再把本地文件删除。
下面这个示例(摘自
Spring Batch samples 项目)
就是一个承担这种职责的 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>