Bean Lifecycle

Bean의 생명주기를 의미합니다. 즉, 언제 Bean이 생성되고 언제 Bean이 제거되는 지를 의미합니다.

먼저 정의된 Bean은 다음과 같은 생명 주기를 가지게 됩니다. 핵심은, 객체를 생성하는 과정과 의존성을 주입하는 과정이 분리되어 있다는 것입니다. 그렇기 때문에 객체 생성을 위한 책임과 초기화를 위한 책임을 분리하는 것이 적절하다고 합니다. (아직까지는 그렇게 명확하게 와닿지는 않습니다😅)

graph TD
    A[Bean 생성] --> |1. Bean 인스턴스가 생성됩니다.| B[초기화]
    B --> |2. 생성자나 초기화 메서드를 통해 초기화됩니다.| C[의존성 주입]
    C --> |3. 필요한 의존성들이 주입됩니다.| D[상태 설정]
    D --> |4. 필요한 상태가 설정됩니다.| E[사용 중]
    E --> |5. Bean이 애플리케이션에서 사용됩니다.| F[소멸 준비]
    F --> |6. 소멸 전에 필요한 정리 작업을 수행합니다.| G[Bean 소멸]

Lifecycle callbacks

이렇게 여러 단계를 가지는 생명주기에서 프로그래머가 직접 접근할 수 있는 Lifecycle callback 을 제공합니다. 쉽게 표현하면, 초기화/소멸 과정 사이에 프로그래머의 코드를 직접 넣을 수 있다는 것 입니다.

InitializeBean, DisposableBean

가장 초창기에 정의된 인터페이스입니다. 각각 초기화, 소멸 단계에서의 Callback을 제공하게 됩니다.

graph TD
    A[Bean 생성] -->|의존성 주입| B[초기화 단계 - InitializeBean]
    B --> C[...]
    C -->|소멸 준비| D[소멸 단계 - DisposableBean]
    D --> E[...]

     Styles
    style B fill:#bbf,stroke:#333,stroke-width:2px
    style D fill:#fb9,stroke:#333,stroke-width:2px
// Configuration 
@Bean(initMethod = "init", destroyMethod = "close")
fun networkClient(): NetworkClient {
	// ...
}
 
// NetworkClient
class NetworkClient {
    var url: String? = null
 
    /*
     *  ....
     */
     
    fun init() {
        // 의존관계 주입이 끝나면 호출
        connect()
        call("afterPropertiesSet")
    }
    fun close() {
        // 빈이 종료될 때 호출
        disconnect()
    }
}

Spring also supports inference of destroy methods, detecting a public close or shutdown method.

destroyMethod를 정의하지 않더라도, 기본적으로 close, shutdown 메소드를 탐색하여 해당 메소드를 호출하게 됩니다.

 it automatically matches java.lang.AutoCloseable or java.io.Closeable implementations

이게 무언가 Tricky한 구현이 아니라, 인터페이스의 구현체와의 이름과 같기 때문입니다.
마찬가지로 initMethod도 기본으로 정의된 이름을 찾아나서는데, 더 자세한 내용은 요기를 ㅎ..

https://github.com/spring-projects/spring-framework/blob/8d707eb5304e42babe3d680c5cd3880869cfabe2/spring-beans/src/main/java/org/springframework/beans/factory/xml/BeanDefinitionParserDelegate.java#L620

@PostConstruct, @PreDestory

가장 자연스러운 방식으로 현재까지 추천되는 방식입니다. Annotation 기반으로 제어할 수 있기 때문에 편리하고@Component과 Annotation 기반 제어에 자연스럽게 적용할 수 있습니다.

graph TD
    A[Bean 생성] -->|의존성 주입| B[초기화 단계 - @PostConstruct 호출]
    B --> C[...]
    C -->|소멸 준비| D[소멸 단계 - @PreDestroy 호출]
    D --> E[...]

    %% Styles
    style B fill:#bbf,stroke:#333,stroke-width:2px
    style D fill:#fb9,stroke:#333,stroke-width:2px
// NetworkClient
class NetworkClient {
    var url: String? = null
 
    /*
     *  ....
     */
 
    @PostConstruct
    fun constructer() {
        // 의존관계 주입이 끝나면 호출
        connect()
        call("afterPropertiesSet")
    }
 
	@PreDestroy
    fun destroyer() {
        // 빈이 종료될 때 호출
        disconnect()
    }
}

해당 방법은 수정이 어려운 외부 라이브러리에는 적용이 불가능하지만, Java 표준으로 등록되었기 때문에 타 컨테이너 기반 프레임워크와의 호환성을 고려한다면 가장 강력히 추천되는 방식입니다.

Reference