스프링 빈 생명주기

박시시·2022년 12월 11일
0

SPRING

목록 보기
2/2

스프링 컨테이너는 빈을 관리한다. 싱글턴 레지스트리 기능을 제공하거나 의존 주입 등을 하는 것 외에도 빈 객체의 생명주기 역시 관리한다. 컨테이너가 초기화될 때 빈 객체를 생성하여 등록, 의존성 주입을 하고 컨테이너가 종료될 시점에 빈 객체 역시 소멸시킨다.

스프링 빈 이벤트 생명주기
스프링컨테이너 생성 -> 스프링 빈 생성 -> 의존관계 주입 -> 초기화 콜백 -> 사용 -> 소멸전 콜백 -> 스프링 종료

위의 빈 생명주기에서 보듯이 스프링은 빈의 의존관계 주입이 완료되면 콜백 메서드를 통해 초기화 시점을 알려주는 다양한 기능을 제공해준다. 마찬가지로 스프링 컨테이너가 소멸 전에도 소멸 콜백을 제공한다.

객체의 생성과 초기화를 분리하자.
생성자의 책임은 파라미터를 받고 메모리를 할당하여 객체를 생성하는 것이다. 반면 초기화는 이렇게 생성된 객체의 값을 활용하여 무거운 동작을 수행한다. 따라서 생성자 안에서 초기화 작업을 함께 진행하는 것 보다는 초기화 부분을 나누어 관리하는 것이 유지보수 관점에서 좋다.

빈 생명주기

빈 생명주기를 조금 더 디테일하게 들여다 보면 아래의 그림과 같다.

빈 생성 페이즈

  • Instantiation: 스프링은 마치 우리가 수동으로 자바 객체를 생성할 때 처럼 빈 객체를 초기화 한다.
  • Populating Properties: 객체를 초기화한 후 스프링은 Aware 인터페이스를 구현한 빈을 스캔하고 관련된 프로퍼티를 세팅하기 시작한다.
  • Pre-Initialization: 스프링의 BeanPostProcessors가 이 페이즈에서 활용된다. postProcessBeforeInitialization() 메서드들이 그들의 잡을 한다. 또한 @PostConstruct가 달린 메서드가 그 후에 바로 실행된다.
  • AfterPropertiesSet: 스프링은 InitializingBean 인터페이스를 구현한 빈들의 afterPropertiesSet() 메서드들을 실행한다.
  • Custom Initialization: 스프링은 @Bean 어노테이션의 initMethod 어트리뷰트에 정의한 초기화 메서드를 트리거한다.
  • Post-Initialization: 스프링의 BeanPostProcessors가 또 한 번 작동된다. 이 페이즈에서는 postProcessAfterInitialization()메서드를 트리거한다.

빈 소멸 페이즈

  • Pre-Destroy: 스프링은 이 페이즈에서 @PreDestroy 어노테이션이 달린 메서드를 실행한다.
  • Destroy: 스프링은 DisposableBean 구현체의 destroy() 메서드를 실행한다.
  • Custom Destruction: @Bean 어노테이션의 destroyMethod 어트리뷰트에 커스텀 destruction 훅을 정의할 수 있다. 스프링은 마지막 페이즈에서 이를 실행한다.

빈 생명주기 콜백

스프링은 인터페이스, 설정 정보 활용, @PostConstruct, @PreDestroy 어노테이션 사용 등 크게 3가지 방법으로 빈 생명주기 콜백을 지원한다.

스프링 인터페이스 사용

InitializingBean 인터페이스를 구현 후 afterPropertiesSet() 메서드에 코드를 작성한다.

@Component
class MySpringBean implements InitializingBean {

  @Override
  public void afterPropertiesSet() {
    //...
  }
}

이와 유사하게 DisposableBean을 구현하여 destroy() 메서드를 오버라이드 하면 소멸 전에 해당 콜백 메서드를 호출한다.

@Component
class MySpringBean implements DisposableBean {

  @Override
  public void destroy() {
    //...
  }
}

단점

  • 스프링 전용 인터페이스라 코드가 스프링에 의존하게 된다.
  • 메서드의 이름을 변경할 수가 없다.
  • 외부 라이브러리에 적용이 불가능하다.

@Bean 어노테이션의 어트리뷰트 사용

@Bean(initMethod = "init", destroyMethod = "close")와 같이 설정 정보에 메서드를 지정가능하다.

@Configuration
class MySpringConfiguration {

  @Bean(initMethod = "onInitialize", destroyMethod = "onDestroy")
  public MySpringBean mySpringBean() {
    return new MySpringBean();
  }
}

인터페이스 사용과 달리 메서드 이름을 자유롭게 지정할 수 있으며 스프링 코드에 의존하지도 않게 된다. 또한 설정 정보를 이용하므로 코드 변경이 불가한 외부 라이브러리에도 초기화 및 종료 메서드를 적용할 수 있다.

참고로 빈에 close() 혹은 shutdown() 메서드가 있다면 자동으로 소멸 전에 해당 메서드를 호출한다. 만약 이러한 추론 기능을 사용하기 싫다면 아래와 같이 세팅해주면된다.

@Configuration
class MySpringConfiguration {

  @Bean(destroyMethod = "")
  public MySpringBean mySpringBean() {
    return new MySpringBean();
  }
}

@PostConstruct, @PreDestroy 어노테이션 사용

pre-initialization 페이즈와 destroy 페이즈에 이 어노테이션들을 사용할 수 있다.

@Component
class MySpringBean {

  @PostConstruct
  public void postConstruct() {
    //...
  }

  @PreDestroy
  public void preDestroy() {
    //...
  }
}

이 방법이 최신 스프링에서 가장 권장하는 방법이다. 스프링 종속적인 기술이 아닌 JSR-250 자바 표준에 속한 어노테이션이므로 스프링 의존적이지 않게 된다.
외부 라이브러리에는 적용할 수 없으므로 만약 외부 라이브러리에 콜백을 사용하려면 @Bean 어트리뷰트 기능을 함께 사용해야 한다.

참조

인프런 스프링 핵심원리 기본편
https://haruhiism.tistory.com/186
https://reflectoring.io/spring-bean-lifecycle/

0개의 댓글