빈 생명주기 콜백

유병익·2022년 10월 17일
0

스프링 핵심 원리

목록 보기
8/9
post-thumbnail

1. 빈 생명주기 콜백


  • 스프링을 통해 객체의 초기화 작업종료 작업을 어떻게 진행하는지 알아보자.

  • 스프링 빈은 간단하게 다음과 같은 라이프 사이클을 가진다. 객체 생성 → 의존관계 주입

  • 초기화 작업은 의존관계 주입이 모두 완료되고 난 다음에 호출해야 함

스프링은 의존관계 주입이 완료되면 스프링 빈에게 콜백 메서드를 통해서 초기화 시점을 알려주는 다양한 기능을 제공한다.

  • 스프링 빈의 이벤트 라이프사이클

📌 스프링 컨테이너 생성 → 스프링 빈 생성 → 의존관계 주입 → 초기화 콜백 사용

→ 소멸전 콜백 → 스프링 종료

📝 초기화 콜백: 빈이 생성되고, 빈의 의존관계 주입이 완료된 후 호출
소멸전 콜백: 빈이 소멸되기 직전에 호출

  • 참고
📝 객체를 생성하는 부분과 초기화 하는 부분을 명확하게 나누는 것이 유지보수 관점에서 좋다. 📝 스프링 컨테이너가 종료될 때 싱글톤 빈들도 함께 종료된다.

따라서, 스프링 컨테이너가 종료되기 직전에 소멸전 콜백이 일어난다.

  • 스프링은 크게 3가지 방법으로 빈 생명주기 콜백을 지원
    • 인터페이스(InitializingBean, DisposableBean)
    • 설정 정보초기화 메서드, 종료 메서드 지정
    • @PostConstruct, @PreDestroy 애노테이션 지원

2. InitializingBean, DisposableBean


  • Example
    package hello.core.lifecycle;
    import org.springframework.beans.factory.DisposableBean;
    import org.springframework.beans.factory.InitializingBean;
    
    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 {
    				 connect();
    				 call("초기화 연결 메시지");
    		 }
    
    		 @Override
    		 public void destroy() throws Exception {
    				 disConnect();
    		 }
    }

📝 InitializingBeanafterPropertiesSet()메서드로 초기화를 지원
DisposableBeandestroy() 메서드로 소멸을 지원

  • 초기화, 소멸 인터페이스 단점
    • 스프링 전용 인터페이스다.
    • 해당 코드가 스프링 전용 인터페이스에 의존한다.
    • .초기화, 소멸 메서드의 이름을 변경할 수 없다.
    • 내가 코드를 고칠 수 없는 외부 라이브러리에 적용할 수 없다.

참고: 인터페이스를 사용하는 초기화, 종료 방법은 스프링 초창기에 나온 방법들이고, 지금은 다음의 더 나은 방법들이 있어서 거의 사용하지 않는다.

3. 빈 등록 초기화, 소멸 메소드 지정


  • 설정 정보에 초기화, 소멸 메서드를 지정할 수 있다.
  • Example
    • 설정 정보를 사용하도록 변경

      package hello.core.lifecycle;
      
      public class NetworkClient {
      		 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);
      		 }
      
      		 public void init() {
      				 System.out.println("NetworkClient.init");
      				 connect();
      				 call("초기화 연결 메시지");
      		 }
      
      		 public void close() {
      				 System.out.println("NetworkClient.close");
      				 disConnect();
      		 }
      }
    • 설정 정보에 초기화 소멸 메서드 지정

      @Configuration
      static class LifeCycleConfig {
       @Bean(initMethod = "init", destroyMethod = "close")
       public NetworkClient networkClient() {
       NetworkClient networkClient = new NetworkClient();
       networkClient.setUrl("http://hello-spring.dev");
       return networkClient;
       }
      }
  • 설정 정보 사용 특징
    • 메서드 이름을 자유롭게 줄 수 있다.
    • 스프링 빈이 스프링 코드에 의존하지 않는다.
    • 코드를 고칠 수 없는 외부 라이브러리에도 초기화, 종료 메서드를 적용 가능
  • 종료 메서드 추론

📝 @BeandestroyMethod는 기본값이 inferred(추론)으로 등록되어 있다. 종료 메서드를 추론해서 호출

close , shutdown 라는 이름의 메서드를 자동으로 호출 (종료 메서드를 추론해서 호출)

직접 스프링 빈으로 등록하면 종료 메서드는 따로 적어주지 않아도 잘 동작한다.

추론 기능을 사용하기 싫으면 destroyMethod="" 처럼 빈 공백을 지정하면 된다

4. @PostConstruct & @PreDestroy


  • @PostConstruct , @PreDestroy 을 사용하면 가장 편리하게 초기화와 종료 실행 가능
  • Example
    package hello.core.lifecycle;
    import javax.annotation.PostConstruct;
    import javax.annotation.PreDestroy;
    
    public class NetworkClient { 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);
    		 }
    
    		 @PostConstruct
    		 public void init() {
    				 System.out.println("NetworkClient.init");
    				 connect();
    				 call("초기화 연결 메시지");
    		 }
    
    		 @PreDestroy
    		 public void close() {
    				 System.out.println("NetworkClient.close");
    				 disConnect();
    		 }
    }
    @Configuration
    static class LifeCycleConfig {
    
    		 @Bean
    		 public NetworkClient networkClient() {
    				 NetworkClient networkClient = new NetworkClient();
    				 networkClient.setUrl("http://hello-spring.dev");
    				 return networkClient;
    		 }
    }
  • @PostConstruct & @PreDestroy 특징
    • 최신 스프링에서 가장 권장하는 방법
    • 애노테이션 하나만 붙이면 되므로 매우 편리함
    • 스프링에 종속적인 기술이 아니라 JSR-250 라는 자바 표준 → 스프링이 아닌 다른 컨테이너에서도 동작
    • 컴포넌트 스캔과 잘 어울린다.
    • 유일한 단점 = 외부 라이브러리에는 적용 X 📝 외부 라이브러리를 초기화, 종료 해야 하면 @Bean의 기능을 사용하자
  • 정리

📝 @PostConstruct, @PreDestroy 애노테이션을 사용

외부 라이브러리를 초기화, 종료 → @BeaninitMethod , destroyMethod 를 사용

profile
Backend 개발자가 되고 싶은

0개의 댓글