본문 바로가기
Java/Spring

[Spring batch] Listener 종류와 작동 순서, Step 작동 순서

by 오늘의개발부 2022. 8. 13.
반응형

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가 한번 더 일어나는 것을 확인할 수 있었다.

반응형