如何从 StepExecutionListener 中的 beforeStep 方法跳过步骤执行?

How to skip a step execution from beforeStep method in StepExecutionListener?

我想为任何步骤实现一个通用的 StepExecutionListener,因此在 beforeStep 方法中(也就是说,在当前步骤 tasklet 开始之前)它可以检查一些与数据库相关的条件:如果不成功,它将跳过当前步骤并继续下一步。

下:

public void beforeStep(StepExecution stepExecution)

目前我在 "stepExecution" 对象中没有找到任何东西可以做到这一点。我只是将退出状态更改为已完成,但仍然执行相应的 tasklet。

有办法实现吗?我也听说过决策者,但我不喜欢在 XML.

上添加太多冗长的配置

提前致谢。

在这种情况下,决策程序是更好的选择,因为它们的唯一目的是决定是否执行您的步骤。他们确实使 xml 变得冗长,但这更好,因为其他人乍一看无法理解。

我认为 beforeStep 可以处理此问题的唯一方法是当任务名称属于您要忽略的任务时抛出异常。然而,您的下一步可能需要配置为 运行 即使上一步失败(如果未跳过该步骤时这不是您想要的,则不推荐)。

不过,如果你的任务扩展了,你需要决定3-4步的话,beforeStep中的处理会变得很麻烦。

让我知道你的搭配!

编辑:

决策者与步骤的用法示例

<batch:decision id="decider1" decider="deciderClass1">
        <batch:next on="FAILED" to="decider2" />
        <batch:next on="COMPLETED" to="task1"/>
    </batch:decision>
    <batch:step id="step1" next="decider2" parent="step1Class"/>

    <batch:decision id="decider2" decider="deciderClass2">
        <batch:next on="FAILED" to="step3" />
        <batch:next on="COMPLETED" to="step2"/>
    </batch:decision>
    <batch:step id="step2" parent="step2Class" next="step3"/>
.....

我会自我回答以介绍一些代码。

根据Priteesh的提议,我定义了一个这样的Decider:

    <decision id="deciderId" decider="deciderDeclaration">
        <next on="CONTINUE" to="step1" />
        <next on="SKIP" to="finalStep" />
    </decision>

把它放在我的 context.xml 的第一位。这很重要,因为如果不满足条件,step1 永远不会执行。

我的决策是这样的:

public class StepExecutionDecider implements JobExecutionDecider{

    /** LOGGER. */
    private Logger LOGGER = ...

    private static final String FLOW_STATUS_CONTINUE = "CONTINUE";
    private static final String FLOW_STATUS_SKIP = "SKIP";

    @Override
    public FlowExecutionStatus decide(JobExecution jobExecution, StepExecution stepExecution) {

        String status = FLOW_STATUS_CONTINUE;

        String condition = ....

        if (Constants.NO.equals(condition)){

            LOGGER.warn(jobExecution.getJobInstance().getJobName(), " -> The Step execution is disabled. Continuing with the next Step.");

            status = FLOW_STATUS_SKIP;
        }

        return new FlowExecutionStatus(status);
    }

    ...
}

奇怪的是,每当我的条件不满足时,LOGGER 消息就会在我的日志中打印两次,时间戳完全相同...

无论如何,非常感谢 Priteesh 的帮助。

我有相同的目标:在 StepExecutionListener 中使用 beforeStep() 检查非 spring 批处理相关条件,以决定该步骤是否应该 运行。

什么不起作用

使用决策器 (JobExecutionDecider) 不起作用,因为 JobExecutionDecider.decide() 先前执行的 作为参数接收。决策者的目的是让您检查上一步执行的状态以选择下一步做什么。如果我正在测试特定于 StepXYZ 的条件,我需要在执行之前准备好该步骤

好吧,StepExecutionListener.execute() 在执行之前将 StepXyz 执行放在你的手上。听起来很有前途...但是,API 没有任何机制告诉 spring 批处理不要 运行 StepXyz。为了说明这一点,以下语句中的 none 将满足我们的要求。

stepExecution.status = BatchStatus.ABANDONED
stepExecution.exitStatus = ExitStatus.NOOP
stepExecution.setTerminateOnly()   

结论

在搜索了所有框架代码、SO 和 spring 文档之后,我的结论是:

One cannot use beforeStep() to gracefully tell spring batch not to run that step.

(我正在使用 Spring Boot 2.3.9。)

我的简单解决方案

在我的例子中,要测试的条件是一个或多个feature flags。我已经有一个 FeatureFlags Component,所以我将它注入到 tasklet 中并在 execute():

开始时测试了前置条件
override fun execute(contribution: StepContribution, chunkContext: ChunkContext): RepeatStatus? {
    if (!featureFlags.stepShouldRun) {
        contribution.exitStatus = ExitStatus("SKIPPED BASED ON FEATURE FLAGS")
        return RepeatStatus.FINISHED
    }
    return execute()
}

fun execute(): RepeatStatus {
    // the actual step logic goes here
    // add StepContribution as a param if necessary
}