tasklet 에서 @BeforeStep 이 동작하지 않을때
spring batch 에서 Step 라이프사이클을 활용해 step 이 실행되기전 선작업이 필요한 경우 2가지 방법이 있다.
StepExecutionListener 인터페이스를 구현하는 방법과 @BeforeStep 애노테이션을 이용해 선언적으로 명시하는 방법이다.
- StepExecutionListener 인터페이스를 구현하는 방식(인터페이스의 추상메서드들을 기본으로 구현해주는 스켈레톤 클래스인 StepExecutionListenerSupport 를 상속받았다.)
class SampleTasklet : Tasklet, StepExecutionListenerSupport() {
override fun execute(contribution: StepContribution, chunkContext: ChunkContext): RepeatStatus? {
println("hello tasklet")
return RepeatStatus.FINISHED
}
override fun beforeStep(stepExecution: StepExecution) {
println("hello class before")
}
}
- @BeforeStep 애노테이션을 이용하는 방식
class SampleTasklet : Tasklet {
override fun execute(contribution: StepContribution, chunkContext: ChunkContext): RepeatStatus? {
println("hello tasklet")
return RepeatStatus.FINISHED
}
@BeforeStep
fun before(stepExecution: StepExecution) {
println("hello annotation before")
}
}
두가지 방식중 선호하는 방식을 사용하면되는데 애노테이션을 이용할 경우 아래처럼 tasklet 만 등록해서는 before() 메서드가 정상적으로 호출되지 않는다.
@Bean
fun sampleStep(sampleTasklet: SampleTasklet): Step =
stepBuilderFactory["sampleStep"]
.tasklet(sampleTasklet)
.build()
이때 이를 해결하는 방법은 2가지가 있다.
1. 애노테이션 방식을 포기하고 StepExecutionListener 를 구현한다
2. listener 에도 등록해준다
1번은 이미 위에서 살펴본 방식이므로 자세히 설명하지 않는다. 2번도 특별할건 없는데 아래와 같이 listener 에도 등록해주면 된다.
@Bean
fun sampleStep(sampleTasklet: SampleTasklet): Step =
stepBuilderFactory["sampleStep"]
.tasklet(sampleTasklet)
.listener(sampleTasklet)
.build()
인터페이스를 구현할때는 동작을 하고, 애노테이션 방식에선 동작하지 않는 이유는 무엇일까? 일단 beforeStep 이 동작하려면 listener 에 등록이 되어야한다. 그럼 애노테이션은 등록이 안되고 인터페이스 구현일때만 등록이 되는 이유는 무엇일까? 현재 스프링 코드는 아래처럼 인스턴스를 확인하여 인터페이스를 구현했을때만 자동으로 listener 를 등록하게 하고있기 때문이다.
public void setTasklet(Tasklet tasklet) {
this.tasklet = tasklet;
if (tasklet instanceof StepExecutionListener) {
registerStepExecutionListener((StepExecutionListener) tasklet);
}
}
애노테이션 방식을 이용할 경우 저 if 문에서 false 가 나오기 때문에 listener 에 등록되지 않는다. 이게 버그인지 스펙인지는 아직 모르겠으나 @BeforeStep 이 의도한대로 동작하지 않는다면 위와 같이 해결할 수 있다.