【问题标题】:Java - How to set ExitCode in SpringBatch ApplicationJava - 如何在 Spring Batch 应用程序中设置退出代码
【发布时间】:2018-07-20 08:27:34
【问题描述】:

我正在制作一个带有 tasklet 和处理器的 Spring Batch 应用程序。 在处理器中我抛出异常,我的需要是根据我抛出的异常来 System.exit() 应用程序。

但我不知道该怎么做。

感谢您的回复

【问题讨论】:

    标签: java spring spring-boot spring-batch


    【解决方案1】:

    我的需要是根据我抛出的异常System.exit()应用程序

    我不建议调用 System.exit 来终止您的应用程序,因为这会在没有正常关闭进程和清理资源的情况下终止您的工作。

    如果您使用CommandLineJobRunner,您可以提供自定义ExitCodeMapper。见这里:https://docs.spring.io/spring-batch/4.0.x/reference/html/job.html#exitCodes

    如果使用Spring Boot,需要调用System.exit,返回值为SpringApplication。您可以在此处找到示例:https://github.com/spring-projects/spring-boot/blob/master/spring-boot-samples/spring-boot-sample-batch/src/main/java/sample/batch/SampleBatchApplication.java#L67,更多详细信息请参见此处的参考文档:https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-application-exit

    现在为了能够在处理器发生异常时自定义作业的退出代码,您需要:

    • 在作业级别传播此信息。这可以通过访问处理器中的作业执行上下文,然后在作业侦听器中使用它来实现
    • 使用自定义的ExitCodeGenerator,通过Spring Boot发送的JobExectutionEvent获取作业执行权限并自定义退出代码

    这是一个例子:

    @Configuration
    @EnableBatchProcessing
    public class MyJob {
    
    @Autowired
    private JobBuilderFactory jobs;
    
    @Autowired
    private StepBuilderFactory steps;
    
    @Bean
    public Step step() {
        return steps.get("step")
                .<Integer, Integer>chunk(2)
                .reader(new ListItemReader<>(Arrays.asList(1, 2, 3, 4)))
                .processor(new ItemProcessor<Integer, Integer>() {
    
                    private StepExecution stepExecution;
    
                    @BeforeStep
                    public void beforeStep(StepExecution stepExecution) {
                        this.stepExecution = stepExecution;
                    }
    
                    @Override
                    public Integer process(Integer item) {
                        if (item.equals(3)) {
                            stepExecution.getJobExecution().getExecutionContext().put("failure", "item not accepted");
                            throw new IllegalArgumentException("3 is not accepted");
                        }
                        return item;
                    }
                })
                .writer(items -> {
                    for (Integer item : items) {
                        System.out.println("item = " + item);
                    }
                })
                .build();
    }
    
    @Bean
    public Job job() {
        return jobs.get("job")
                .start(step())
                .listener(new JobExecutionListenerSupport() {
                    @Override
                    public void afterJob(JobExecution jobExecution) {
                        String failure = jobExecution.getExecutionContext().getString("failure");
                        if (failure != null && failure.equalsIgnoreCase("item not accepted")) {
                            jobExecution.setExitStatus(new ExitStatus("Not Accepted"));
                        }
                    }
                })
                .build();
    }
    
    @Bean
    public ExitCodeGenerator exitCodeGenerator () {
        return new MyExitCodeGenerator();
    }
    
    static class MyExitCodeGenerator implements ExitCodeGenerator, ApplicationListener<JobExecutionEvent> {
    
        private JobExecution jobExecution;
    
        @Override
        public int getExitCode() {
            if (jobExecution.getExitStatus().getExitCode().equalsIgnoreCase("Not Accepted")) {
                return 42;
            }
            return 0;
        }
    
        @Override
        public void onApplicationEvent(JobExecutionEvent jobExecutionEvent) {
            this.jobExecution = jobExecutionEvent.getJobExecution();
        }
       }
    
    }
    

    如果你运行这个:

    @SpringBootApplication
    public class DemoExitCodeApplication {
    
    public static void main(String[] args) {
        System.exit(
                SpringApplication.exit(
                    SpringApplication.run(DemoExitCodeApplication.class, args)
                )
        );
       }
    }
    

    JVM的退出码是42,这是处理器抛出异常时设置的。

    【讨论】:

    【解决方案2】:

    您可以在article 中找到一个很好的解释。一种方法是提供 ExitCodeExceptionMapper bean 以将异常映射到某些特定的退出代码:

    @Bean
    ExitCodeExceptionMapper exceptionMapper() {
        return exception -> {
            if (exception.getCause() instanceof NumberFormatException) {
                return 80;
            }
            return 1;
        };
    }
    

    更新

    这个小例子应该很清楚:

    @SpringBootApplication
    public class DemoApplication implements CommandLineRunner {
      @Bean
      ExitCodeExceptionMapper exitCodeExceptionMapper() {
        return exception -> {
          if (exception.getCause() instanceof MyException) {
            return 10;
          }
          return 1;
        };
      }
    
      public static void main(String[] args) {
        System.exit(SpringApplication
            .exit(SpringApplication.run(DemoApplication.class, args)));
      }
    
      @Override
      public void run(String... args) throws Exception {
        throw new MyException();
      }
    
      private class MyException extends RuntimeException {}
    }
    

    然后我执行以下命令来运行应用程序:

    mvn package
    java -jar target/demo-1.0.0-SNAPSHOT.jar
    

    在此命令之后,我可以使用以下命令检查退出代码:

    echo Exit Code is %errorlevel%
    

    然后它返回:

    Exit Code is 10
    

    【讨论】:

    • 您好 Sergii,感谢您的回复,我尝试在我的 Application 类中添加 ExitCodeExceptionMapper 但似乎从未触发 getExitCode ...
    • 我只有一个 main 方法,用于运行我的 BatchConfiguration 类的 SpringApplication.run()。
    • @Neewd 我添加了一个完整的示例。
    猜你喜欢
    • 2020-12-01
    • 2022-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多