Listener 종류
각각 인터페이스를 구현받거나 간단하게 어노테이션을 사용하는 방식으로 listener를 설정할 수 있다.
////////////////////////////////////////////////////////
//JobExecutionListener 인터페이스 구현 or 어노테이션 사용
@BeforeJob
public void beforeJob(JobExecution stepExecution) {
log.info(">>>Before Job");
}
@AfterJob
public void afterJob(JobExecution stepExecution) {
log.info(">>>After Job");
}
////////////////////////////////////////////////////////
//StepExecutionListener 인터페이스 구현 or 어노테이션 사용
@BeforeStep
public void beforeStep(StepExecution stepExecution) {
log.info(">>>Before step");
}
@AfterStep
public void afterStep(StepExecution stepExecution) {
log.info(">>>After step");
}
////////////////////////////////////////////////////////
//ChunkListener 인터페이스 구현 or 어노테이션 사용
@BeforeChunk
public void beforeChunk(ChunkContext stepExecution) {
log.info(">>>Before Chunk");
}
@AfterChunk
public void afterChunk(ChunkContext stepExecution) {
log.info(">>>After Chunk");
}
@AfterChunkError
public void afterChunkError(ChunkContext stepExecution) {
log.info(">>>After Chunk Error");
}
////////////////////////////////////////////////////////
//ItemReadListener 인터페이스 구현 or 어노테이션 사용
@BeforeRead
public void beforeRead() {
log.info(">>>Before Read");
}
@AfterRead
public void afterRead(Object item) {
log.info(">>>After Read");
}
@OnReadError
public void onReadError(Exception e) {
log.info(">>>OnReadError");
}
////////////////////////////////////////////////////////
//ItemProcessListener 인터페이스 구현 or 어노테이션 사용
@BeforeProcess
public void beforeProcess(TestDTO testDTO) {
log.info(">>>Before Process");
}
@AfterProcess
public void afterProcess(TestDTO testDTO1, TestDTO testDTO2) {
log.info(">>>After Process");
}
@OnProcessError
public void onProcessError(TestDTO testDTO1, Exception e) {
log.info(">>>OnProcessError");
}
////////////////////////////////////////////////////////
//ItemWriteListener 인터페이스 구현 or 어노테이션 사용
@BeforeWrite
public void beforeWrite(List<TestDTO> list) {
log.info(">>>Before Write");
}
@AfterWrite
public void afterWrite(List<TestDTO> list) {
log.info(">>>After Write");
}
@OnWriteError
public void onWriteError(Exception e, List<TestDTO> list) {
log.info(">>>onWriteError");
}
////////////////////////////////////////////////////////
//SkipListener 인터페이스 구현 or 어노테이션 사용
@OnSkipInRead
public void onSkipInRead(Throwable t) {
log.info(">>>onSkipInRead");
}
@OnSkipInProcess
public void onSkipInProcess(TestDTO item, Throwable t) {
log.info(">>>onSkipInProcess");
}
@OnSkipInWrite
public void onSkipInWrite(TestDTO item, Throwable t) {
log.info(">>>onSkipInWrite");
}
테스트 세팅
테스트를 위해 TestBatchConfig를 만들었다.
@Bean
public Step testStep() throws IOException {
log.info("==================================================");
return stepBuilderFactory.get("testStep")
.<TestDTO, TestDTO>chunk(10)
.reader(testReader())
.processor(testProcessor())
.writer(testWriter())
.listener(testListener)
.build();
}
reader는 특정 폴더에서 모든 txt 파일을 읽어오는 MultiResourceItemReader이고 1번 파일엔 12줄, 2번 파일엔 16줄의 텍스트가 있다.
실행 결과
1. Before Step
2. Before Chunk
3. Before Read, After Read가 10회 반복
4. Before Process, After Process가 10회 반복
5. Before Write, After Write 실행
6. After Chunk
7. Before Chunk
8. Before Read, After Read가 10회 반복
9. Before Process, After Process가 10회 반복
10. Before Write, After Write 실행
11. After Chunk
12. Before Chunk
13. Before Read, After Read가 8회 반복
14. Before Process, After Process가 8회 반복
15. Before Write, After Write 실행
16. After Chunk
17. After Step
파일 2개에 걸쳐있는 총 28라인의 텍스트를 읽기위해 chunk가 10으로 세팅되었고, 이에따라 10회, 10회, 8회씩 read, process를 하고 3회의 write를 하고 있다.
Before step
>>>Before Chunk
>>>>>>Before Read
>>>>>>After Read
>>>>>>Before Read
>>>>>>After Read
>>>>>>Before Read
>>>>>>After Read
>>>>>>Before Read
>>>>>>After Read
>>>>>>Before Read
>>>>>>After Read
>>>>>>Before Read
>>>>>>After Read
>>>>>>Before Read
>>>>>>After Read
>>>>>>Before Read
>>>>>>After Read
>>>>>>Before Read
>>>>>>After Read
>>>>>>Before Read
>>>>>>After Read
>>>>>>>>>Before Process
>>>>>>>>>Processor
>>>>>>>>>After Process
>>>>>>>>>Before Process
>>>>>>>>>Processor
>>>>>>>>>After Process
>>>>>>>>>Before Process
>>>>>>>>>Processor
>>>>>>>>>After Process
>>>>>>>>>Before Process
>>>>>>>>>Processor
>>>>>>>>>After Process
>>>>>>>>>Before Process
>>>>>>>>>Processor
>>>>>>>>>After Process
>>>>>>>>>Before Process
>>>>>>>>>Processor
>>>>>>>>>After Process
>>>>>>>>>Before Process
>>>>>>>>>Processor
>>>>>>>>>After Process
>>>>>>>>>Before Process
>>>>>>>>>Processor
>>>>>>>>>After Process
>>>>>>>>>Before Process
>>>>>>>>>Processor
>>>>>>>>>After Process
>>>>>>>>>Before Process
>>>>>>>>>Processor
>>>>>>>>>After Process
>>>>>>>>>>>>Before Write
>>>>>>>>>>>>Write
>>>>>>>>>>>>After Write
>>>After Chunk
>>>Before Chunk
>>>>>>Before Read
>>>>>>After Read
>>>>>>Before Read
>>>>>>After Read
>>>>>>Before Read
>>>>>>After Read
>>>>>>Before Read
>>>>>>After Read
>>>>>>Before Read
>>>>>>After Read
>>>>>>Before Read
>>>>>>After Read
>>>>>>Before Read
>>>>>>After Read
>>>>>>Before Read
>>>>>>After Read
>>>>>>Before Read
>>>>>>After Read
>>>>>>Before Read
>>>>>>After Read
>>>>>>>>>Before Process
>>>>>>>>>Processor
>>>>>>>>>After Process
>>>>>>>>>Before Process
>>>>>>>>>Processor
>>>>>>>>>After Process
>>>>>>>>>Before Process
>>>>>>>>>Processor
>>>>>>>>>After Process
>>>>>>>>>Before Process
>>>>>>>>>Processor
>>>>>>>>>After Process
>>>>>>>>>Before Process
>>>>>>>>>Processor
>>>>>>>>>After Process
>>>>>>>>>Before Process
>>>>>>>>>Processor
>>>>>>>>>After Process
>>>>>>>>>Before Process
>>>>>>>>>Processor
>>>>>>>>>After Process
>>>>>>>>>Before Process
>>>>>>>>>Processor
>>>>>>>>>After Process
>>>>>>>>>Before Process
>>>>>>>>>Processor
>>>>>>>>>After Process
>>>>>>>>>Before Process
>>>>>>>>>Processor
>>>>>>>>>After Process
>>>>>>>>>>>>Before Write
>>>>>>>>>>>>Write
>>>>>>>>>>>>After Write
>>>After Chunk
>>>Before Chunk
>>>>>>Before Read
>>>>>>After Read
>>>>>>Before Read
>>>>>>After Read
>>>>>>Before Read
>>>>>>After Read
>>>>>>Before Read
>>>>>>After Read
>>>>>>Before Read
>>>>>>After Read
>>>>>>Before Read
>>>>>>After Read
>>>>>>Before Read
>>>>>>After Read
>>>>>>Before Read
>>>>>>After Read
>>>>>>Before Read <<<
>>>>>>>>>Before Process
>>>>>>>>>Processor
>>>>>>>>>After Process
>>>>>>>>>Before Process
>>>>>>>>>Processor
>>>>>>>>>After Process
>>>>>>>>>Before Process
>>>>>>>>>Processor
>>>>>>>>>After Process
>>>>>>>>>Before Process
>>>>>>>>>Processor
>>>>>>>>>After Process
>>>>>>>>>Before Process
>>>>>>>>>Processor
>>>>>>>>>After Process
>>>>>>>>>Before Process
>>>>>>>>>Processor
>>>>>>>>>After Process
>>>>>>>>>Before Process
>>>>>>>>>Processor
>>>>>>>>>After Process
>>>>>>>>>Before Process
>>>>>>>>>Processor
>>>>>>>>>After Process
>>>>>>>>>>>>Before Write
>>>>>>>>>>>>Write
>>>>>>>>>>>>After Write
>>>After Chunk
After step
하지만 마지막 Chunk에서 Before Read, After Read를 8번 수행할 것으로 예상했으나 마지막에 Read가 한번 더 일어난 로그를 확인할 수 있다. 왤까?
org.springframework.batch.core.step.item.SimpleChunkProvider.class
/**
* Surrounds the read call with listener callbacks.
* @return the item or {@code null} if the data source is exhausted
* @throws Exception is thrown if error occurs during read.
*/
@Nullable
protected final I doRead() throws Exception {
try {
listener.beforeRead();
I item = itemReader.read();
if(item != null) {
listener.afterRead(item);
}
return item;
}
catch (Exception e) {
if (logger.isDebugEnabled()) {
logger.debug(e.getMessage() + " : " + e.getClass().getName());
}
listener.onReadError(e);
throw e;
}
}
read하기 전에 beForeRead는 반드시 일어난다. beforeRead 후 더 읽을 것이 있는지 null 체크를 하기 때문에 Before Read가 한번 더 일어나는 것을 확인할 수 있었다.
'Java > Spring' 카테고리의 다른 글
[JPA/QueryDsl] 페이징에서의 N+1 해결 기록 (0) | 2022.05.16 |
---|---|
[Spring Security] Servlet Filter 등록과 동작 순서 (1) (0) | 2022.01.12 |
[Mybatis] Select시 NullPointerException ... org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible... (0) | 2021.01.18 |
[Spring]WebClient 파라미터 인코딩 (1) | 2021.01.15 |
[Spring][Transaction] 트랜잭션 제외하기, 트랜잭션 제외 안 될 때 (0) | 2020.09.07 |