Conditional Flow in Spring Batch

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

CHECK OUT THE COURSE

1. Introduction

We use Spring Batch to compose jobs from multiple steps that read, transform, and write data. If the steps in a job have multiple paths, similar to using an if statement in our code, we say that the job flow is conditional.

In this tutorial, we'll look at two ways to create Spring Batch jobs with a conditional flow.

2. Exit Status and Batch Status

When we specify a conditional step with Spring's Batch framework, we're using the exit status of a step or job. Therefore, we need to understand the difference between Batch Status and Exit Status in our steps and jobs:

  • BatchStatus is an Enum representing the status of a step/job and is used by the Batch framework internally
  • Possible values are: ABANDONED, COMPLETED, FAILED, STARTED, STARTING, STOPPED, STOPPING, UNKNOWN
  • ExitStatus is the status of a step when execution is completed and is used to conditionally determine the flow

By default, the ExitStatus of a step or job is the same as its BatchStatus. We can also set a custom ExitStatus to drive flow.

3. Conditional Flow

Let's say we have an IOT device that sends us measurements. Our device measurements are arrays of integers, and we need to send notifications if any of our measurements contain positive integers.

In other words, we need to send a notification when we detect a positive measurement.

3.1. ExitStatus

Importantly, we use the exit status of a step to drive conditional flow.

To set the exit status of a step, we need to use the StepExecution object's setExitStatus method. In order to do that, we need to create an ItemProcessor that extends ItemListenerSupport and gets the step's StepExecution.

We use this to set our step's exit status to NOTIFY when we find a positive number. When we determine our exit status based on data within the batch job, we can use an ItemProcessor.

Let's look at our NumberInfoClassifier to see the three methods we need:

public class NumberInfoClassifier extends ItemListenerSupport<NumberInfo, Integer>
implements ItemProcessor<NumberInfo, Integer> { private StepExecution stepExecution; @BeforeStep
public void beforeStep(StepExecution stepExecution) {
this.stepExecution = stepExecution;
this.stepExecution.setExitStatus(new ExitStatus(QUIET));
} @Override
public Integer process(NumberInfo numberInfo) throws Exception {
return Integer.valueOf(numberInfo.getNumber());
} @Override
public void afterProcess(NumberInfo item, Integer result) {
super.afterProcess(item, result);
if (item.isPositive()) {
stepExecution.setExitStatus(new ExitStatus(NOTIFY));
}
}
}

Note: we use the ItemProcessor to set the ExitStatus in this example, but we could just as easily do it in our step's ItemReader or ItemWriter.

Finally, when we create our job, we tell our JobBuilderFactory to send notifications for any step that exits with a status of NOTIFY:

jobBuilderFactory.get("Number generator - second dataset")
.start(dataProviderStep)
.on("NOTIFY").to(notificationStep)
.end()
.build();

Also note that when we have additional conditional branches and multiple exit codes, we can add them to our job with the from and on methods of the JobBuilderFacotry:

jobBuilderFactory.get("Number generator - second dataset")
.start(dataProviderStep)
.on("NOTIFY").to(notificationStep)
.from(step).on("LOG_ERROR").to(errorLoggingStep)
.end()
.build();

Now, any time our ItemProcessor sees a positive number, it will direct our job to run the notificationStep, which simply prints a message to System.out:

Second Dataset Processor 11
Second Dataset Processor -2
Second Dataset Processor -3
[Number generator - second dataset] contains interesting data!!

If we had a data set without a positive number, we would not see our notificationStep message:

Second Dataset Processor -1
Second Dataset Processor -2
Second Dataset Processor -3

3.2. Programmatic Branching With JobExecutionDecider

Alternatively, we can use a class that implements JobExecutionDecider to determine job flow. This is especially useful if we have external factors for determining execution flow.

To use this method, we first need to modify our ItemProcessor to remove the ItemListenerSupport interface and @BeforeStep method:

public class NumberInfoClassifierWithDecider extends ItemListenerSupport<NumberInfo, Integer>
implements ItemProcessor<NumberInfo, Integer> { @Override
public Integer process(NumberInfo numberInfo) throws Exception {
return Integer.valueOf(numberInfo.getNumber());
}
}

Next, we create a decider class that determines the notification status of our step:

public class NumberInfoDecider implements JobExecutionDecider {

    private boolean shouldNotify() {
return true;
} @Override
public FlowExecutionStatus decide(JobExecution jobExecution, StepExecution stepExecution) {
if (shouldNotify()) {
return new FlowExecutionStatus(NOTIFY);
} else {
return new FlowExecutionStatus(QUIET);
}
}
}

Then, we set up our Job to use the decider in the flow:

jobBuilderFactory.get("Number generator - third dataset")
.start(dataProviderStep)
.next(new NumberInfoDecider()).on("NOTIFY").to(notificationStep)
.end()
.build();

4. Conclusion

In this quick tutorial, we explored two options for implementing conditional flows with Spring Batch. First, we looked at how to use the ExitStatus to control the flow of our job.

Then we took a look at how we can control flow programmatically by defining our own JobExecutionDecider.

As always, the full source code of the article is available over on GitHub.

Spring Batch(0)——控制Step执行流程的更多相关文章

  1. spring batch中控制step的走向

    1.顺序执行step: <job id="job"> <step id="stepA" parent="s1" next= ...

  2. Spring Security 案例实现和执行流程剖析

    Spring Security Spring Security 是 Spring 社区的一个顶级项目,也是 Spring Boot 官方推荐使用的安全框架.除了常规的认证(Authentication ...

  3. Spring 文件上传MultipartFile 执行流程分析

    在了解Spring 文件上传执行流程之前,我们必须知道两点: 1.Spring 文件上传是基于common-fileUpload 组件的,所以,文件上传必须引入此包 2.Spring 文件上传需要在X ...

  4. Spring MVC必须知道的执行流程

    Spring MVC的执行流程 一.名词解释 1.前端控制器(DispatcherServlet) 接收请求,响应结果,相当于转发器,中央处理器 2.处理器映射器(HandlerMapping) 根据 ...

  5. Struts 2 Spring Hibernate三大框架的执行流程以及原理

    Struts2框架 一.简介 Struts2是一个相当强大的Java Web开源框架,是一个基于POJO的Action的MVC Web框架.它基于当年的WebWork和XWork框架,继承其优点,同时 ...

  6. Spring MVC 原理介绍(执行流程)

    Spring MVC工作流程图   图一   图二    Spring工作流程描述       1. 用户向服务器发送请求,请求被Spring 前端控制Servelt DispatcherServle ...

  7. ASP.NET MVC3 中整合 NHibernate3.3、Spring.NET2.0 使用AOP执行事务处理

    方法1 <object id="ServiceOperation" type="Spring.Aop.Support.SdkRegularExpressionMet ...

  8. Spring Batch在大型企业中的最佳实践

    在大型企业中,由于业务复杂.数据量大.数据格式不同.数据交互格式繁杂,并非所有的操作都能通过交互界面进行处理.而有一些操作需要定期读取大批量的数据,然后进行一系列的后续处理.这样的过程就是" ...

  9. Spring Batch实践

    Spring Batch在大型企业中的最佳实践 在大型企业中,由于业务复杂.数据量大.数据格式不同.数据交互格式繁杂,并非所有的操作都能通过交互界面进行处理.而有一些操作需要定期读取大批量的数据,然后 ...

随机推荐

  1. 大爽Python入门教程 总目录

    作者自我介绍:b站小UP主,时常直播编程+红警三,python1对1辅导老师. 大爽Python入门公开课教案 本篇博客为公开课教案目录,正文内容在目录章节链接的博客里 除目录本身外,没有链接的章节, ...

  2. [atARC111F]Do you like query problems

    (以下修改指1和2类操作,询问指3类操作,操作指修改或询问) 注意到总方案数确定,那么不妨求出答案的期望,再乘上方案数即为答案 (这里从期望的角度考虑只是为了描述方便,并没有太大的实际意义) 设$E( ...

  3. [atARC084D]Small Multiple

    构造一张图:$\forall x$,向$10x$连一条边权为0的边,向$x+1$连1条边权为1的边,那么0到$i$的代价即为$i$各位数字之和 考虑到我们只关心于当前点的两个特征:1.模$n$的余数( ...

  4. idea提交代码好习惯-代码格式化

    提交代码的时候,勾选这个可以格式化提交的代码,非常好! reformat code

  5. springboot增加多端口管理

    目标是这样的: 方法 方法还是比较简单的1.点击菜单栏:Views -> Tool Windows -> Services:中文对应:视图 -> 工具窗口 -> 服务:快捷键是 ...

  6. Jmeter BlazeMeter实现web录制

      1. BlazeMeter安装和注册 BlazeMeter是一款与Apache JMeter兼容的chrome插件,采用BlazeMeter可以方便的进行流量录制和脚本生成,作为接口测试脚本编写的 ...

  7. 洛谷 P4088 [USACO18FEB] Slingshot P(线段树+二维数点)

    题目链接 题意:有一个数轴,上面有 \(n\) 个传送门,使用第 \(i\) 个传送门,你可以从 \(x_i\) 走到 \(y_i\),花费的时间为 \(t_i\) 秒.你的速度为 \(1\) 格/秒 ...

  8. GWAS分析结果中pvalue/p.ajust为0时如何处理?

    在GWAS分析的结果中,偶尔会遇到到pvalue为0的SNP位点,这时如果直接做曼哈顿或QQ图,会出错,因为log0无意义. 此时,该如何处理? 如果你用的是Plink1.9来做的GWAS,可加一个参 ...

  9. lua5.4 beta中的to-be-closed变量的用法

    对应目前最新lua5.4 beta版本:2019-10-09发布 这个功能之前修改过两次语法,当前的语法不出意外将会是最终决定了,目前还没有最新的中文资料,所以我来这里发一下. 先介绍下这个功能: 被 ...

  10. 选择省份、选择省市区举例【c#】【js】

    <style type="text/css"> .labelhide { -webkit-box-shadow: 0px 1px 0px 0px #f3f3f3 !im ...