Spring Bean Lifecycle

Hansu Kim·2022년 2월 20일
0

Spring boot

목록 보기
8/10

스프링 빈은 객체 생성 및 DI 주입이 끝난 후 부터 사용할 수 있다.
싱글톤 빈의 경우, 아래와 같은 Lifecycle을 가진다.

Singleton Bean의 Lifecycle

  1. 스프링 컨테이너 생성
  2. 스프링 빈 생성
  3. 의존 관계 주입
  4. 초기화 콜백
  5. 사용
  6. 소멸 전 콜백
  7. 스프링 종료

개발자가 빈을 사용할 수 있는 것은 '5'번에서 뿐이다.
위 Lifecycle에서, 개발자는 콜백 함수들에 대해 접근하여 빈의 Lifecycle을 커스터마이징할 수 있다.

SOLID 중 S. Single responsibility 원칙에 의해 빈의 생성과 초기화는 분리하여 구현하는 것이 좋다. (보통 생성은 가볍고 초기화는 무거운 작업이므로, 분리하는게 유지보수에 좋다.)

Bean 생명주기 콜백의 종류

스프링에서는 아래 3가지 방식을 통해 빈 생명주기 콜백을 지원한다,

  • 인터페이스(InitializingBean, DisposableBean)
  • 설정 정보에 초기화 메서드, 종료 메서드 지정
  • @PostConstruct, @PreDestroy 애노테이션 지원

인터페이스(InitializingBean, DisposableBean)

public class NetworkClient implements InitializingBean, DisposableBean {
    private String url;

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

    public void setUrl(String url) {
        this.url = url;
    }

    //서비스 시작시 호출
    public void connect() {
        System.out.println("connect: " + url);
    }

    public void call(String message) {
        System.out.println("call: " + url + " message = " + message);
    }

    //서비스 종료시 호출
    public void disconnect() {
        System.out.println("close: " + url);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("NetworkClient.afterpropertiesSet");
        connect();
        call("초기화 연결 메시지");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("NetworkClient.destroy");
        disconnect();
    }
}

위와 같이, InitializingBean 인터페이스를 상속받아 afterPropertieSet() 메소드를 오버라이드하여 초기화 콜백 함수를 구현할 수 있다.
또한 DisposableBean 인터페이스를 상속받고, destroy() 메소드를 오버라이드해 소멸 전 콜백함수를 구현할 수 있다.

하지만, 해당 방식의 경우 아래 3가지 단점이 있다.

  • 스프링 전용 인터페이스들을 상속하므로, 코드가 스프링 인터페이스에 의존적
  • 메소드 이름 변경 불가
  • 코드가 공개되지않은 외부 라이브러리에 적용 불가

최근에는 단점들로 인해 해당 방식은 사용하지 않는다.

설정 정보에 초기화, 종료 메소드 지정 방식

class NetworkClient {
  void init(){}
  void close(){}
}
@Configuration
static class LifeCycleConfig {
	@Bean(initMethod = "init", destroyMethod = "close")
    public NetworkClient networkClient() {
    	NetworkClient networkClient = new NetworkClient();
    	networkClient.setUrl("http://hello-spring.dev");
        return networkClient;
    }
}

위와 같이, 클래스 내부에 초기화/종료 메소드를 구현해놓고 @Bean(initMethod = "init", destroyMethod = "close") 처럼 Bean Annotation에 추가 아규먼트들을 설정해주는 방식으로 콜백 메소드들을 사용할 수 있다.

해당 방식의 장점은 아래와 같다.

  • 메소드 이름을 자유롭게
  • 스프링 빈이 스프링 코드에 의존하지 않음
  • 코드를 고칠 수 없는 외부 라이브러리에도 초기화, 종료 메소드를 적용 가능

@Bean(destroyMethod)에는 아주 특별한 기능이 있다. 별도의 종료 메소드를 지정해주지 않더라도, 'close', 'shutdown' 이름의 메소드들을 자동으로 호출해준다.
실제로, destroyMethod의 스프링 코드를 까보면 아래와 같다.

//Bean.java의 destroyMethod 선언부
String destroyMethod() default AbstractBeanDefinition.INFER_METHOD;
//AbstractBeanDefinition.java의 INFER_METHOD 선언부
public static final String INFER_METHOD = "(inferred)";

Annotation @PostConstruct, @PreDestroy

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

@PostConstruct
public void init() throws Exception {
	System.out.println("NetworkClient.afterpropertiesSet");
    connect();
    call("초기화 연결 메시지");
}
@PreDestroy
public void close() throws Exception {
	System.out.println("NetworkClient.destroy");
    disconnect();
}

단순 어노테이션을 통해 콜백 함수들을 커스터마이징해준다.
해당 방식의 장점은 아래와 같다.

  • import문에서 알 수 있듯이, 스프링 인터페이스에 의존적이지 않으며, 최신 스프링에서 권장하는 방식이다.
  • 컴포넌트 스캔과 활용도가 높다.

하지만 아래와 같은 단점이 있다.

  • 코드 수정이 불가능한 외부 라이브러리에는 적용할 수 없다.

결론

코드 수정이 불가능한 외부 라이브러리에는 @Bean(initMethod="XX", destroyMethod="XX") 방식을 사용하자.
그 외의 경우엔 @PostConstruct, @PreDestroy를 사용하자.

0개의 댓글