Chapter 6. 빈 라이프사이클 범위

Posted by yunki kim on May 5, 2022

컨테이너 초기화와 종료

  스프링 컨테이너는 초기화, 종료 라는 라이프 사이클을 갖는다. 

1
2
3
4
5
6
7
8
// 스프링 컨테이너 초기화
// 스프링 컨테이너는 설정 클래스에서 정보를 읽어와 알맞은 빈 객체를 생성하고
// 의존 주입 하는 작업을 수행한다.
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppCtx.class);
// 초기화된 컨테이너를 사용한다 - 메서드를 이용해 컨터이너에 보관된 빈 객체를 구한다.
Greeting greeting = context.getBean("greeting", Greeting.class);
// 컨테이너를 종료한다.
context.close();
cs

  위 예시에서 close() 메서드는 AbstractApplicationContext 클래스에 정의되 있다. 자바 설정을 사용하는 AnnotationConfigApplicationContext와 XML 설정을 사용하는 GenerixXmlApplicationContext는 모두 AbstarctApplicationContext를 상속받는다.

  컨테이너의 초기화, 종료는 다음과 같은 작업도 동반한다.

    컨테이너 초기화 - 빈 객체의 생성, 의존 주입, 초기화

    컨테이너 종료 - 빈 객체의 소멸

스프링 빈 객체의 라이프사이클

  스프링 컨테이너는 빈 객체의 라이프사이클을 관리한다. 빈 객체의 라이프사이클은 다음과 같다.

객체 생성 -> 의존 설정 -> 초기화 -> 소멸

  스프링 컨테이너를 초기화하면 빈 객체를 생성하고 의존성을 주입한다. 의존 자동 주입을 통한 의존 설정이 이 시점에 수행된다. 그 후 빈 객체의 초기화를 수행한다. 이를 위해 스프링은 빈 객체의 지정된 메서드를 호출한다. 스프링 컨테이너 종료 시 컨테이너가 빈 객체의 소멸을 처리하는데, 이 때 역시 지정한 메서드를 처리한다.

빈 객체의 초기화와 소멸: 스프링 인터페이스

    빈 객체를 초기화 하거나 소멸하기할 때 특정 로직을 수행해야 한다면 다음과 같은 두 개의 인터페이스를 implements 하면 된다.

1
2
3
4
5
6
7
8
9
// org.springframework.beans.factory.initializingBean
public interface InitializingBean {
    void afterPropertiesSet() throws Exception;
}
 
// org.springframework.beans.factory.DisposableBean
public interface DisposableBean {
    void destroy() throws Exception;
}
cs

  InitializingBean 인터페이스를 구현하면 스프링 컨테이너 초기화 과정에서 빈 객체의 afterPropertiesSet() 메서드를 실행한다. DisposaableBean 인터페이스를 구현했다면 소멸과정에서 destroy() 메서드를 실행한다.

  이런 동작은 DB 연결 등에 필요하다. DB 커넥션 풀을 위한 빈 객체는 초기화 과정에서 DB 연결을 생성하고 빈 객체가 소멸하면 DB 연결을 끊어야 한다. 이 때 InitializingBean과 DisposableBean 인터페이스를 사용하면 된다.

빈 객체의 초기화와 소멸: 커스텀 메서드

  외부에서 제공받은 클래스는 InitializingBean과 DisposableBean 인터페이스를 implements할 수 없다. 이럴때는 스프링 설정에서 직접 메서드를 지정할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Bean {
    
    public void connect() {
    }
    
    public void printHello() {
        System.out.println("hello");
    }
    
    public void close(){
    }
}
 
// 위와 같은 클래스가 존재하고
// connect()를 초기화 과정에서, close()를 소멸 과정에서 사용하고 싶다면
// 아래와 같이 @Bean 어노테이션에 속성을 지정하면 된다.
@Bean(initMethod = "connect", destroyMethod = "close")
public setter.Bean bean() {
    return new setter.Bean();
}
cs

  connect() 메서드와 close() 메서드는 private 이여도 상관없다. 또 한, initMethod, destroyMethod 속성에 지정한 메서드는 파라미터가 없어야 한다. 있다면 스프링 컨테이너는 다음과 같은 에러를 발생시킨다.

1
2
Invocation of init method failed; nested exception is 
org.springframework.beans.factory.support.BeanDefinitionValidationException
cs

  initMethod 속성을 지정하고 싶지 않다면, 다음과 같이 사용해 initMethod 속성을 사용하지 않을 수 있다.

1
2
3
4
5
6
@Bean(destroyMethod = "close")
public setter.Bean bean() {
    setter.Bean bean = new setter.Bean();
    bean.connect();
    return bean;
}
cs

빈 객체의 생성과 관리 범위

  스프링의 빈은 기본적으로 싱글톤 스코프이다. 즉, 한 식별자에 대해 한 개의 객체만 존재한다. 이 스코프를 프로토타입으로 지정한다면 빈 객체를 구할 때마다 매번 새로운 객체를 생성한다. 스코프의 지정은 @Scope 어노테이션으로 할 수 있다.

 

1
2
3
4
5
6
7
8
// prototy scope으로 지정
@Bean
@Scope("prototype")
public setter.Bean bean() {
    setter.Bean bean = new setter.Bean();
    bean.connect();
    return bean;
}
cs

  빈이 프로토타입 스코프를 갖는다면 완전한 라이프사이클을 따르지 않는다. 프로토타입 스코프의 빈은 컨테이너 생성 시 빈 객체가 생성되고 설정을 초기화 하는 과정 까지는 싱글톤 스코프 빈과 동일하다. 하지만 컨테이너 종료 시 빈 객체가 소멸되지 않는다. 따라서 컨테이너 종료시 명시적으로 소멸처리 해야 한다.

 

출처 - 초보 웹 개발자를 위한 스프링 5 프로그래밍 입문