본문 바로가기
공부/Spring

[Spring][스프링 핵심 원리] 빈 생명주기 콜백

by 웅대 2023. 1. 24.
728x90
반응형

본 포스팅은 김영한 강사님의 인프런 강의 "스프링 핵심 원리 - 기본편"을 정리한 포스팅으로 강의 자료에서 사용한 자료를 사용했음을 밝힙니다.

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8#

 

스프링 핵심 원리 - 기본편 - 인프런 | 강의

스프링 입문자가 예제를 만들어가면서 스프링의 핵심 원리를 이해하고, 스프링 기본기를 확실히 다질 수 있습니다., - 강의 소개 | 인프런...

www.inflearn.com

 

네트워크 관련된 객체를 만들 때 애플리케이션이 시작할 때 초기화를 하고 애플리케이션이 종료될 때 연결을 끊는 작업이 필요할 수 있다.

 

네트워크의 커넥션 연결과 같은 작업은 무겁기 때문에 객체의 생성과 초기화를 분리하는 편이 좋다.

 

그런데 이렇게 객체의 생성과 초기화를 분리할 경우 스프링 빈에 등록할 때 문제가 발생할 수 있다.

 

아래와 같은 네트워크 관련 객체를 스프링 빈에 등록해보자.

public class NetworkClient {
    private String url;
    public void setUrl(String url) {
        this.url = url;
    }
    public NetworkClient() {
        System.out.println("생성자 호출, 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(){

        connect();
        call("초기화 연결 메시지");
    }
    public void close(){
        disconnect();
    }
}
@Configuration
static class LifeCycleConfig{
    @Bean
    public NetworkClient networkClient(){
        NetworkClient networkClient = new NetworkClient();
        networkClient.setUrl("http://naver.com");
        return networkClient;
    }
}

스프링 빈의 라이프 사이클은 객체 생성 이후 의존 관계를 주입한다. 

 

그리고 보다시피 멤버 변수인 url을 setter를 통해 주입받고 있다.

 

connect 메소드가 실행되기 전에 url 변수를 초기화해야 메소드가 제대로 동작할 수 있다.

 

즉 위와 같이 객체의 생성과 초기화를 분리했다면 의존관계 주입이 완료된 다음에 connect 메소드를 실행해야한다.

 

그래서 우리는 의존 관계 주입이 완료된 시점을 알아내야한다.

 

참고로 생성자 주입의 경우 객체 생성과 동시에 의존 관계를 주입하기 때문에 의존 관계 주입이 완료된 시점을 몰라도 된다.

 

만약 NetworkClient 객체의 url을 setter 주입이 아니라 생성자 주입으로 구현을 했다면 connect 함수가 실행될 때는 이미 의존 관계 주입이 완료된 상태이기 때문이다.

 

스프링에서는 초기화 콜백소멸전 콜백을 제공한다.

 

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

 

이러한 생명주기 콜백을 사용하는 방법은 주로 두 가지 방법이 있다.

 

1. 설정 정보에서 메소드 지정

 

빈을 등록할 때 초기화 메소드와 종료 메소드를 지정할 수 있다.

 

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

initMethod와 destroyMethod에 메소드 이름을 넣어주면 지정이 완료된다.

 

참고로 종료 메소드의 이름이 close나 shutdown일 경우 이름을 정해주지 않아도 자동으로 찾아준다.

 

이 방법은 설정 정보에서 지정하는 것이기 때문에 코드 변경할 수 없는 외부 라이브러리를 사용할 때 유용하게 사용된다.

 

2. 어노테이션 사용

설정 정보에서 설정하는 것이 아니라 해당 메소드에 직접 어노테이션을 달아주는 방법도 존재한다.

 

초기화 메소드에는 @PostConstructor 어노테이션을 달아주고 종료 메소드에서는 @PreDestroy 어노테이션을 달아준다.

 

<설정 정보>

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

<NetworkClient  객체>

public class NetworkClient {
    private String url;
    public void setUrl(String url) {
        this.url = url;
    }
    public NetworkClient() {
        System.out.println("생성자 호출, 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(){

        connect();
        call("초기화 연결 메시지");
    }
    @PreDestroy
    public void close(){
        disconnect();
    }
}

 어노테이션 방식은 자바 표준이기 때문에 스프링이 아닌 다른 컨테이너에서도 사용할 수 있다.

 

그러나 이 방식은 코드를 변경할 수 없는 외부 라이브러리를 사용할 경우 사용이 불가능하다.

 

결론

 

  • 1. 네트워크와 같은 초기화가 무거운 작업의 경우 생성자 주입을 사용하지 않고 객체의 생성과 초기화를 분리한다.
  • 2. 외부 라이브러리를 초기화, 종료해야하면 설정 정보 등록 방식을, 그렇지 않다면 어노테이션 방식을 사용한다.

 

728x90
반응형

댓글