Spring Bean LifeCycle

최근 사내에서 Kafka 관련된 설정을 리팩토링하는 작업을 했습니다. SpringBoot를 도입하며 Kafka 설정 관련된 부분들을 SpringBoot의 @ConfigurationProperties를 이용하도록 변경하고, XML 기반의 빈 생성 부분을 Java Config 기반으로 변경했습니다.
현재 프로젝트에서는 여러가지 이유로 기존의 Kafka Producer를 확장(extends)해서 사용하고 있는데, 이번에 리팩토링 관련한 PR에서 확장해서 사용하고 있는 Kafka Producer에서 close 메서드를 구현하고 있는지 확인해달라는 리뷰를 받았습니다.

컨테이너에 등록된 Bean이 DisposableBean interface를 구현하고 있거나 @PreDestroy 어노테이션 또는 destroyMethod 속성을 사용하고 있다면 Bean의 Lifecycle 마지막에 자원을 해제하거나 필요한 작업을 수행할 수 있습니다. 그러나 위의 방법 말고도 Spring container에서 Bean을 제거 할 때, close()shutdown() 메서드를 호출합니다.

Kafka ProducerCloseable interface를 구현(implement)하고 있기 때문에 close 메서드를 포함하고 있습니다. Kafka Producer가 Bean으로 등록되어 있고 후에 소멸될 때 close 메서드가 호출되어 해당 자원이 모두 해제될 것입니다.

따라서 제가 받았던 리뷰는 기존 Kafka Producer를 확장한 것을 Bean으로 등록해 사용하고 있는데, 해당 확장 클래스가 close 메서드를 제대로 구현해 Bean이 소멸 될 때 호출될 close 메서드에서 자원이 제대로 해제하고 있는지 확인해 달라는 것이었습니다.

AutoCloseabletry-with-resource 구문과 함께 사용됩니다.

이번 리뷰를 통해 Bean의 LifeCycle과 close, shutdown 메서드에 대해 다시 살펴보는 계기가 되었습니다.

Initialize 메서드

Initialize 메서드는 Bean Object가 생성되고 DI를 마친 후 실행되는 메서드입니다. 일반적으로 Object의 초기화 작업이 필요한 경우 생성자에서 처리하지만 DI를 통해 Bean이 주입된 후에 초기화할 작업이 있다면 초기화 메서드를 이용해서 초기화를 진행할 수 있습니다.

@PostConstruct

초기화 하고 싶은 메서드에 @PostConstruct 어노테이션을 붙여주면 Spring이 해당 메서드를 초기화시에 호출합니다. PostConstruct는 JSR-250 스펙에 포함되어 있기 때문에 JSR-250을 구현한 다른 프레임워크 혹은 라이브러리에서도 동작합니다. 다른 초기화 메서드에 비해 Spring에 의존적이지 않다는 장점이 있습니다.

JSR-250
JSR 250 is a Java Specification Request with the objective to develop annotations (that is, information about a software program that is not part of the program itself) for common semantic concepts in the Java SE and Java EE platforms that apply across a variety of individual technologies.

1
2
3
4
5
6
7
8
9
@Slf4j
@Component
public class SimpleBean {

@PostConstruct
public void postConstruct() {
log.info("postConstruct");
}
}

InitializingBean

InitializingBean 인터페이스를 구현하면 Spring이 afterPropertiesSet 메서드를 초기화시에 호출합니다.

1
2
3
4
5
6
7
8
9
@Slf4j
@Component
public class SimpleBean implements InitializingBean {

@Override
public void afterPropertiesSet() throws Exception {
log.info("afterPropertiesSet");
}
}

@Bean(initMethod)

@Bean 어노테이션을 이용해 Bean을 생성할 때, @Bean 어노테이션의 initMethod 속성을 이용해 초기화 메서드를 지정할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Configuration
public class TestConfiguration {

@Bean(initMethod = "init")
public SimpleBean simpleBean() {
return new SimpleBean();
}

@Slf4j
public static class SimpleBean {

public void init() throws Exception {
log.info("init");
}
}
}

Destroy 메서드

Destroy 메서드는 스프링 컨테이너가 종료 될 때, 호출되어 Bean이 사용한 리소스들을 반환하거나 종료 시점에 처리해야할 작업이 있을 경우 사용합니다.

PreDestroy

@PreDestroy 도 PostConstruct처럼 JSR-250 스펙에 포함되어 있기 때문에 JSR-250을 구현한 다른 프레임워크 혹은 라이브러리에서도 동작합니다. 컨테이너가 종료 될 때 실행하고 싶은 메서드에 어노테이션을 붙여주면 Spring이 컨테이너 종료 시 해당 메서드를 호출합니다.

1
2
3
4
5
6
7
8
9
@Slf4j
@Component
public class SimpleBean {

@PreDestroy
public void preDestroy() {
log.info("preDestroy");
}
}

DisposableBean

DisposableBean 인터페이스를 구현하면 Spring이 destroy 메서드를 호출합니다.

1
2
3
4
5
6
7
8
9
lf4j
@Component
public class SimpleBean implements DisposableBean {

@Override
public void destroy() throws Exception {
log.info("destroy");
}
}

@Bean(destroyMethod)

@Bean 어노테이션을 이용해 Bean을 생성할 때, @Bean 어노테이션의 destroyMethod 속성을 이용해 컨테이너 종료시 실행하고자 하는 메서드를 지정할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Configuration
public class TestConfiguration {

@Bean(destroyMethod = "destroy")
public SimpleBean simpleBean() {
return new SimpleBean();
}

@Slf4j
public static class SimpleBean {

public void destroy() throws Exception {
log.info("destroy");
}
}
}

close & shutdown

close와 shutdown 메서드는 DisposableBeanAdapter에 의해 실행됩니다.