【问题标题】:Spring Batch Conditional Flow Not executing the else partSpring Batch条件流不执行else部分
【发布时间】:2018-09-18 16:27:33
【问题描述】:

我正在尝试使用 Spring 批处理来实现下图中显示的流程。我指的是https://docs.spring.io/spring-batch/4.0.x/reference/pdf/spring-batch-reference.pdf 第 85 页上的 Java 配置,其中谈到了 Java 配置。

由于某种原因,当 Decider 返回 TYPE2 时,批处理以 Failed State 结束,没有任何错误消息。以下是我工作的java配置:

jobBuilderFactory.get("myJob")
            .incrementer(new RunIdIncrementer())
            .preventRestart()
            .start(firstStep())
            .next(typeDecider()).on("TYPE1").to(stepType1()).next(lastStep())
            .from(typeDecider()).on("TYPE2").to(stepType2()).next(lastStep())
            .end()
            .build();

我认为 java 配置有问题,尽管它与 Spring 文档匹配。流程在这里可能很有用,但我相信没有它会有办法。关于如何实现这一点的任何想法?

【问题讨论】:

    标签: java spring spring-integration spring-batch


    【解决方案1】:

    您不仅需要定义从决策者到后续步骤的流程,还需要定义从stepType1stepType2lastStep 的流程。这是一个例子:

    import org.springframework.batch.core.Job;
    import org.springframework.batch.core.JobParameters;
    import org.springframework.batch.core.Step;
    import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
    import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
    import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
    import org.springframework.batch.core.job.flow.FlowExecutionStatus;
    import org.springframework.batch.core.job.flow.JobExecutionDecider;
    import org.springframework.batch.core.launch.JobLauncher;
    import org.springframework.batch.repeat.RepeatStatus;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    @EnableBatchProcessing
    public class MyJob {
    
        @Autowired
        private JobBuilderFactory jobs;
    
        @Autowired
        private StepBuilderFactory steps;
    
        @Bean
        public Step firstStep() {
            return steps.get("firstStep")
                    .tasklet((contribution, chunkContext) -> {
                        System.out.println("firstStep");
                        return RepeatStatus.FINISHED;
                    })
                    .build();
        }
    
        @Bean
        public JobExecutionDecider decider() {
            return (jobExecution, stepExecution) -> new FlowExecutionStatus("TYPE1"); // or TYPE2
        }
    
        @Bean
        public Step stepType1() {
            return steps.get("stepType1")
                    .tasklet((contribution, chunkContext) -> {
                        System.out.println("stepType1");
                        return RepeatStatus.FINISHED;
                    })
                    .build();
        }
    
        @Bean
        public Step stepType2() {
            return steps.get("stepType2")
                    .tasklet((contribution, chunkContext) -> {
                        System.out.println("stepType2");
                        return RepeatStatus.FINISHED;
                    })
                    .build();
        }
    
        @Bean
        public Step lastStep() {
            return steps.get("lastStep")
                    .tasklet((contribution, chunkContext) -> {
                        System.out.println("lastStep");
                        return RepeatStatus.FINISHED;
                    })
                    .build();
        }
    
        @Bean
        public Job job() {
            return jobs.get("job")
                    .start(firstStep())
                    .next(decider())
                        .on("TYPE1").to(stepType1())
                        .from(decider()).on("TYPE2").to(stepType2())
                        .from(stepType1()).on("*").to(lastStep())
                        .from(stepType2()).on("*").to(lastStep())
                        .build()
                    .build();
        }
    
        public static void main(String[] args) throws Exception {
            ApplicationContext context = new AnnotationConfigApplicationContext(MyJob.class);
            JobLauncher jobLauncher = context.getBean(JobLauncher.class);
            Job job = context.getBean(Job.class);
            jobLauncher.run(job, new JobParameters());
        }
    
    }
    

    打印出来:

    firstStep
    stepType1
    lastStep
    

    如果决策者返回TYPE2,则示例打印:

    firstStep
    stepType2
    lastStep
    

    希望这会有所帮助。

    【讨论】:

    • 感谢 Mahmoud 的详细回复。这以这种方式工作,但对我来说不起作用,因为我有复杂的步骤,比如读取、处理和写入,而不是 tasklet。如果每条路径都有两个工作,我的步骤就可以正常工作。我无法想象为什么它不应该一起工作。当我进一步调试时,我看到 org.springframework.batch.core.job.flow.support.SimpleFlow#transitionMap 的键是 else 情况下的 decision1 和 if 情况下的 decision()。真奇怪。
    • DSL 对于这样的“如果”很糟糕
    【解决方案2】:

    遇到了没有调用 else 部分的类似问题(技术上只调用了第一个配置的 on())

    几乎所有与流程和决策器示例相关的网站都有类似的作业配置,无法弄清楚问题所在。

    经过一番研究,发现了spring是如何维护决策者和决策的。 在高层次上,在初始化应用程序时,spring 根据作业配置维护一个决策对象的决策列表(如 decsion0、decision1 等)。

    当我们调用decisionr() 方法时,它总是为decisioner 返回一个新对象。当它返回一个新对象时,列表只包含每个对象的一个​​映射(即,decision0)并且由于它是一个列表,它总是返回第一个配置的决策。所以这就是为什么只有第一个配置的转换只存在的原因调用。

    解决方案: 不要对决策者进行方法调用,而是为决策者创建一个单吨 bean 并在作业配置中使用它

    例子:

    @Bean
    public JobExecutionDecider stepDecider() {
        return new CustomStepDecider();
    }
    
    

    注入它并在作业创建bean中使用它

    @Bean
    public Job sampleJob(Step step1, Step step2,Step step3,
    JobExecutionDecider stepDecider) {
    
    return jobBuilderFactory.get("sampleJob")                    
            .start(step1)
            .next(stepDecider).on("TYPE1").to(step2)
            .from(stepDecider).on("TYPE2").to(step3)
    
    
    }
    

    希望这会有所帮助。

    【讨论】:

      【解决方案3】:

      创建一个 dummyStep,它将返回 FINISH 状态并跳转到下一个决策者。完成当前步骤后,您需要将流光标重定向到下一个决策程序或虚拟步骤

      .next(copySourceFilesStep())
      .next(firstStepDecider).on(STEP_CONTINUE).to(executeStep_1())
      .from(firstStepDecider).on(STEP_SKIP).to(virtualStep_1())
      
      //-executeStep_2
      .from(executeStep_1()).on(ExitStatus.COMPLETED.getExitCode())
      .to(secondStepDecider).on(STEP_CONTINUE).to(executeStep_2())
      .from(secondStepDecider).on(STEP_SKIP).to(virtualStep_3())
      
      .from(virtualStep_1()).on(ExitStatus.COMPLETED.getExitCode())
      .to(secondStepDecider).on(STEP_CONTINUE).to(executeStep_2())
      .from(secondStepDecider).on(STEP_SKIP).to(virtualStep_3())
      
      //-executeStep_3
      .from(executeStep_2()).on(ExitStatus.COMPLETED.getExitCode())
      .to(thirdStepDecider).on(STEP_CONTINUE).to(executeStep_3())
      .from(thirdStepDecider).on(STEP_SKIP).to(virtualStep_4())
      
      .from(virtualStep_3()).on(ExitStatus.COMPLETED.getExitCode())
      .to(thirdStepDecider).on(STEP_CONTINUE).to(executeStep_3())
      .from(thirdStepDecider).on(STEP_SKIP).to(virtualStep_4())
      

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

      【讨论】:

        猜你喜欢
        • 2014-05-06
        • 1970-01-01
        • 2022-11-14
        • 1970-01-01
        • 1970-01-01
        • 2016-08-31
        • 1970-01-01
        • 2023-01-10
        • 1970-01-01
        相关资源
        最近更新 更多