ItemWriter在Spring Batch中使用StepScope注解访问ItemReader中的参数时未产生输出。

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

ItemWriter not producing output when having a StepScope annotation to access parameter in ItemReader in Spring Batch

问题

I'm using a spring boot with spring batch application to test passing a parameter to the ItemReader by using below code, but the content of contact-out.dat file (which is the output file) is empty when I use StepScope annotation. There's no error, it just doesn't produce any result:

@Bean
@StepScope
public ItemReader<Contact> reader(@Value("#{jobParameters['fileName']}") String fileName) {    	    	
    BeanIOFlatFileItemReader<Contact> reader = new BeanIOFlatFileItemReader<>();
    try {    		
        reader.setUseSpringExceptions(true);
        reader.setResource(new FileSystemResource(fileName));
        reader.setStreamName(inputContactStreamName);
        reader.setStreamMapping(new ClassPathResource(beanIoConfigurationXmlPath));
        reader.setStreamFactory(StreamFactory.newInstance());
        reader.getLineNumber();
        reader.afterPropertiesSet();
    } catch (Exception e) {
        log.error("ERROR: An issue occurred in the BeanIO Item Reader:: {} {}", e.getMessage(), e.getStackTrace());
    }
    return reader;    	
}

@Bean
public FlatFileItemWriter<Contact> writer() {
    FlatFileItemWriter<Contact> writer = new FlatFileItemWriter<>();
    writer.setResource(new FileSystemResource(fileOutputContact));
    writer.setLineAggregator(new DelimitedLineAggregator<Contact>() {
        {
            setDelimiter(",");
            setFieldExtractor(new BeanWrapperFieldExtractor<Contact>() {
                {
                    setNames(new String[] { "firstName", "lastName", "street", "city", "state", "zip" });
                }    				
            });
        }
    });
    return writer;
}

@Bean("InputJob")
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Job importUserJob(Step step) {
    return jobBuilderFactory.get("importUserJob")
      .incrementer(new RunIdIncrementer())          
      .flow(step)
      .end()
      .build();
}

@Bean
public Step step(ItemReader<Contact> reader) {    	
    return stepBuilderFactory.get("step")
      .<Contact, Contact> chunk(10)
      .reader(reader(null))          
      .processor(contactItemProcessor)
      .writer(writer())          
      .build();
}

Now, if I remove the StepScope annotation and make below changes then contact-out.dat file has the expected content:

@Bean
//@StepScope
public ItemReader<Contact> reader(/*@Value("#{jobParameters['fileName']}") String fileName*/) {...}

@Bean
public Step step(ItemReader<Contact> reader) {    	
    return stepBuilderFactory.get("step")
      .<Contact, Contact> chunk(10)
      .reader(reader(/*null*/))          
      .processor(contactItemProcessor)
      .writer(writer())          
      .build();
}

What I'm doing wrong here when using StepScope annotation that output file is always empty?

英文:

I'm using a spring boot with spring batch application to test passing a parameter to the ItemReader by using below code, but the content of contact-out.dat file (which is the output file) is empty when I use StepScope annotation. There's no error, it just doesn't produce any result:

@Bean
@StepScope
public ItemReader&lt;Contact&gt; reader(@Value(&quot;#{jobParameters[&#39;fileName&#39;]}&quot;) String fileName) {    	    	
BeanIOFlatFileItemReader&lt;Contact&gt; reader = new BeanIOFlatFileItemReader&lt;&gt;();
try {    		
reader.setUseSpringExceptions(true);
reader.setResource(new FileSystemResource(fileName));
reader.setStreamName(inputContactStreamName);
reader.setStreamMapping(new ClassPathResource(beanIoConfigurationXmlPath));
reader.setStreamFactory(StreamFactory.newInstance());
reader.getLineNumber();
reader.afterPropertiesSet();
} catch (Exception e) {
log.error(&quot;ERROR: An issue occurred in the BeanIO Item Reader:: {} {}&quot;, e.getMessage(), e.getStackTrace());
}
return reader;    	
}    
@Bean
public FlatFileItemWriter&lt;Contact&gt; writer() {
FlatFileItemWriter&lt;Contact&gt; writer = new FlatFileItemWriter&lt;&gt;();
writer.setResource(new FileSystemResource(fileOutputContact));
writer.setLineAggregator(new DelimitedLineAggregator&lt;Contact&gt;() {
{
setDelimiter(&quot;,&quot;);
setFieldExtractor(new BeanWrapperFieldExtractor&lt;Contact&gt;() {
{
setNames(new String[] { &quot;firstName&quot;, &quot;lastName&quot;, &quot;street&quot;, &quot;city&quot;, &quot;state&quot;, &quot;zip&quot; });
}    				
});
}
});
return writer;
}
@Bean(&quot;InputJob&quot;)
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Job importUserJob(Step step) {
return jobBuilderFactory.get(&quot;importUserJob&quot;)
.incrementer(new RunIdIncrementer())          
.flow(step)
.end()
.build();
}
@Bean
public Step step(ItemReader&lt;Contact&gt; reader) {    	
return stepBuilderFactory.get(&quot;step&quot;)
.&lt;Contact, Contact&gt; chunk(10)
.reader(reader(null))          
.processor(contactItemProcessor)
.writer(writer())          
.build();
}

Now, if I remove the StepScope annotation and make below changes then contact-out.dat file has the expected content:

@Bean
//@StepScope
public ItemReader&lt;Contact&gt; reader(/*@Value(&quot;#{jobParameters[&#39;fileName&#39;]}&quot;) String fileName*/) {...}
@Bean
public Step step(ItemReader&lt;Contact&gt; reader) {    	
return stepBuilderFactory.get(&quot;step&quot;)
.&lt;Contact, Contact&gt; chunk(10)
.reader(reader(/*null*/))          
.processor(contactItemProcessor)
.writer(writer())          
.build();
}

What I'm doing wrong here when using StepScope annotation that ouput file is always empty?

答案1

得分: 1

在使用 @Configuration 类时,对于 @Bean 方法,最好尽量具体地指定类型。因为在加载时使用该类型信息来确定要执行哪些回调或创建哪个代理。

当添加 @StepScope 时,会为方法的参数类型创建一个懒惰代理。在你的情况下,参数类型是 ItemReader&lt;Contact&gt;。如果你查看 FlatFileItemReader 的 javadoc,你会看到它实现了更多接口。这些接口是 Spring Batch 需要的,用于执行适当的回调以初始化读取器。但是如果它是一个 ItemReader,那些回调就不存在。

@Bean
@StepScope
public BeanIOFlatFileItemReader&lt;Contact&gt; reader(@Value(&quot;#{jobParameters[&#39;fileName&#39;]}&quot;) String fileName) {             
    BeanIOFlatFileItemReader&lt;Contact&gt; reader = new BeanIOFlatFileItemReader&lt;&gt;();
    reader.setUseSpringExceptions(true);
    reader.setResource(new FileSystemResource(fileName));
    reader.setStreamName(inputContactStreamName);
    reader.setStreamMapping(new ClassPathResource(beanIoConfigurationXmlPath));
    reader.setStreamFactory(StreamFactory.newInstance());
    reader.getLineNumber();
    return reader;          
}

通过这样做,将创建适当的代理,并且所有回调将生效。

在你的配置中,你已经将 ItemReader 作为参数注入了,可以使用它,而不是调用 reader(null)

@Bean
public Step step(BeanIOFlatFileItemReader&lt;Contact&gt; reader) {      
    return stepBuilderFactory.get(&quot;step&quot;)
      .&lt;Contact, Contact&gt; chunk(10)
      .reader(reader)          
      .processor(contactItemProcessor)
      .writer(writer())          
      .build();
}
英文:

When using @Configuration classes the best thing to do for @Bean methods is to be as specific as possible about the type. As this type information is used at load time to determine what callbacks to do or what proxy to create.

When you are adding an @StepScope what happens is that a lazy proxy is created for the argument type of the method. In your case that is an ItemReader&lt;Contact&gt;. If you check the javadoc of the FlatFileItemReader you see that it implements more interfaces. Those interfaces are needed by Spring Batch to do the appropriate callbacks to initialize the reader. However if it is an ItemReader those callbacks aren't there.

@Bean
@StepScope
public BeanIOFlatFileItemReader&lt;Contact&gt; reader(@Value(&quot;#{jobParameters[&#39;fileName&#39;]}&quot;) String fileName) {             
    BeanIOFlatFileItemReader&lt;Contact&gt; reader = new BeanIOFlatFileItemReader&lt;&gt;();
    reader.setUseSpringExceptions(true);
    reader.setResource(new FileSystemResource(fileName));
    reader.setStreamName(inputContactStreamName);
    reader.setStreamMapping(new ClassPathResource(beanIoConfigurationXmlPath));
    reader.setStreamFactory(StreamFactory.newInstance());
    reader.getLineNumber();
    return reader;          
}

With this the proper proxy will be created and all the callbacks will be effective.

In your configuration you are already injecting the ItemReader as an argument use that instead of calling reader(null).

@Bean
public Step step(BeanIOFlatFileItemReader&lt;Contact&gt; reader) {      
    return stepBuilderFactory.get(&quot;step&quot;)
      .&lt;Contact, Contact&gt; chunk(10)
      .reader(reader)          
      .processor(contactItemProcessor)
      .writer(writer())          
      .build();
}

huangapple
  • 本文由 发表于 2023年7月28日 02:06:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/76782384.html
匿名

发表评论

匿名网友

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

确定