How to access execution context from a Spring Batch Step? Error: No context holder available for job scope

huangapple go评论95阅读模式
英文:

How to access execution context from a Spring Batch Step? Error: No context holder available for job scope

问题

  1. 我正在尝试通过将它注入到我的步骤中来在运行时设置我的 Spring Batch 作业的分块大小代码如下所示
  2. @Bean
  3. @JobScope
  4. @Qualifier("myStep")
  5. public Step myStep(@Value("#{jobExecutionContext['chunkSize']}") Integer chunkSize, StepBuilderFactory stepBuilderFactory, ItemReader<Object> reader, ItemWriter<Object> writer, Listener listener) {
  6. return stepBuilderFactory.get("myStep")
  7. .<Object, Object>chunk(chunkSize)
  8. .reader(reader)
  9. .writer(writer)
  10. .listener(listener)
  11. .build();
  12. }

但是我遇到了以下错误:java.lang.IllegalStateException: 作业范围(job scope)下没有可用的上下文持有者

我在网上进行了一些研究,但是无法理解为什么会出现这个异常。我希望能得到帮助,理解这个错误的含义以及如何解决它。谢谢!

  1. <details>
  2. <summary>英文:</summary>
  3. I am trying to set the chunk size of my spring batch job at runtime by injecting it into my step as follows:

@Bean
@JobScope
@Qualifier("myStep")
public Step myStep(@Value("#{jobExecutionContext['chunkSize']}") Integer chunkSize, StepBuilderFactory stepBuilderFactory, ItemReader<Object> reader, ItemWriter<Object> writer, Listener listener) {
return stepBuilderFactory.get("myStep")
.<Object, Object>chunk(chunkSize)
.reader(reader)
.writer(writer)
.listener(listener)
.build();
}

  1. But I am getting the following error: **java.lang.IllegalStateException: No context holder available for job scope**
  2. I&#39;ve done some research online but am not able to understand why I am hitting this exception. I&#39;d appreciate assistance in understanding what this error means and how to resolve it. Thanks!
  3. </details>
  4. # 答案1
  5. **得分**: 1
  6. 我在数据库表中为每个需要运行的作业定义了块大小。块大小通过在作业中的此步骤之前运行的一个任务来放入执行上下文中。
  7. 以下是一个符合预期的快速示例:
  8. ```java
  9. import java.util.Arrays;
  10. import org.springframework.batch.core.Job;
  11. import org.springframework.batch.core.JobParameters;
  12. import org.springframework.batch.core.Step;
  13. import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
  14. import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
  15. import org.springframework.batch.core.configuration.annotation.JobScope;
  16. import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
  17. import org.springframework.batch.core.launch.JobLauncher;
  18. import org.springframework.batch.core.listener.ChunkListenerSupport;
  19. import org.springframework.batch.core.scope.context.ChunkContext;
  20. import org.springframework.batch.item.support.ListItemReader;
  21. import org.springframework.batch.repeat.RepeatStatus;
  22. import org.springframework.beans.factory.annotation.Value;
  23. import org.springframework.context.ApplicationContext;
  24. import org.springframework.context.annotation.AnnotationConfigApplicationContext;
  25. import org.springframework.context.annotation.Bean;
  26. import org.springframework.context.annotation.Configuration;
  27. @Configuration
  28. @EnableBatchProcessing
  29. public class SO64447747WithJobExecutionContext {
  30. @Bean
  31. public Step step1(StepBuilderFactory stepBuilderFactory) {
  32. return stepBuilderFactory.get("step1")
  33. .tasklet((contribution, chunkContext) -> {
  34. // TODO get chunk size from table and put in job execution context
  35. chunkContext.getStepContext().getStepExecution().getJobExecution().getExecutionContext().put("chunkSize", 2);
  36. return RepeatStatus.FINISHED;
  37. })
  38. .build();
  39. }
  40. @Bean
  41. @JobScope
  42. public Step step2(StepBuilderFactory stepBuilderFactory, @Value("#{jobExecutionContext['chunkSize']}") Integer chunkSize) {
  43. return stepBuilderFactory.get("step2")
  44. .<Integer, Integer>chunk(chunkSize)
  45. .reader(new ListItemReader<>(Arrays.asList(1, 2, 3, 4)))
  46. .writer(items -> items.forEach(System.out::println))
  47. .listener(new ChunkListenerSupport() {
  48. @Override
  49. public void beforeChunk(ChunkContext context) {
  50. System.out.println("starting to work on a new chunk of size " + chunkSize);
  51. }
  52. })
  53. .build();
  54. }
  55. @Bean
  56. public Job job(JobBuilderFactory jobBuilderFactory, StepBuilderFactory stepBuilderFactory) {
  57. return jobBuilderFactory.get("job")
  58. .start(step1(stepBuilderFactory))
  59. .next(step2(stepBuilderFactory, null))
  60. .build();
  61. }
  62. public static void main(String[] args) throws Exception {
  63. ApplicationContext context = new AnnotationConfigApplicationContext(SO64447747WithJobExecutionContext.class);
  64. JobLauncher jobLauncher = context.getBean(JobLauncher.class);
  65. Job job = context.getBean(Job.class);
  66. jobLauncher.run(job, new JobParameters());
  67. }
  68. }

这将打印以下输出,没有您提到的错误:

  1. starting to work on a new chunk of size 2
  2. 1
  3. 2
  4. starting to work on a new chunk of size 2
  5. 3
  6. 4

我尝试将块大小作为作业参数传递,但是我看到了与作业范围相关的相同问题。

当作为作业参数传递chunkSize时,相同的方法在没有任何异常的情况下工作:

  1. import java.util.Arrays;
  2. import org.springframework.batch.core.Job;
  3. import org.springframework.batch.core.JobParameters;
  4. import org.springframework.batch.core.JobParametersBuilder;
  5. import org.springframework.batch.core.Step;
  6. import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
  7. import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
  8. import org.springframework.batch.core.configuration.annotation.JobScope;
  9. import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
  10. import org.springframework.batch.core.launch.JobLauncher;
  11. import org.springframework.batch.core.listener.ChunkListenerSupport;
  12. import org.springframework.batch.core.scope.context.ChunkContext;
  13. import org.springframework.batch.item.support.ListItemReader;
  14. import org.springframework.beans.factory.annotation.Value;
  15. import org.springframework.context.ApplicationContext;
  16. import org.springframework.context.annotation.AnnotationConfigApplicationContext;
  17. import org.springframework.context.annotation.Bean;
  18. import org.springframework.context.annotation.Configuration;
  19. @Configuration
  20. @EnableBatchProcessing
  21. public class SO64447747WithJobParameter {
  22. @Bean
  23. @JobScope
  24. public Step step(StepBuilderFactory stepBuilderFactory, @Value("#{jobParameters['chunkSize']}") Integer chunkSize) {
  25. return stepBuilderFactory.get("step")
  26. .<Integer, Integer>chunk(chunkSize)
  27. .reader(new ListItemReader<>(Arrays.asList(1, 2, 3, 4)))
  28. .writer(items -> items.forEach(System.out::println))
  29. .listener(new ChunkListenerSupport() {
  30. @Override
  31. public void beforeChunk(ChunkContext context) {
  32. System.out.println("starting to work on a new chunk of size " + chunkSize);
  33. }
  34. })
  35. .build();
  36. }
  37. @Bean
  38. public Job job(JobBuilderFactory jobBuilderFactory, StepBuilderFactory stepBuilderFactory) {
  39. return jobBuilderFactory.get("job")
  40. .start(step(stepBuilderFactory, null))
  41. .build();
  42. }
  43. public static void main(String[] args) throws Exception {
  44. ApplicationContext context = new AnnotationConfigApplicationContext(SO64447747WithJobParameter.class);
  45. JobLauncher jobLauncher = context.getBean(JobLauncher.class);
  46. Job job = context.getBean(Job.class);
  47. JobParameters jobParameters = new JobParametersBuilder()
  48. .addLong("chunkSize", 2L)
  49. .toJobParameters();
  50. jobLauncher.run(job, jobParameters);
  51. }
  52. }

这将生成与第一个示例相同的输出。

英文:

> I have the chunk sizes defined in a database table for each job that needs to get run. The chunk size gets put into the execution context through a tasklet that runs before this step in the job

Here is a quick example which works as expected:

  1. import java.util.Arrays;
  2. import org.springframework.batch.core.Job;
  3. import org.springframework.batch.core.JobParameters;
  4. import org.springframework.batch.core.Step;
  5. import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
  6. import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
  7. import org.springframework.batch.core.configuration.annotation.JobScope;
  8. import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
  9. import org.springframework.batch.core.launch.JobLauncher;
  10. import org.springframework.batch.core.listener.ChunkListenerSupport;
  11. import org.springframework.batch.core.scope.context.ChunkContext;
  12. import org.springframework.batch.item.support.ListItemReader;
  13. import org.springframework.batch.repeat.RepeatStatus;
  14. import org.springframework.beans.factory.annotation.Value;
  15. import org.springframework.context.ApplicationContext;
  16. import org.springframework.context.annotation.AnnotationConfigApplicationContext;
  17. import org.springframework.context.annotation.Bean;
  18. import org.springframework.context.annotation.Configuration;
  19. @Configuration
  20. @EnableBatchProcessing
  21. public class SO64447747WithJobExecutionContext {
  22. @Bean
  23. public Step step1(StepBuilderFactory stepBuilderFactory) {
  24. return stepBuilderFactory.get(&quot;step1&quot;)
  25. .tasklet((contribution, chunkContext) -&gt; {
  26. // TODO get chunk size from table and put in job execution context
  27. chunkContext.getStepContext().getStepExecution().getJobExecution().getExecutionContext().put(&quot;chunkSize&quot;, 2);
  28. return RepeatStatus.FINISHED;
  29. })
  30. .build();
  31. }
  32. @Bean
  33. @JobScope
  34. public Step step2(StepBuilderFactory stepBuilderFactory, @Value(&quot;#{jobExecutionContext[&#39;chunkSize&#39;]}&quot;) Integer chunkSize) {
  35. return stepBuilderFactory.get(&quot;step2&quot;)
  36. .&lt;Integer, Integer&gt;chunk(chunkSize)
  37. .reader(new ListItemReader&lt;&gt;(Arrays.asList(1, 2, 3, 4)))
  38. .writer(items -&gt; items.forEach(System.out::println))
  39. .listener(new ChunkListenerSupport() {
  40. @Override
  41. public void beforeChunk(ChunkContext context) {
  42. System.out.println(&quot;starting to work on a new chunk of size &quot; + chunkSize);
  43. }
  44. })
  45. .build();
  46. }
  47. @Bean
  48. public Job job(JobBuilderFactory jobBuilderFactory, StepBuilderFactory stepBuilderFactory) {
  49. return jobBuilderFactory.get(&quot;job&quot;)
  50. .start(step1(stepBuilderFactory))
  51. .next(step2(stepBuilderFactory, null))
  52. .build();
  53. }
  54. public static void main(String[] args) throws Exception {
  55. ApplicationContext context = new AnnotationConfigApplicationContext(SO64447747WithJobExecutionContext.class);
  56. JobLauncher jobLauncher = context.getBean(JobLauncher.class);
  57. Job job = context.getBean(Job.class);
  58. jobLauncher.run(job, new JobParameters());
  59. }
  60. }

This prints the following output without the error you mentioned:

  1. starting to work on a new chunk of size 2
  2. 1
  3. 2
  4. starting to work on a new chunk of size 2
  5. 3
  6. 4

> I tried to pass the chunk size as a job parameter but I see the same issue regarding the job scope.

The same approach works without any exception when passing the chunkSize as a job parameter:

  1. import java.util.Arrays;
  2. import org.springframework.batch.core.Job;
  3. import org.springframework.batch.core.JobParameters;
  4. import org.springframework.batch.core.JobParametersBuilder;
  5. import org.springframework.batch.core.Step;
  6. import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
  7. import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
  8. import org.springframework.batch.core.configuration.annotation.JobScope;
  9. import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
  10. import org.springframework.batch.core.launch.JobLauncher;
  11. import org.springframework.batch.core.listener.ChunkListenerSupport;
  12. import org.springframework.batch.core.scope.context.ChunkContext;
  13. import org.springframework.batch.item.support.ListItemReader;
  14. import org.springframework.beans.factory.annotation.Value;
  15. import org.springframework.context.ApplicationContext;
  16. import org.springframework.context.annotation.AnnotationConfigApplicationContext;
  17. import org.springframework.context.annotation.Bean;
  18. import org.springframework.context.annotation.Configuration;
  19. @Configuration
  20. @EnableBatchProcessing
  21. public class SO64447747WithJobParameter {
  22. @Bean
  23. @JobScope
  24. public Step step(StepBuilderFactory stepBuilderFactory, @Value(&quot;#{jobParameters[&#39;chunkSize&#39;]}&quot;) Integer chunkSize) {
  25. return stepBuilderFactory.get(&quot;step&quot;)
  26. .&lt;Integer, Integer&gt;chunk(chunkSize)
  27. .reader(new ListItemReader&lt;&gt;(Arrays.asList(1, 2, 3, 4)))
  28. .writer(items -&gt; items.forEach(System.out::println))
  29. .listener(new ChunkListenerSupport() {
  30. @Override
  31. public void beforeChunk(ChunkContext context) {
  32. System.out.println(&quot;starting to work on a new chunk of size &quot; + chunkSize);
  33. }
  34. })
  35. .build();
  36. }
  37. @Bean
  38. public Job job(JobBuilderFactory jobBuilderFactory, StepBuilderFactory stepBuilderFactory) {
  39. return jobBuilderFactory.get(&quot;job&quot;)
  40. .start(step(stepBuilderFactory, null))
  41. .build();
  42. }
  43. public static void main(String[] args) throws Exception {
  44. ApplicationContext context = new AnnotationConfigApplicationContext(SO64447747WithJobParameter.class);
  45. JobLauncher jobLauncher = context.getBean(JobLauncher.class);
  46. Job job = context.getBean(Job.class);
  47. JobParameters jobParameters = new JobParametersBuilder()
  48. .addLong(&quot;chunkSize&quot;, 2L)
  49. .toJobParameters();
  50. jobLauncher.run(job, jobParameters);
  51. }
  52. }

This gives the same output as the first example.

huangapple
  • 本文由 发表于 2020年10月20日 22:53:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/64447747.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定