[Spring] Bean Lifecycle Callbacks

Bam·2024년 10월 22일
0

Spring

목록 보기
47/49
post-thumbnail

Bean Lifecycle

스프링은 IoC 제어의 역전으로 인해 프로그래머가 아닌 스프링 IoC 컨테이너가 빈(객체)을 관리하게 됩니다. 따라서 빈의 생성부터 등록, 소멸까지의 과정을 스프링 컨테이너가 관리하기 때문에 프로그래머는 프로그램의 코드(logic)에만 집중할 수 있게 됩니다.

빈의 생성부터 소멸까지의 과정을 스프링 빈 라이프사이클(스프링 빈 생명주기)라고 하는데요. 이 과정에서 콜백 함수를 통해 특정 시점에 특정 로직을 실행 하도록 만들 수 있습니다.

스프링 빈 라이프사이클(싱글톤 빈 기준)은 다음과 같은 순서로 진행됩니다.

  1. 어플리케이션 실행
  2. 스프링 컨테이너 생성
  3. 빈 생성 & 의존관계 주입(DI)
  4. 초기화 콜백 실행
  5. 빈 사용(메소드 호출 등)
  6. 소멸 콜백 실행
  7. 스프링 컨테이너 종료 & 스프링 종료

웹 관련 빈들은 웹 요청, 세션의 라이프사이클을 따르기 때문에 스프링 컨테이너가 종료되기 전에 소멸이 발생합니다. 이런 빈 들의 경우 소멸 콜백은 스프링 컨테이너 종료 시점이 아니라 해당 빈이 종료되기 직전 시점에 호출됨에 유의해주세요.


Bean Lifecycle Callbacks

스프링에서는 3 가지 방식으로 빈 생명주기 콜백들을 지원하고 있습니다. 지금부터 하나하나씩 알아보도록 하겠습니다.

1. @PostConstruct, @PreDestroy

첫 번째 방법은 @PostConstruct, @PreDestroy 어노테이션을 사용하는 것 입니다.

@PostConstruct, @PreDestroy는 특정 메소드에 붙여주기만 하면 해당 메소드가 각각 초기화, 소멸 콜백 메소드가 되어 초기화/소멸 과정에서 호출이 됩니다.

package com.hello.myboot;

import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;

public class LifecycleCallbacksClient {
    public LifecycleCallbacksClient() {
        System.out.println("LifecycleCallbacksClient 생성자 호출");
    }

    @PostConstruct	//초기화 콜백 메소드
    public void init() {
        System.out.println("LifecycleCallbacksClient 초기화 콜백 호출");
    }

    @PreDestroy	//소멸 콜백 메소드
    public void destroy() {
        System.out.println("LifecycleCallbacksClient 소멸 콜백 호출");
    }
}
package com.hello.myboot;

import org.junit.jupiter.api.Test;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

public class LifecycleCallbacksTest {

    @Test
    public void lifecycleCallbacksTest() {
        ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(TestConfig.class);

        LifecycleCallbacksClient lifecycleCallbacksClient = context.getBean(LifecycleCallbacksClient.class);
        
        context.close();
    }

    @Configuration
    static class TestConfig {

        @Bean
        public LifecycleCallbacksClient lifecycleCallbacksClient() {
            return new LifecycleCallbacksClient();
        }
    }
}


빈을 등록(생성자)하고 초기화, 소멸 과정에서 각 메소드 내용이 제대로 호출됨을 볼 수 있습니다.

특징

  • jakarta.annotation 패키지에 등록된 어노테이션입니다. 즉, spring.으로 시작하는 스프링 프레임워크의 기능이 아닌 자바 표준 기술입니다. (= 스프링 외에서도 사용할 수 있다.)

  • 어노테이션만 붙이면 되서 편리하다.

  • 외부 라이브러리 빈에는 적용하기 어렵다.

  • 스프링에서 권장하고 있는 방식이다.

    스프링 공식 문서에도 이 표준을 다룬 페이지가 따로 있는 스프링 권장 사양입니다.


2. Bean Metadata 설정

두 번째 방법은 @Bean을 정의할 때 메타데이터(설정 정보)를 지정하는 방식입니다.

@Bean(initMethod = "초기화 콜백 함수명", destroyMethod = "소멸 콜백 함수명")

위 코드를 변경해서 빈 메타데이터 설정 방식으로 초기화/소멸 콜백 함수를 호출해보도록 만들겠습니다.

package com.hello.myboot;

public class LifecycleCallbacksClient {
    public LifecycleCallbacksClient() {
        System.out.println("LifecycleCallbacksClient 생성자 호출");
    }

    public void init() {	//초기화 콜백 메소드
        System.out.println("LifecycleCallbacksClient 초기화 콜백 호출");
    }

    public void destroy() {	//소멸 콜백 메소드
        System.out.println("LifecycleCallbacksClient 소멸 콜백 호출");
    }
}
public class LifecycleCallbacksTest {

    @Test
    public void lifecycleCallbacksTest() {
        ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(TestConfig.class);

        LifecycleCallbacksClient lifecycleCallbacksClient = context.getBean(LifecycleCallbacksClient.class);

        context.close();
    }

    @Configuration
    static class TestConfig {

        @Bean(initMethod = "init", destroyMethod = "destroy")
        public LifecycleCallbacksClient lifecycleCallbacksClient() {
            return new LifecycleCallbacksClient();
        }
    }
}

역시 잘 호출 되었죠?

특징

  • 자유로운 메소드 이름 설정 가능(보통 암묵적으로 사용되는 init, close, destroy, shutdown 를 주로 사용)

  • 코드가 아닌 메타데이터(설정 정보)이므로 외부 라이브러리에도 적용시킬 수 있다.

  • destroyMethod inferred

destroyMethod inferred

destroyMethodinferred(추론) 동작을 수행합니다.

destroyMethod의 추론은 빈에서 close, shutdown이라는 이름을 가진 메소드를 찾아서 자동으로 호출하는 동작을 합니다.

따라서 종료 메소드의 이름을 close 또는 shutdown으로 지정했을 때 destroyMethod는 따로 적어주지 않아도 알아서 찾아서 종료 콜백을 실행합니다.

추론 기능을 사용하지 않는다면 destroyMethod의 값을 "" 빈 문자열로 전달하면 됩니다.


3. InitializingBean, DisposableBean 인터페이스

세 번째 방법은 InitializingBean, DisposableBean 인터페이스를 사용하는 방법입니다.

이 방식은 오래된 사양이기에 현재는 많이 사용되는 방식은 아니므로 이런게 있다 정도로만 보겠습니다.

초기화는 InitializingBean, 소멸은 DisposableBean 인터페이스의 메소드들을 오버라이딩해서 사용합니다. 따라서 콜백 메소드의 이름을 변경할 수 없습니다.

public class LifecycleCallbacksClient implements InitializingBean, DisposableBean {

    public LifecycleCallbacksClient() {
        System.out.println("LifecycleCallbacksClient 생성자 호출");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("LifecycleCallbacksClient 초기화 콜백 호출");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("LifecycleCallbacksClient 소멸 콜백 호출");
    }
}
public class LifecycleCallbacksTest {

    @Test
    public void lifecycleCallbacksTest() {
        ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(TestConfig.class);

        LifecycleCallbacksClient lifecycleCallbacksClient = context.getBean(LifecycleCallbacksClient.class);
        
        context.close();
    }

    @Configuration
    static class TestConfig {

        @Bean
        public LifecycleCallbacksClient lifecycleCallbacksClient() {
            return new LifecycleCallbacksClient();
        }
    }
}


마무리

오늘은 빈 생명주기 콜백에 대해 알아보았습니다.

권장사항인 @PostConstruct, @PreDestroy를 우선해서 사용하되, 외부 라이브러리 등과 같이 고칠 수 없는 빈에 대해서는 빈 메타데이터 방법을 사용하시면 된다라고 할 수 있습니다.

0개의 댓글

관련 채용 정보