英文:
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<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 ouput file is always empty?
答案1
得分: 1
在使用 @Configuration
类时,对于 @Bean
方法,最好尽量具体地指定类型。因为在加载时使用该类型信息来确定要执行哪些回调或创建哪个代理。
当添加 @StepScope
时,会为方法的参数类型创建一个懒惰代理。在你的情况下,参数类型是 ItemReader<Contact>
。如果你查看 FlatFileItemReader 的 javadoc,你会看到它实现了更多接口。这些接口是 Spring Batch 需要的,用于执行适当的回调以初始化读取器。但是如果它是一个 ItemReader
,那些回调就不存在。
@Bean
@StepScope
public BeanIOFlatFileItemReader<Contact> reader(@Value("#{jobParameters['fileName']}") String fileName) {
BeanIOFlatFileItemReader<Contact> reader = new BeanIOFlatFileItemReader<>();
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<Contact> reader) {
return stepBuilderFactory.get("step")
.<Contact, Contact> 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<Contact>
. 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<Contact> reader(@Value("#{jobParameters['fileName']}") String fileName) {
BeanIOFlatFileItemReader<Contact> reader = new BeanIOFlatFileItemReader<>();
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<Contact> reader) {
return stepBuilderFactory.get("step")
.<Contact, Contact> chunk(10)
.reader(reader)
.processor(contactItemProcessor)
.writer(writer())
.build();
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论