[Spring] @PostConstruct와 @PreDestory 스프링 빈 생명주기 관리

Spring Bean의 생명 주기

Spring Bean은 다음과 같은 생명 주기를 갖습니다.

스프링 컨테이너 생성 => 스프링 빈 생성 => 의존 관계 주입(DI) => 초기화 콜백 => 사용 => 소멸전 콜백 => 스프링 종료

 

위 생명 주기를 보면 스프링 빈(Spring Bean)은 객체를 생성하고, 의존관계 주입이 다 끝난 후에야 필요한 데이터를 사용할 수 있는 준비가 완료 됩니다.

즉, 생성자가 호출되었을 때 스프링 빈은 초기화 전이고 DI(의존관계 주입)이 이루어 지고 나서야 스프링 빈이 초기화 됩니다.

초기화 작업은 의존관계 주입이 모두 완료되고 난 다음에 호출하게 되는데, 개발자가 의존관계 주입이 완료된 시점이 언제인지 알 수 있는 방법이 없습니다.

 

그래서 Spring은 다음과 같은 빈 생명주기 콜백을 지원합니다.

  1. 인터페이스(InitializingBean, DisposableBean)
  2. Bean 설정 정보에 초기화 메서드, 종료 메서드 지정
  3. @PostConstruct, @PreDestroy 어노테이션 지원

하지만 1번의 경우에는 거의 사용하지 않아 2, 3번에 대해 다뤄보려 합니다. 

 


@PostConstruct, @PreDestroy 어노테이션

위 두 어노테이션은 최근 스프링에서 가장 권장하는 방법으로 어노테이션을 붙이기만 하면 되므로 매우 편리합니다.

패키지는 javax.annotation 으로 스프링에 종속적인 기술이 아닌 자바 표준이기 때문에 스프링이 아닌 다른 컨테이너에서도 동작합니다.

 

@PostConstruct - 초기화 콜백

생성자가 호출 되었을 때, 의존 관계 주입이 끝나지 않았기 때문에 스프링 빈은 초기화 되지 않습니다.

하지만 @PostConstruct를 이용해 의존 관계 주입(DI)가 이루어진 후 초기화를 수행하여 객체의 값을 설정한 채로 호출할 수 있습니다.

 

@PreDestroy - 소멸전 콜백

ApplicationContext 에서 Bean을 제거하기 위해 사용됩니다.

 

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

@Component
public class ConnectTest {
    private String message;

    public ConnectTest() {
        System.out.println("생성자 호출, message = " + message);
    }

    @PostConstruct
    public void init() {
        System.out.println("초기화 콜백");
        connect();
    }

    @PreDestroy
    public void close() {
        System.out.println("종료 전 콜백");
        disconnect();
    }

    public void connect() {
        System.out.println("연결 완료 - message : " + message);
    }

    public void disconnect() {
        System.out.println("연결 종료");
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

 

 

@PostConstruct, @PreDestroy의 단점

위 두 어노테이션의 단점은 외부 라이브러리에 적용하지 못한다는 점입니다.

외부 라이브러리를 초기화, 종료 해야 한다면 @Bean에 옵션을 추가하는 기능을 사용해야합니다.

 

 


Bean옵션에 초기화 메서드, 종료 메서드 지정

설정 정보(Configuration)에 아래와 같이 @Bean 어노테이션에 초기화, 종료 메서드를 지정하는 방식입니다.

    @Configuration
    static class ConnectConfig {
        @Bean(initMethod = "init", destroyMethod = "close")
        public ConnectTest connectTest() {
            ConnectTest connectTest = new ConnectTest();
            connectTest.setMessage("connect to http://localhost:8080");
            return connectTest;
        }
    }

 

@PostConstruct, @PreDestroy과 똑같이 DI가 이루어진 후 초기화 되는 것을 볼 수 있습니다.

package com.example.spring2.lifecycle;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

public class ConnectTest {
    private String message;

    public ConnectTest() {
        System.out.println("생성자 호출, message = " + message);
    }

    public void init() {
        System.out.println("초기화 콜백");
        connect();
    }

    public void close() {
        System.out.println("종료 전 콜백");
        disconnect();
    }

    public void connect() {
        System.out.println("연결 완료 - message : " + message);
    }

    public void disconnect() {
        System.out.println("연결 종료");
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

 

@Bean의 destroyMethod의 inferred(추론) 기능

destroyMethod에는 inferred(추론)이라는 특별한 기능이 있습니다.

라이브러리들은 대부분 close, shutdown 이라는 종료 메서드를 사용하는데

destroyMethod에는 inferred(추론)이라는 Default(기본값) 옵션close, shutdown 이라는 이름의 메서드를 자동으로 호출해줍니다.

기본 값이므로 destroyMethod 옵션을 따로 적지 않아도 작동하며

만약 inferred(추론)기능을 사용하지 않는다destroyMethod="" 처럼 빈 공백을 지정해줘야합니다.