본문 바로가기

web +a

스프링빈 생명주기 콜백 메서드

요약 : 

스프링빈 초기화/종료 콜백을 사용하기 위해

- @PostConstruct, @PreDestroy를 쓰자!

- 근데 외부라이브러리는 수정 못하니까(저 애노테이션 못 붙이니까) @Bean(initMethod = "init", destroyMethod = "close") 사용해서 알맞게 지정해서 사용하자. 


빈 생명주기 콜백 메서드 ... 초기화/종료 콜백

 

애플리케이션이 실행 & 종료되면서 객체의 초기화 & 종료 작업이 필요하다.

스프링은 '초기화 콜백'을 통해 그러한 초기화 시점을 알려주고,

'소멸 콜백'을 통해 빈 소멸 직전에 안전하게 종료 작업을 진행할 수 있게 한다. 

 

 

∨ 스프링 빈의 이벤트 라이프사이클 (singleton일 경우)

↓ 스프링 컨테이너 생성

 스프링 빈 생성

 의존관계 주입

recall) 생성자 주입인 경우 생성&주입 단계가 분리되진 않음

 초기화 콜백 (빈이 생성되고 빈 의존관계 주입 완료 후 호출)

 사용

 소멸전 콜백 (빈이 소멸되기 직전에 호출) 

 스프링 종료

 

 

cf) 객체의 생성과 초기화는 분리하자.

즉, 생성자 안에 초기화하는 큰 로직을 넣지 말자는 것이다.

단일책임원칙(SRP)관련 얘기다.

생성자는 메모리할당&객체 생성에만 집중하고,

초기화는 비교적 무거운 동작인 외부커넥션 연결 등을 수행하도록 분리하자. 

 


이러한 초기화/소멸 콜백, 즉 빈 생명주기 콜백은 스프링이 지원하는데,

크게 3가지 방법으로 콜백을 지원한다.  

 

인터페이스(InitializingBean, DisposableBean) : 거의 사용하지 X

설정 정보에 초기화 메서드, 종료 메서드 지정 

@PostConstruct, @PreDestroy 애노테이션 지원 (권장)

 

 

 

→ lifecycle 관련 Test코드를 통해 이 3가지 방법을 공부해보자.

상황 : NetworkClient가 connect()하고 disconnect()하는 메서드를 호출할 시점을 스프링의 콜백을 통해 관리해줄 것이다.

 

→ Test 코드

그냥 컨테이너에 빈 등록하고 컨테이너 닫아주는 동작의 라이프사이클을 확인할 것이다 ↓

public class BeanLifeCycleTest {

    @Test
    public void lifeCycleTest() {
        ConfigurableApplicationContext ac = new AnnotationConfigApplicationContext(LifeCycleConfig.class);
        NetworkClient client = ac.getBean(NetworkClient.class);
        ac.close();
    }

    @Configuration
    static class LifeCycleConfig {
        @Bean
        public NetworkClient networkClient() {
            NetworkClient networkClient = new NetworkClient();
            networkClient.setUrl("http://~~");
            return networkClient;
        }
    }

cf) ApplicationContext > ConfigurableApplicationContext > AnnotationConfigApplicationContext

 

 


인터페이스(InitializingBean, DisposableBean) : 스프링 초창기 방식! 거의 사용X

NetworkClient 클래스가 implements InitializingBean, DisposableBean하면

각각 afterPropertiesSet()과 destroy()를 오버라이딩하게 되어있다. 

- afterPropertiesSet() : 즉, 생성&주입해 propertiy를 세팅한 후에 할 동작이다.

-destroy() : 빈 소멸 직전에 할 동작이다.

 

그래서 NetworkClient 클래스를 다음과 같이 작성할 수 있다. 

더보기
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 {
        System.out.println("NetworkClient.afterPropertiesSet");
        connect();
        call("초기화 연결 메시지");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("NetworkClient.destroy");
        disconnect();
    }
}

 

 

테스트코드 동작 (: networkClient 빈 등록) 과정을 text로 보면 이렇다.

∨ 생성자 호출 단계

: url에 아무 값도 들어오지 않은 상태 (setter 주입이기 때문에 당연)

 생성&DI를 마치고 나서

: 초기화 콜백 afterPropertiesSet()에서 connect(), call() 메서드가 실행되었다.

 ac.close()로 인한 빈 소멸 직전에 (Closing contatiner)

: 소멸전 콜백 destroy()에서 close() 메서드가 안전하게 호출되었다.

 

 

이러한 인터페이스 스프링 콜백 방식의 단점

스프링 전용 인터페이스에 의존해서 작성해야 한다.

annotation 수준이 아니라 아예 코드레벨로 가져와야 한다.

메서드 이름도 그대로 써야 하고... 등등.. 

 


 설정 정보에 초기화 메서드, 종료 메서드 지정

그냥 @Bean에 아예 '이건 초기화 메서드다', '이건 종료 메서드다' 지정해주는 방식이다.

 @Bean(initMethod = "init", destroyMethod = "close")

 

 

● 위 인터페이스 방식과 달리, 콜백 메서드 이름을 자유롭게 지정 가능하다.

- 초기화 메서드 : init()로 지정함

- 소멸전 메서드 : close()로 지정함

 

 

 여기서 보이는 인터페이스 방식과 다른 또다른 장점은,

빈이 스프링 코드에 의존하지 않는다는 것이다.

아래 코드는 빈으로 등록할 클래스 내부에 자유롭게 작성해준 초기화/소멸 콜백 메서드다.

그냥 자바 코드다. 어떠한 스프링 기능에 의존하지 않는다. 

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

    public void close() {
        System.out.println("NetworkClient.close");
        disconnect();
    }

이렇게 콜백메서드를 자유롭게 작성해준 뒤에는

config(설정정보) 안에 @Bean(initMethod = "init", destroyMethod = "close")만 해주면 된다.

=> 이 빈의 라이프사이클 콜백 메서드를 지정해준 것이다.

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

 

 구식의 인터페이스 콜백 방식의 큰 단점 중 하나가 '외부 라이브러리를 이용할 경우'이다.

외부 라이브러리의 경우 이용해야 하는 콜백 메서드가 지정되어있기도 하다. 그러나 인터페이스 콜백 방식을 쓰면, 외부 라이브러리는 수정할 수 없기 때문에, 빈으로 등록할 클래스에 콜백 인터페이스를 적용하는 방식의 콜백은 적용할 수 없다.

 

하지만 이렇게 설정 정보에 @Bean(initMethod = "init", destroyMethod = "close")로 등록해주는 콜백 방식을 사용하면, 외부 라이브러리에도 초기화 & 종료 메서드를 적용할 수 있다.

그냥 @Bean할 때 콜백으로 사용할 메서드명만 적어주면 되니까!!

 

 

● 참고)

지정하지 않아도 자동으로 destroyMethod가 호출된다?!

 

@Bean(initMethod = "init", destroyMethod = "close")처럼 초기화/종료 콜백 메서드를 지정해주었다.

그러나 @Bean의 destroyMethod 속성은 디폴트가 "(inffered)"로 되어있다.

더보기
String destroyMethod() default AbstractBeanDefinition.INFER_METHOD;

 

public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
		implements BeanDefinition, Cloneable {
    ...
    public static final String INFER_METHOD = "(inferred)";
    ...
}

 

일반적으로 외부 라이브러리의 종료메서드는 close, shutdown 라는 이름을 가진다.

그래서  딱히 @Bean에 destroyMethod 값을 지정하지 않아도 디폴트가 "(inffered)"로 되어있기 떄문에

종료 메서드를 추론해서 자동으로 호출해줄 수 있다!

 

 

 


 @PostConstruct, @PreDestroy 애노테이션 지원 (권장)

이 애노테이션은 스프링이 아니라 java에서 공식으로 지원하는 거라서 다른 프레임워크에서도 사용가능하다.

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

 

 

그냥 메서드에 애노테이션 붙이면 끗..

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

 

그럼 @Bean 등록 할 때 알아서 초기화/종료 메서드로 사용된다.

와우~ 너무좋은걸~

 

 

 

단점은 외부라이브러리에 적용 못한다는 거!
당연.. 클래스 열어서 @PostConstruct @PreDestroy 붙여줄 수 없으니.. 

 

그래서 외부라이브러리 초기화/종료 콜백 메서드 사용하기 위해서는 두번째 방법인 

@Bean(initMethod = "init", destroyMethod = "close") 이용해주면 되겠다!

 

 


요약 : 

스프링빈 초기화/종료 콜백을 사용하기 위해

- @PostConstruct, @PreDestroy를 쓰자!

- 근데 외부라이브러리는 수정 못하니까(저 애노테이션 못 붙이니까) @Bean(initMethod = "init", destroyMethod = "close") 사용해서 알맞게 지정해서 사용하자. 

반응형
다른 블로그