首页 > 解决方案 > 使用 @StepScope 时 Spring Batch 重启功能不起作用

问题描述

我想使用 Spring Batch (v3.0.9) 重新启动功能,以便在JobInstance重新启动时流程步骤从最后一个失败的块点向前读取。只要我不对我的bean 方法使用@StepScope注释,我的重启就可以正常工作。 我正在使用,以便我可以进行后期绑定以获取我的bean 方法 如果我在 bean 方法上使用注释,则重新启动不起作用,因为它会创建新实例(范围 = step,name = scopedTarget.myBatisPagingItemReader)。 如果我使用 stepscope,我的 myBatisPagingItemReader 是否可以从上次失败中设置 read.count 以重新启动工作? myBatisPagingItemReader
@StepScopeJobParametersmyBatisPagingItemReader@Value("#{jobParameters['run-date']}"))
@StepScopemyBatisPagingItemReader()

我已经用下面的例子解释了这个问题。

@Configuration
@EnableBatchProcessing
public class BatchConfig {
    @Bean
    public Step step1(StepBuilderFactory stepBuilderFactory,
        ItemReader<Model> myBatisPagingItemReader,
        ItemProcessor<Model, Model> itemProcessor,
         ItemWriter<Model> itemWriter) {

         return stepBuilderFactory.get("data-load")
            .<Model, Model>chunk(10)
            .reader(myBatisPagingItemReader)
            .processor(itemProcessor)
             .writer(itemWriter)
             .listener(itemReadListener())
             .listener(new JobParameterExecutionContextCopyListener())
             .build();
   }
   @Bean
   public Job job(JobBuilderFactory jobBuilderFactory, @Qualifier("step1") 
               Step step1) {
           return jobBuilderFactory.get("load-job")
            .incrementer(new RunIdIncrementer())
            .start(step1)
            .listener(jobExecutionListener())
            .build();
   }
   @Bean
   @StepScope
   public ItemReader<Model> myBatisPagingItemReader(
         SqlSessionFactory sqlSessionFactory,
         @Value("#{JobParameters['run-date']}") String runDate) 
  {
     MyBatisPagingItemReader<Model> reader = new 
     MyBatisPagingItemReader<>();
     Map<String, Object> parameterValues = new HashMap<>();
     parameterValues.put("runDate", runDate);
     reader.setSqlSessionFactory(sqlSessionFactory);
     reader.setParameterValues(parameterValues);
     reader.setQueryId("query");
     return reader;
   }
}

@Stepscope当我使用注释时重新启动示例myBatisPagingItemReader(),阅读器正在获取 5 条记录,并且我将块大小(提交间隔)设置为 3。

作业实例 - 01 - 作业参数 - 01/02/2019。
chunk-1:
- process record-1
- process record-2
- process record-3
writer - 写入所有 3 条记录
chunk-1 提交成功

块 2:
进程记录 4
进程记录 5 - 抛出和异常
作业完成并设置为“失败”状态

现在,使用相同的作业参数再次重新启动作业。
作业实例 - 01 - 作业参数 - 01/02/2019。
chunk-1:
进程记录 1
进程记录 2
进程记录 3
写入器 - 写入所有 3 条记录
块 1 提交成功

块 2:
进程记录 4
进程记录 5 - 抛出和异常
作业完成并设置为“失败”状态

bean 方法上的@StepScope注释myBatisPagingItemReader()创建了一个新实例,请参见下面的日志消息。
在 scope=step, name=scopedTarget.myBatisPagingItemReader 中创建对象 在 scope=step, name=scopedTarget.myBatisPagingItemReader 中
注册销毁回调
因为它是新实例,所以它从 start 开始,而不是从 chunk-2 开始。

如果我不使用@Stepscope,它将从块 2 重新启动,因为重新启动的作业步骤设置 - MyBatisPagingItemReader.read.count=3。

标签: spring-batch

解决方案


这里的问题是您返回的是一个ItemReader而不是完全限定的类 ( MyBatisPagingItemReader),或者至少是ItemStreamReader. 当您使用 Spring Batch 的 step 范围时,我们会创建一个代理以允许后期初始化。代理基于方法的返回类型(ItemReader在您的情况下)。您遇到的问题是,因为代理是 of ItemReader,所以 Spring Batch 不知道您的 bean 也实现ItemStream了,并且正是接口启用了可重新启动性。默认情况下,Spring Batch 会自动ItemStream为您注册所有类型的 bean(您也可以自己显式注册 bean,但通常不需要)。

为了解决您的问题,以下应该有效(注意返回类型的更改):

   @Bean
   @StepScope
   public MyBatisPagingItemReader<Model> myBatisPagingItemReader(
         SqlSessionFactory sqlSessionFactory,
         @Value("#{JobParameters['run-date']}") String runDate) {

     MyBatisPagingItemReader<Model> reader = 
         new MyBatisPagingItemReader<>();

     Map<String, Object> parameterValues = new HashMap<>();
     parameterValues.put("runDate", runDate);

     reader.setSqlSessionFactory(sqlSessionFactory);
     reader.setParameterValues(parameterValues);
     reader.setQueryId("query");

     return reader;
   }

这就是为什么我建议在可能的情况下,在使用@Bean带注释的方法时,您应该尽可能返回最具体的类型,以让 Spring 尽可能地提供帮助。


推荐阅读