如何在 Spring Batch 中从 ItemReader 访问作业参数?

发布于 2024-11-08 18:48:45 字数 1045 浏览 0 评论 0 原文

这是我的 job.xml 的一部分:

<job id="foo" job-repository="job-repository">
  <step id="bar">
    <tasklet transaction-manager="transaction-manager">
      <chunk commit-interval="1"
        reader="foo-reader" writer="foo-writer"
      />
    </tasklet>
  </step>
</job>

这是项目阅读器:

import org.springframework.batch.item.ItemReader;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component("foo-reader")
public final class MyReader implements ItemReader<MyData> {
  @Override
  public MyData read() throws Exception {
    //...
  }
  @Value("#{jobParameters['fileName']}")
  public void setFileName(final String name) {
    //...
  }
}

这是 Spring Batch 在运行时所说的内容:

Field or property 'jobParameters' cannot be found on object of 
type 'org.springframework.beans.factory.config.BeanExpressionContext'

这里出了什么问题?在哪里可以阅读有关 Spring 3.0 中这些机制的更多信息?

This is part of my job.xml:

<job id="foo" job-repository="job-repository">
  <step id="bar">
    <tasklet transaction-manager="transaction-manager">
      <chunk commit-interval="1"
        reader="foo-reader" writer="foo-writer"
      />
    </tasklet>
  </step>
</job>

This is the item reader:

import org.springframework.batch.item.ItemReader;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component("foo-reader")
public final class MyReader implements ItemReader<MyData> {
  @Override
  public MyData read() throws Exception {
    //...
  }
  @Value("#{jobParameters['fileName']}")
  public void setFileName(final String name) {
    //...
  }
}

This is what Spring Batch is saying in runtime:

Field or property 'jobParameters' cannot be found on object of 
type 'org.springframework.beans.factory.config.BeanExpressionContext'

What's wrong here? Where I can read more about these mechanisms in Spring 3.0?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

空城缀染半城烟沙 2024-11-15 18:48:46

这可能是一种更简单的方法:

@Configuration
@Setter
@StepScope
public  class Reader extends FlatFileItemReader<Object> {

public Reader(@Value("#{jobParameters['filePath']}") String resource){
    setResource(new FileSystemResource(resource));
   }

}

This could be an easier manner to do it:

@Configuration
@Setter
@StepScope
public  class Reader extends FlatFileItemReader<Object> {

public Reader(@Value("#{jobParameters['filePath']}") String resource){
    setResource(new FileSystemResource(resource));
   }

}
躲猫猫 2024-11-15 18:48:46

您是否将作业参数正确地声明为 bean 的映射?

或者您是否不小心实例化了 JobParameters 对象,它没有文件名的 getter?

有关表达式语言的更多信息,您可以在 Spring 文档 这里

Did you declare the jobparameters as map properly as bean?

Or did you perhaps accidently instantiate a JobParameters object, which has no getter for the filename?

For more on expression language you can find information in Spring documentation here.

她比我温柔 2024-11-15 18:48:45

如前所述,您的读者需要处于“步骤”范围内。您可以通过 @Scope("step") 注释来完成此操作。如果您将该注释添加到阅读器中,它应该对您有用,如下所示:

import org.springframework.batch.item.ItemReader;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component("foo-reader")
@Scope("step")
public final class MyReader implements ItemReader<MyData> {
  @Override
  public MyData read() throws Exception {
    //...
  }

  @Value("#{jobParameters['fileName']}")
  public void setFileName(final String name) {
    //...
  }
}

默认情况下此范围不可用,但如果您使用 batch XML 命名空间,则该范围将可用。如果不是,请将以下内容添加到 Spring 配置中将使范围可用,按照 Spring Batch 文档

<bean class="org.springframework.batch.core.scope.StepScope" />

As was stated, your reader needs to be 'step' scoped. You can accomplish this via the @Scope("step") annotation. It should work for you if you add that annotation to your reader, like the following:

import org.springframework.batch.item.ItemReader;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component("foo-reader")
@Scope("step")
public final class MyReader implements ItemReader<MyData> {
  @Override
  public MyData read() throws Exception {
    //...
  }

  @Value("#{jobParameters['fileName']}")
  public void setFileName(final String name) {
    //...
  }
}

This scope is not available by default, but will be if you are using the batch XML namespace. If you are not, adding the following to your Spring configuration will make the scope available, per the Spring Batch documentation:

<bean class="org.springframework.batch.core.scope.StepScope" />
大姐,你呐 2024-11-15 18:48:45

如果您想定义 ItemReader 实例和您的 Step 单个 JavaConfig 类中的实例。您可以使用 @StepScope@Value 注释,例如:

@Configuration
public class ContributionCardBatchConfiguration {

   private static final String WILL_BE_INJECTED = null;

   @Bean
   @StepScope
   public FlatFileItemReader<ContributionCard> contributionCardReader(@Value("#{jobParameters['fileName']}")String contributionCardCsvFileName){

     ....
   }

   @Bean
   Step ingestContributionCardStep(ItemReader<ContributionCard> reader){
         return stepBuilderFactory.get("ingestContributionCardStep")
                 .<ContributionCard, ContributionCard>chunk(1)
                 .reader(contributionCardReader(WILL_BE_INJECTED))
                 .writer(contributionCardWriter())
                 .build();
    }
}

技巧是将 null 值传递给 itemReader,因为它将通过 @Value("#{ jobParameters['fileName']}") 注释。

感谢 Tobias Flohre 的文章:Spring Batch 2.2 – JavaConfig 第 2 部分:JobParameters、ExecutionContext 和 StepScope

If you want to define your ItemReader instance and your Step instance in a single JavaConfig class. You can use the @StepScope and the @Value annotations such as:

@Configuration
public class ContributionCardBatchConfiguration {

   private static final String WILL_BE_INJECTED = null;

   @Bean
   @StepScope
   public FlatFileItemReader<ContributionCard> contributionCardReader(@Value("#{jobParameters['fileName']}")String contributionCardCsvFileName){

     ....
   }

   @Bean
   Step ingestContributionCardStep(ItemReader<ContributionCard> reader){
         return stepBuilderFactory.get("ingestContributionCardStep")
                 .<ContributionCard, ContributionCard>chunk(1)
                 .reader(contributionCardReader(WILL_BE_INJECTED))
                 .writer(contributionCardWriter())
                 .build();
    }
}

The trick is to pass a null value to the itemReader since it will be injected through the @Value("#{jobParameters['fileName']}") annotation.

Thanks to Tobias Flohre for his article : Spring Batch 2.2 – JavaConfig Part 2: JobParameters, ExecutionContext and StepScope

披肩女神 2024-11-15 18:48:45

很晚了,但您也可以通过注释 @BeforeStep 方法来做到这一点:

@BeforeStep
    public void beforeStep(final StepExecution stepExecution) {
        JobParameters parameters = stepExecution.getJobExecution().getJobParameters();
        //use your parameters
}

Pretty late, but you can also do this by annotating a @BeforeStep method:

@BeforeStep
    public void beforeStep(final StepExecution stepExecution) {
        JobParameters parameters = stepExecution.getJobExecution().getJobParameters();
        //use your parameters
}
内心荒芜 2024-11-15 18:48:45

为了能够使用 jobParameters,我认为您需要将阅读器定义为范围“步骤”,但我不确定您是否可以使用注释来做到这一点。

使用 xml-config 会像这样:

<bean id="foo-readers" scope="step"
  class="...MyReader">
  <property name="fileName" value="#{jobExecutionContext['fileName']}" />
</bean>

进一步查看 Spring Batch 文档

也许它可以通过使用 @Scope 并在 xml-config 中定义步骤范围来工作:

<bean class="org.springframework.batch.core.scope.StepScope" />

To be able to use the jobParameters I think you need to define your reader as scope 'step', but I am not sure if you can do it using annotations.

Using xml-config it would go like this:

<bean id="foo-readers" scope="step"
  class="...MyReader">
  <property name="fileName" value="#{jobExecutionContext['fileName']}" />
</bean>

See further at the Spring Batch documentation.

Perhaps it works by using @Scope and defining the step scope in your xml-config:

<bean class="org.springframework.batch.core.scope.StepScope" />
阳光的暖冬 2024-11-15 18:48:45

再补充一个示例,您可以访问 JavaConfig 类中的所有作业参数:

@Bean
@StepScope
public ItemStreamReader<GenericMessage> reader(@Value("#{jobParameters}") Map<String,Object> jobParameters){
          ....
}

Complement with an additional example, you can access all job parameters in JavaConfig class:

@Bean
@StepScope
public ItemStreamReader<GenericMessage> reader(@Value("#{jobParameters}") Map<String,Object> jobParameters){
          ....
}
瑕疵 2024-11-15 18:48:45

在执行作业时,我们需要传递作业参数,如下所示:

JobParameters jobParameters= new JobParametersBuilder().addString("file.name", "filename.txt").toJobParameters();   
JobExecution execution = jobLauncher.run(job, jobParameters);  

通过使用表达式语言,我们可以导入值,如下所示:

 #{jobParameters['file.name']}

While executing the job we need to pass Job parameters as follows:

JobParameters jobParameters= new JobParametersBuilder().addString("file.name", "filename.txt").toJobParameters();   
JobExecution execution = jobLauncher.run(job, jobParameters);  

by using the expression language we can import the value as follows:

 #{jobParameters['file.name']}
歌枕肩 2024-11-15 18:48:45

“技术”写作。

配置类。

    @Autowired
    @Qualifier("testReader")
    private testReader reader;


    @Bean(name = "testJob")
    public Job testJob(@Autowired @Qualifier("testStep") Step step) {
        return jobBuilderFactory
                .get("testJob")
                .incrementer(new RunIdIncrementer())
//                .listener(new JobCompletionListener())
                .start(step)
                .build();

    }

    @Bean("testStep")
    @JobScope
    public Step testStep(@Value("#{jobParameters['key']}") String key) {
        return stepBuilderFactory.get("testStep")
                .<UniqueUserVO, List<UniqueUser>>chunk(500)
                .reader(reader.setKey(key).reader())
                .processor(processor.processor())
                .writer(writer.writer())
                .build();

    }

读者接口类。

public interface Reader<T> {

    /**
     * reader 
     * @return
     */
    ItemReader<T> reader();
}

读者班

@Component
public class TestReader implements Reader<UniqueUserVO> {
    private String key;

    public TestReader setKey(String key) {
        this.key= key;
        return this;
    }
    @Override
    public ItemReader<UniqueUserVO> reader() {
       xxxxx
    }
}

'technical' writing.

configuration class.

    @Autowired
    @Qualifier("testReader")
    private testReader reader;


    @Bean(name = "testJob")
    public Job testJob(@Autowired @Qualifier("testStep") Step step) {
        return jobBuilderFactory
                .get("testJob")
                .incrementer(new RunIdIncrementer())
//                .listener(new JobCompletionListener())
                .start(step)
                .build();

    }

    @Bean("testStep")
    @JobScope
    public Step testStep(@Value("#{jobParameters['key']}") String key) {
        return stepBuilderFactory.get("testStep")
                .<UniqueUserVO, List<UniqueUser>>chunk(500)
                .reader(reader.setKey(key).reader())
                .processor(processor.processor())
                .writer(writer.writer())
                .build();

    }

reader interface class.

public interface Reader<T> {

    /**
     * reader 
     * @return
     */
    ItemReader<T> reader();
}

reader class

@Component
public class TestReader implements Reader<UniqueUserVO> {
    private String key;

    public TestReader setKey(String key) {
        this.key= key;
        return this;
    }
    @Override
    public ItemReader<UniqueUserVO> reader() {
       xxxxx
    }
}

天赋异禀 2024-11-15 18:48:45

您可以在项目阅读器内使用 @BeforeStep 注释的方法内使用 StepExecution 上下文来获取作业参数和任务。将其设置为一个变量,您可以在 read 方法中使用该变量。

就我而言,我写了这样的东西:-

@Component
@RequiredArgsConstructor
public class SpelItemReader implements ItemReader<BatchRequest>{

private String requestId;


@Override
public BatchRequest read() {
   //access the request-id variable here
}

@BeforeStep
public void beforeStep(StepExecution stepExecution) {
    requestId = stepExecution.getJobParameters().getString("requestId");
}

}

You can use StepExecution context inside a method annotated with @BeforeStep annotation inside your item reader to get the Job parameters & set it to a variable, which you can use inside your read method.

In my case I've written something like this :-

@Component
@RequiredArgsConstructor
public class SpelItemReader implements ItemReader<BatchRequest>{

private String requestId;


@Override
public BatchRequest read() {
   //access the request-id variable here
}

@BeforeStep
public void beforeStep(StepExecution stepExecution) {
    requestId = stepExecution.getJobParameters().getString("requestId");
}

}

伤痕我心 2024-11-15 18:48:45

让我们考虑一个场景,您需要访问 ItemWriter 或 ItemReader 中的 JobParameters 以设置值(例如用于写入的文件路径)。在这种情况下,您可以利用 JobParameterExecutionContextCopyListener

假设您有一个非 bean 类,您可以在其中创建 STEP 读取器/写入器。然后,您可以创建一个 JobParameterExecutionContextCopyListener 并指定要提取的键,例如 myDate

private TaskletStep internalBuild() {
    try {
        JobParameterExecutionContextCopyListener listener = new JobParameterExecutionContextCopyListener();
        listener.setKeys(new String []{"myDate"});
        return new StepBuilder("MY_STEP", jobRepository)
                .<T, T>chunk(batchSize, transactionManager)
                .reader(itemReader())
                .processor(new PassThroughItemProcessor<>())
                .writer(itemWriter())
                .listener(listener)
                .build();
    } catch (IOException e) {
        throw new SqlToCsvException(e);
    }
}

接下来,在您的编写器(例如 FlatFileItemWriter)中,如果您想根据从 JobParameters 获取的日期设置资源,您可以重写 open 方法来处理此逻辑:

    public FlatFileItemWriter itemWriter() {
    FlatFileItemWriter writer = new FlatFileItemWriter<T>(){
        // Override the resource given from the executionContext.
        @Override
        public void open(ExecutionContext executionContext) throws ItemStreamException {
            this.setResource(new FileSystemResource(getMyFileNameFromDate((LocalDate) executionContext.get("myDate"))));
            super.open(executionContext);
        }
    };

    // Other configurations for the writer...

    return writer;
}

此方法允许您根据作业执行期间提供的 JobParameter(例如,日期)动态设置资源(在本例中为文件路径)。

Let's consider a scenario where you need to access JobParameters in an ItemWriter or ItemReader to set values like the file path for writing. In this case, you can utilize a JobParameterExecutionContextCopyListener.

Suppose you have a non-bean class where you create your STEP reader/writer. You can then create a JobParameterExecutionContextCopyListener and specify the keys you want to extract, like myDate:

private TaskletStep internalBuild() {
    try {
        JobParameterExecutionContextCopyListener listener = new JobParameterExecutionContextCopyListener();
        listener.setKeys(new String []{"myDate"});
        return new StepBuilder("MY_STEP", jobRepository)
                .<T, T>chunk(batchSize, transactionManager)
                .reader(itemReader())
                .processor(new PassThroughItemProcessor<>())
                .writer(itemWriter())
                .listener(listener)
                .build();
    } catch (IOException e) {
        throw new SqlToCsvException(e);
    }
}

Next, in your writer (e.g., FlatFileItemWriter), if you want to set the resource based on the date obtained from the JobParameters, you can override the open method to handle this logic:

    public FlatFileItemWriter itemWriter() {
    FlatFileItemWriter writer = new FlatFileItemWriter<T>(){
        // Override the resource given from the executionContext.
        @Override
        public void open(ExecutionContext executionContext) throws ItemStreamException {
            this.setResource(new FileSystemResource(getMyFileNameFromDate((LocalDate) executionContext.get("myDate"))));
            super.open(executionContext);
        }
    };

    // Other configurations for the writer...

    return writer;
}

This approach allows you to dynamically set the resource (file path in this case) based on the JobParameter (e.g., date) provided during the job execution.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文