You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Batch:自定义ExitStatus下JobExecutionDecider失效求助

问题:Spring Batch流程执行initialStep后直接失败,未进入决策器及后续步骤

需求说明

  • 通过initialStep读取Job参数中的输入文件名,判断是否包含特定字符串
  • 若包含则进入process步骤,若不包含则进入failedStep

编写的代码

初始步骤与监听器

@Bean
public Step initialStep() {
    return stepBuilderFactory.get("initialStep")
            .tasklet((contribution, chunkContext) -> {
                System.out.println("INTIAL STEP");
                return RepeatStatus.FINISHED;
            }).listener(stepExecutionListener()).build();
}

@Bean
public StepExecutionListener stepExecutionListener() {
    return new MyStepExecutionListener();
}

static class MyStepExecutionListener implements StepExecutionListener {

    @Override
    public void beforeStep(StepExecution stepExecution) {
        System.out.println("MyStepExecutionListener.beforeStep");
    }

    @Override
    public ExitStatus afterStep(StepExecution stepExecution) {
        var inputFileName = stepExecution.getJobParameters().getString("inputFileName");
        System.out.println("MyStepExecutionListener.afterStep");
        if (inputFileName != null && (inputFileName.contains("abc"))) {
            return new ExitStatus("YES", "For processing step");
        }
        return new ExitStatus("NO", "For failed step");
    }
}

决策器代码

@Bean
public JobExecutionDecider decider() {
    return (jobExecution, stepExecution) -> {
        System.out.println(stepExecution.getExitStatus().getExitCode());
        if("YES".equals(stepExecution.getExitStatus().getExitCode())) {
            return new FlowExecutionStatus("YES");
        } else {
            return new FlowExecutionStatus("NO");
        }
    };
}

其他步骤代码

@Bean
public Step process() {
    return stepBuilderFactory.get("process")
            .tasklet((contribution, chunkContext) -> {
                //logic to process
                return RepeatStatus.FINISHED;
            }).build();
}

@Bean
public Step failedStep() {
    return stepBuilderFactory.get("failedStep")
            .tasklet((contribution, chunkContext) -> {
                return RepeatStatus.FINISHED;
            }).build();
}

Job配置代码

@Bean
Job job() throws Exception {
    return jobBuilderFactory
            .get("job")
            .incrementer(new RunIdIncrementer())
            .start(initialStep())
              .next(decider()).on("YES").to(process())
              .from(decider()).on("NO").to(failedStep()).end()
            .build();
}

运行错误日志

2023-04-03 15:41:42.865  INFO 17532 --- [  restartedMain] o.s.b.c.l.support.SimpleJobLauncher      : Job: [FlowJob: [name=job]] launched with the following parameters: [{RunDate=2023-04-03T15:41:41.080044100}]
2023-04-03 15:41:44.656  INFO 17532 --- [  restartedMain] o.s.batch.core.job.SimpleStepHandler     : Executing step: [initialStep]
MyStepExecutionListener.beforeStep
INTIAL STEP
MyStepExecutionListener.afterStep
2023-04-03 15:42:04.078  INFO 17532 --- [  restartedMain] o.s.batch.core.step.AbstractStep         : Step: [initialStep] executed in 19s422ms
2023-04-03 15:42:04.868  INFO 17532 --- [  restartedMain] o.s.b.c.l.support.SimpleJobLauncher      : Job: [FlowJob: [name=job]] completed with the following parameters: [{RunDate=2023-04-03T15:41:41.080044100}] and the following status: [FAILED] in 21s652ms

问题原因与解决方案

问题根源

  1. Spring Batch默认仅将ExitStatus.COMPLETED视为步骤成功状态,你返回的YES/NO属于自定义状态,未被声明为允许流转的状态,因此被判定为步骤失败,直接终止Job。
  2. Job配置的流转链式调用存在逻辑漏洞,未正确衔接initialStep的自定义状态到决策器。

修正方案一:调整Job配置,允许自定义状态流转

修改Job配置,显式声明YES/NO为合法流转状态,确保步骤执行后能进入决策器:

@Bean
Job job() throws Exception {
    return jobBuilderFactory
            .get("job")
            .incrementer(new RunIdIncrementer())
            .start(initialStep())
              .on("YES", "NO").to(decider()) // 允许这两个自定义状态流转到决策器
              .from(decider()).on("YES").to(process())
              .from(decider()).on("NO").to(failedStep())
              .end()
            .build();
}

修正方案二:简化逻辑,直接在决策器中处理参数

无需通过StepListener传递状态,直接在决策器内读取Job参数做判断,避免状态流转问题:

// 重构决策器
@Bean
public JobExecutionDecider decider() {
    return (jobExecution, stepExecution) -> {
        String inputFileName = jobExecution.getJobParameters().getString("inputFileName");
        if (inputFileName != null && inputFileName.contains("abc")) {
            return new FlowExecutionStatus("YES");
        }
        return new FlowExecutionStatus("NO");
    };
}

// 简化Job配置
@Bean
Job job() throws Exception {
    return jobBuilderFactory
            .get("job")
            .incrementer(new RunIdIncrementer())
            .start(initialStep())
              .next(decider())
              .on("YES").to(process())
              .on("NO").to(failedStep())
              .end()
            .build();
}

额外注意事项

  • 运行Job时必须传入inputFileName参数,否则会因参数null进入failedStep分支。
  • 若initialStep无额外业务逻辑,可直接从决策器启动Job,进一步简化代码。

内容的提问来源于stack exchange,提问作者Vamsi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 23:57:02