15.3 Deep Dive: ItemReader, ItemProcessor, ItemWriter
1. FlatFileItemReader — CSV File Processing
@Bean
public FlatFileItemReader<MemberCsvDto> csvMemberReader() {
return new FlatFileItemReaderBuilder<MemberCsvDto>()
.name("csvMemberReader")
.resource(new ClassPathResource("data/members.csv"))
.delimited()
.names("name", "email", "age") // Map CSV columns to field names
.targetType(MemberCsvDto.class)
.linesToSkip(1) // Skip header row
.build();
}
2. ItemProcessor — Transformation and Filtering
ItemProcessor transforms each item from input to output type. Returning null filters out that item — it won't be passed to the Writer.
@Bean
public ItemProcessor<MemberCsvDto, Member> memberCsvToEntityProcessor() {
return csvDto -> {
// Filter out minors
if (csvDto.getAge() < 18) {
return null; // null = filtered out, not passed to writer
}
return Member.builder()
.name(csvDto.getName())
.email(csvDto.getEmail())
.age(csvDto.getAge())
.build();
};
}
3. CompositeItemProcessor — Chaining Processors
Use this when you want to chain multiple transformation/validation steps in order.
@Bean
public CompositeItemProcessor<MemberCsvDto, Member> compositeProcessor() {
CompositeItemProcessor<MemberCsvDto, Member> processor = new CompositeItemProcessor<>();
processor.setDelegates(List.of(
new AgeFilterProcessor(), // Step 1: Age filter
new EmailValidateProcessor(), // Step 2: Email format validation
new CsvToEntityProcessor() // Step 3: Final entity transformation
));
return processor;
}
4. ItemWriteListener — Post-Write Success/Failure Handling
@Component
public class BatchWriteListener implements ItemWriteListener<Member> {
@Override
public void afterWrite(Chunk<? extends Member> items) {
log.info("Successfully saved {} items", items.size());
}
@Override
public void onWriteError(Exception exception, Chunk<? extends Member> items) {
log.error("Write failed! Failed items: {}", items);
// Send Slack alert or push to a Dead Letter Queue
}
}