[spring] 빈 생명주기와 콜백 (스프링 기본편 by 김영한)

su_y2on·2022년 1월 31일
0

Spring

목록 보기
24/30
post-thumbnail

빈 생명주기와 콜백

스프링 빈은 객체가 생성된 뒤에 의존성이 주입됩니다. 의존관계가 완벽하게 주입이 되어야 필요한 데이터들을 쓸 수 있는 준비도 끝이 납니다. 그렇다면 이런 세팅이 다 끝난 뒤에 연결을 하고 스프링 빈이 죽기전에 연결을 해제해야하는 경우가 생긴다면 어떻게 그 시기를 알 수 있을까요?



바로 스프링이 제공하는 콜백으로 그 시기를 알 수 있습니다. 스프링 빈의 이벤트 라이프사이클은 아래와 같습니다

========================================================================

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

========================================================================

초기화 콜백과 소멸전 콜백은 각각 스프링빈에 의존관계주입이 완료, 스프링빈이 소멸 직전을 알려주는 콜백입니다.
콜백함수의 제공방법은 크게 3가지가 있습니다


1. 인터페이스

InitializingBean과 DisposableBean 인터페이스에서 afterPropertiesSetdestory를 각각 지원받아 초기화와 소멸콜백을 구현할 수 있습니다.

스프링 빈으로 등록해줄 NetworkClient를 만들어서 생성자와 setter를 만들어줍니다.

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();
           }
     }
}
 

🧐테스트코드

LifeCycleConfig를 통해 NetworkClient를 스프링 빈으로 등록을 해주고있습니다. 여기서는 의존성주입과정은 없습니다. 다만 setUrl을 통해서 NetworkClient의 데이터 Url을 세팅해주는 부분이 있습니다.

public class BeanLifeCycleTest {

      @Test
      public void lifeCycleTest() {
          ConfigurableApplicationContext ac = new AnnotationConfigApplicationContext(LifeCycleConfig.class);
          NetworkClient client = ac.getBean(NetworkClient.class); 
          ac.close(); //스프링 컨테이너를 종료, ConfigurableApplicationContext 필요
      }
      
      @Configuration
      static class LifeCycleConfig {
          @Bean
          public NetworkClient networkClient() {
              NetworkClient networkClient = new NetworkClient(); // 생성
              networkClient.setUrl("http://hello-spring.dev");
              return networkClient;
	   } 
      }
}

출력결과를 토대로 스프링 빈의 라이프 사이클을 보면 먼저 스프링빈이 생성되고 그때 생성자호출이 됩니다. 생성과정에서 setUrl을 통해 값도 세팅이 됩니다. 그 뒤에 초기화 콜백이 호출됩니다. 따라서 connect함수도 호출되고 call함수도 호출됩니다. 마지막으로 ac.close()전에 destroy가 호출되어 disconnet가 호출된것이 보입니다.


출력결과

생성자 호출, url = null
NetworkClient.afterPropertiesSet
connect: http://hello-spring.dev
call: http://hello-spring.dev message = 초기화 연결 메시지
13:24:49.043 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Closing NetworkClient.destroy
close + http://hello-spring.dev




단점🧨

  • 초기화, 소멸 메서드의 이름 변경 불가
  • 스프링 전용 인터페이스기 때문에 코드전체가 스프링에 의존적이다
  • 직접 implement를 해줘야하기때문에 직접 코드를 수정할 수 없는 외부라이브러리에는 적용불가

이는 스프링 초창기에 나온 방식으로 지금은 거의 쓰이지 않습니다!





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

이번 방법은 설정파일에서 초기화, 소멸 메서드를 지정하는 방법입니다.

NetworkClient에 초기화, 소멸시에 불러올 메서드를 만들어줍니다. 지금은 init과 close로 만들어 줬습니다.

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();
	  } 
}

이제 설정정보에 가서 initMethod와 destoryMethod에 각각 메서드 이름을 넣어줍니다.

@Configuration
  static class LifeCycleConfig {
  
      @Bean(initMethod = "init", destroyMethod = "close")
      public NetworkClient networkClient() {
          NetworkClient networkClient = new NetworkClient();
          networkClient.setUrl("http://hello-spring.dev");
          return networkClient;
	} 
}

특징🍒

  • 메서드이름에 자유로움
  • 외부라이브러리를 가져다 쓸 때도 설정정보에 적어주면 되기 때문에 초기화, 종료 메서드를 적용가능

종료메서드의 추론

  • @Bean의 destroyMethoddefault가 inferred로 되어있습니다. 추론을 한다는 뜻인데요. 즉 close, shutdown같은 메서드를 알아서 종료메서드로 인식한다는 것입니다. 따라서 종료메서드는 굳이 적어두지 않아도 close와 shutdown으로 이름을 정해주면 잘 작동합니다!





3. 애노테이션 @PostConstruct, @PreDestroy

마지막은 어노테이션을 이용하는 방법입니다.

NetworkClient에서 @PostConstruct와 @PreDestory를 붙여줘서 간단하게 콜백함수들을 정해주는 방식입니다. 그러면 설정정보에서는 따로 적어줄 필요가 없습니다.

@PostConstruct
public void init() {
System.out.println("NetworkClient.init"); connect();
call("초기화 연결 메시지");
}

@PreDestroy
public void close() {
    System.out.println("NetworkClient.close");
    disConnect();
}

특징🍒

  • 최신 스프링에서 가장 권장하는 방식
  • 자바표준에서 제공되는 어노테이션이기때문에 스프링에 종속적이지 않음
  • 컴포넌트 스캔과 잘 어울림
  • 외부라이브러리에 적용할 수 없음. 이때는 @Bean방법 사용해야함

0개의 댓글