如何从 StepExecutionListener afterStep 方法停止 Spring 批处理作业?

How to halt a Spring Batch job from a StepExecutionListener afterStep method?

我已使用此方法从之前的步骤成功停止了作业。

public class FirstListener implements StepExecutionListener {

@Override
public void beforeStep(StepExecution stepExecution) {
    boolean shouldRun = shouldJobRun();
    if (!shouldRun) {
        // listeners will still work, but any other step logic (reader, processor, writer) will not happen
        stepExecution.setTerminateOnly();
        stepExecution.setExitStatus(new ExitStatus("STOPPED", "Job should not be run right now."));
        LOGGER.warn(duplicate_message);
    }
}

代码已针对 brevity/clarity 进行了删减,但这就是要点。调用 stepExecution.setTerminateOnly()stepExecution.setExitStatus() 足以让 Spring Batch 停止作业并且不执行任何后续步骤。状态已正确记录在 BATCH_JOB_EXECUTION

EXIT_MESSAGE                                              STATUS
org.springframework.batch.core.JobInterruptedException    STOPPED

但是,afterStep 方法中的相同方法被翻转并无法识别。状态被记录为 COMPLETED 并且所有后续步骤都按照他们的快乐方式进行(最终以他们自己可怕的方式失败,因为 afterStep 中的故障检测正在检测故障,因此他们不必这样做)。

public class SecondListener implements StepExecutionListener {

@Override
public ExitStatus afterStep(StepExecution stepExecution) {
    if (stepExecution.getExitStatus().getExitCode().equals(ExitStatus.STOPPED.getExitCode())) {
        return stepExecution.getExitStatus();
    }

    if (everythingIsOkay()) {
         return stepExecution.getExitStatus();
    }

    String failureMessage = "Something bad happened.";
    LOGGER.error(failureMessage);
    ExitStatus exitStatus = new ExitStatus(ExitStatus.FAILED.getExitCode(), failureMessage);
    stepExecution.setExitStatus(exitStatus);
    stepExecution.setTerminateOnly();
    return exitStatus;
}

这是我能想到的唯一问题:使用复合侦听器,两个侦听器处于同一步骤。

@Bean(name = "org.springframework.batch.core.StepExecutionListener-compositeListener")
@StepScope
public StepExecutionListener compositeListener() {
    CompositeStepExecutionListener listener = new CompositeStepExecutionListener();
    List<StepExecutionListener> listeners = Lists.newArrayList(secondListener());

    if (jobShouldHaveFirstListener()) {
        listeners.add(0, firstListener()); // prepend; delegates are called in order
    }

    listener.setListeners(listeners.toArray());
    return listener;
}

public Step firstStep() {
    return stepBuilderFactory.get("firstStep")
            .listener(compositeListener)
            // Small batch size for frequency capping, which happens in the writer, before analytics get written
            .<Recipient, Recipient>chunk(500)
            .reader(rawRecipientInputFileItemReader)
            .processor(recipientItemProcessor)
            .writer(recipientWriter)
            .throttleLimit(2)
            .build();
}

@Bean(name = "org.springframework.batch.core.Job-delivery")
public Job deliveryJob() {
    return jobs.get("delivery")
            .preventRestart()
            .start(firstStep)
            .next(deliveryStep)
            .next(handleSentStep)
            .listener(failedCleanupListener)
            .build();
}

我还能做些什么来正确停止执行?

经过多次试验,我发现以下流程定义将允许从 StepListener 正确停止作业,而无需在第一步之后执行步骤。

return jobs.get("delivery")
           .preventRestart()
           .listener(failedCleanupListener)
           .flow(firstStep)
           .next(deliveryStep)
           .next(handleSentStep)
           .end()
           .build();

主要区别在于将 start() 更改为 flow() 并添加从 .start 方法返回的 FlowBuilder.end() method call to the end of the builder chain. The SimpleJobBuilder class 不公开类似 end() 方法。

我不知道为什么这会在作业执行的内部产生如此大的差异,我很乐意将一些要点奖励给可以阐明 什么 实际差异的人为什么 使用 SimpleJobBuilder 忽略它的步骤执行状态代码。但我发现了一些有用的东西,这就是现在最重要的。