IT

Spring Batch 再入門2 ~ジョブの実行とジョブパラメータ~

前回は Spring Batch の最小限のプロジェクトを作成し、Bean登録したジョブ1つだけを実行してみました。今回はジョブを複数実行させてみます。

まずはジョブを定期的に実行するクラスを作ります。

@Component
public class SampleJobLauncher {

  @Autowired
  private JobLauncher jobLauncher;

  @Autowired
  private Job sampleJob;
  
  @Scheduled(cron = "*/20 * * * * *")
  public void launchSampleJob() throws JobExecutionException {
    jobLauncher.run(sampleJob, new JobParameters());
  }

}

JobLauncherはジョブを実行するクラスで、@EnableBatchProcessingを付与していると使えるようになるクラスのうちの1つです。runメソッドにジョブとJobParameters(ジョブのパラメータ。newしただけだと空)を渡すとジョブを実行してくれます。

@Scheduledはメソッドの実行をスケジューリングでき、上記の場合はcron式で20秒おきに実行するよう指定しています。@Scheduledはどこかのクラスに@EnableSchedulingを付与していると有効になります。

@SpringBootApplication
@EnableScheduling  // @Scheduledを有効化する
public class SampleBatchApplication {

  public static void main(String[] args) {
    SpringApplication.run(SampleBatchApplication.class, args);
  }

}

この実装だと、まずジョブのBeanが登録された時に1回実行し、その後は20秒おきに実行するように見えますが、実際には以下のように最初の1回以外は「no action to execute」と言われて何も起きません。

実は Spring Batch のジョブはパラメータが同じだと既に実行したものと見なされ、このように実行がスキップされる仕様となっています。なので、同じ処理を定期的に行わせたい場合は次のように毎回異なる適当なパラメータを指定する必要があります。

  private long counter = 0;
  
  @Scheduled(cron = "*/20 * * * * *")
  public void launchSampleJob() throws JobExecutionException {
    jobLauncher.run(sampleJob, new JobParametersBuilder().addLong("key", counter++).toJobParameters());
  }

これをよりスマートに実装する方法として、ジョブのパラメータを一定の規則に従ってインクリメントするJobParametersIncrementerと呼ばれるクラスをジョブにセットするものがあります。

  @Bean
  public Job sampleJob(Step step1, Step step2) {
    return jobBuilderFactory.get("sampleJob").incrementer(new RunIdIncrementer()).start(step1).next(step2).build();
  }
  private JobParameters jobParameters;
  
  @Scheduled(cron = "*/20 * * * * *")
  public void launchSampleJob() throws JobExecutionException {
    jobParameters = sampleJob.getJobParametersIncrementer().getNext(jobParameters);
    jobLauncher.run(sampleJob, jobParameters);
  }

この例ではRunIdIncrementerを使っており、このgetNextメソッドに空のJobParametersあるいはnullを渡すと「run.id=1」がセットされたJobParameterが返され、run.idがセットされたJobParametersを渡すとその値を1増やしたものが返されます。つまり前回使用したJobParametersから次に使用するものが得られる仕組みですね。コードの量は大して変わりませんが、ジョブを実行する側でパラメータの中身を意識する必要がなくなります。

spring:
  batch:
    job:
      enabled: false

最後に、この実装だとジョブのBeanが登録された時と最初の定期実行の時にrun.id=1となって重複してしまうので、Bean登録時にはジョブを実行しないようapplication.ymlに設定します。

これで同じ処理を定期実行するコードの完成です。次回はジョブやステップの実行が Spring Batch の内部でどのように管理されているのかを見ていきたいと思います。