TaskletStep
Chunk-oriented processing并不是在 Step`中处理的唯一方法。如果`Step`必须包含存储过程调用会怎样?你可以将调用实现为 `ItemReader
,并在过程完成后返回 null。但是,这样做有点不自然,因为需要存在一个 no-op ItemWriter
。Spring Batch 为此场景提供了 TaskletStep
。
Tasklet
接口有一个方法 execute
,在 TaskletStep
调用此方法,直到它返回 RepeatStatus.FINISHED
或抛出异常以表示失败。对 Tasklet
的每个调用都被包装在一个事务中。Tasklet
实现者可以调用存储过程、脚本或 SQL 更新语句。
- Java
-
要在 Java 中创建
TaskletStep
,传递给构建器tasklet
方法的 bean 应该实现Tasklet
接口。构建TaskletStep
时不应该调用chunk
。以下示例显示了一个简单的任务项:
@Bean
public Step step1(JobRepository jobRepository, PlatformTransactionManager transactionManager) {
return new StepBuilder("step1", jobRepository)
.tasklet(myTasklet(), transactionManager)
.build();
}
- XML
-
要在 XML 中创建
TaskletStep
,<tasklet/>
元素的ref
属性应该引用定义Tasklet
对象的 bean。在<tasklet/>
内不应该使用<chunk/>
元素。以下示例显示了一个简单的任务项:
<step id="step1">
<tasklet ref="myTasklet"/>
</step>
如果它实现了 |
TaskletAdapter
与其他 ItemReader
和 ItemWriter
接口适配器一样,Tasklet
接口包含一个允许适配自身到任何预先存在的类的实现:TaskletAdapter
。一个可能用得上的例子是一个用于更新一组记录的标志的现有 DAO。您可以使用 TaskletAdapter
调用这个类,而不必为 Tasklet
接口编写一个适配器。
- Java
-
以下示例显示如何在 Java 中定义
TaskletAdapter
:
@Bean
public MethodInvokingTaskletAdapter myTasklet() {
MethodInvokingTaskletAdapter adapter = new MethodInvokingTaskletAdapter();
adapter.setTargetObject(fooDao());
adapter.setTargetMethod("updateFoo");
return adapter;
}
- XML
-
以下示例显示如何在 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>
Example Tasklet
Implementation
很多批作业包含必须在主处理开始前(用以设置各种资源)或处理完成后(用以清理这些资源)执行的步骤。在大量使用文件的作业的情况下,通常有必要在已成功上传文件到另一个位置后删除某些本地文件。以下示例(取自 link:https://github.com/spring-projects/spring-batch/tree/main/spring-batch-samples[SpringBatch 样例项目)是一个具有这样职责的 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());
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
-
以下示例显示如何在 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
-
以下示例显示如何在 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>