[Spring] Bean의 라이프사이클과 범위

첫걸음 - 6

Posted by owin2828 on 2019-12-30 11:20 · 8 mins read

1. 스프링 Bean 객체의 라이프 사이클


1-1. 컨테이너의 초기화와 종료

  • 스프링 컨테이너는 초기화종료라는 라이프 사이클을 가짐
  • 컨텍스트 객체가 생성되는 시점에 컨테이너를 초기화
  • 스프링 컨테이너는 설정 클래스에서 정보를 읽어와 알맞은 Bean 객체를 생성하고,
    각 Bean을 연결(의존 주입)하는 작업 수행
  • 초기화가 끝난 후 컨테이너를 사용 가능(getBean()등의 작업으로 Bean 객체를 구하는 행위 등)
  • 컨테이너 사용이 끝나면 컨테이너를 close() 매서드를 사용해 종료, Bean 객체의 소멸
    • 기본적으로 Spring의 ApplicationContext 구현은 초기화 프로세스에서 모든 싱글톤 빈을 생성 및 설정
    • 따라서 Bean에 문제가 있을 경우 초기화 단계에서 알 수 있다는 장점이 존재
    • 만약 어떤 이유로, 특정 Bean이 늦은 초기화를 원한다면 다음과 같이 두 가지 방법이 존재
      1. xml에서 Bean을 등록시 lazy-init 속성을 이용하여 초기화
      2. Java Config에서 Bean을 등록시 @Lazy 어노테이션을 이용

    설정파일 전체를 Lazy-init

    @Lazy
    @Configuration
    @ComponentScan(basePackages = "com.baeldung.lazy")
    public class AppConfig {
     
        @Bean
        public Region getRegion(){
            return new Region();
        }
        ...
    

    특정 Bean을 Lazy-init

    @Bean
    @Lazy(true)
    public Region getRegion(){
        return new Region();
    }
    

    Lazy init을 하더라도 해당 Bean을 다른 Bean이 참조한다면 그 시점에 초기화 됨

1-2. Bean 객체의 라이프 사이클

Bean 객체는 아래와 같은 라이프 사이클을 가지며 스프링 컨테이너에 의해 관리됨

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

2. Bean 객체의 초기화와 소멸


2-1. 스프링 인터페이스를 통한 구현

스프링 컨테이너는 Bean 객체를 초기화하고 소멸하기 위해 빈 객체의 지정한 매서드를 호출

  1. 매서드 정의 인터페이스:
    • org.springframework.beans.factory.InitializingBean
    • org.springframework.beans.factory.DisposableBean
  2. 인터페이스:
     // 초기화 인터페이스
     public interface InitializingBean {
         void afterPropertiesSet() throws Exception;
     }
     // 소멸 인터페이스
     public interface DisposableBean {
         void destroy() throws Exception;
     }
    
    • Bean 객체가 각 인터페이스를 구현하면 스프링 컨테이너는 해당하는 매서드를 실행
    • 우리는 위의 코드에서 afterPropertiesSet()destroy() 매서드를 구현하면 됨
  3. 예시
     // Client.java
     // 초기화와 소멸에 해당하는 인터페이스를 상속 후, 각 매서드 재정의
     public class Client implements InitializingBean, DisposableBean {
             ...
             // 초기화 매서드 오버라이딩
         @Override
         public void afterPropertiesSet() throws Exception {
             System.out.println("Client.afterPropertiesSet() 실행");
         }
             ...
             // 소멸 매서드 오버라이딩
         @Override
         public void destroy() throws Exception {
             System.out.println("Client.destroy() 실행");
         }
    
     }
    
       * 해당 코드를 수행하면 콘솔 화면에 매서드에서 정의한  `"Client.afterPropertiesSet() 실행"` 및 `"Client.destroy() 실행"`이 출력 됨  
    


2-2. 커스텀 매서드를 통한 구현

위의 두 인터페이스를 구현할 수 없거나, 두 인터페이스를 사용하지 않고 싶을 때 스프링 설정에서 직접 매서드를 지정 가능

  • Bean 태그에서 initMethod 속성과 destoryMethod 속성을 사용해 사용할 매서드 이름을 지정
// AppCtxWithprototype.java
@Configuration
public class AppCtxWithPrototype {
	...
	// 매서드의 이름을 지정해 초기화와 소멸 로직을 처리
	@Bean(initMethod = "connect", destroyMethod = "close")
	public Client2 client2() {
		Client2 client = new Client2();
		client.setHost("host");
		return client;
	}
}


2-3. Bean 설정 코드에서 직접 초기화/소멸을 수행

// AppCtxWithprototype.java
@Configuration
public class AppCtxWithPrototype {
	...
	@Bean(destroyMethod = "close")
	public Client2 client2() {
		Client2 client = new Client2();
		client.setHost("host");
                // 초기화는 직접 수행, 소멸은 커스텀 매서드를 통해 수행
                client.connect();
		return client;
	}
}
  • 위와 같이 직접 매서드를 호출하여 초기화도 가능
  • 이때 해당 클래스 내부적으로 인터페이스를 상속받아 초기화를 실행해 두 번 초기화 과정이 수행되지 않도록 주의

initMethod 속성과 destroyMethod 속성에 지정한 매서드는 파라미터가 없어야 함
만약 파라미터가 존재할 경우, 스프링 컨테이너는 Exception을 발생

3. Bean 객체의 생성과 관리 범위

  • Bean 객체는 기본적으로 싱글톤(singleton)의 범위를 가짐
  • Bean 객체의 범위를 프로토타입(prototype)으로 지정시 Bean 객체를 매번 새롭게 생성
  • 특정 Bean을 프로토타입으로 지정하려면, 다음과 같이 @Scope 어노테이션을 @Bean 어노테이션과 함께 사용
    • 프로토타입은 새로운 요청이 들어오면, 기존의 Bean을 바탕으로 복사해 새로운 객체를 생성
    • 이렇게 만들어진 새로운 Bean은 스케줄링, 멀티스레딩등에 사용이 됨
      // AppCtxWithprototype.java
      @Configuration
      public class AppCtxWithPrototype {
              // 해당 Bean 객체를 프로토 타입으로 지정
          @Bean
          @Scope("prototype")
          public Client client() {
              Client client = new Client();
              client.setHost("host");
              return client;
          }
          // 해당 Bean을 명시저으로 싱글톤으로 지정
          @Bean(initMethod = "connect", destroyMethod = "close")
          @Scope("singleton")
          public Client2 client2() {
              Client2 client = new Client2();
              client.setHost("host");
              return client;
          }
      }
      
  • 각 경우 해당 Bean 객체의 동일성은 아래와 같음
      // 프로토 타입의 경우
      Client client1 = ctx.getBean("client", Client.class);
      Client client2 = ctx.getBean("client", Client.class);
      // client1 != client -> true
    
      // 싱글톤의 경우
      Client client1 = ctx.getBean("client", Client2.class);
      Client client2 = ctx.getBean("client", Client2.class);
      // client1 != client -> false
    
  • 프로토 타입을 갖는 Bean은 완전한 라이프 사이클을 따르지 않음
  • 생성 및 초기화는 컨테이너가 해주지만, 소멸 처리는 코드에서 직접 해줘야 함