Home Bean 생명주기 콜백
Post
Cancel

Bean 생명주기 콜백

이 게시글은 인프런의 유료 강의 스프링 핵심 원리 - 기본편을 수강하며 공부한 내용을 담고있습니다.
저작권에 의해 강의내용에 해당하는 전체 코드는 공개할 수 없습니다.
저작권에 의해 모든 내용을 담을 수 없습니다. 중복 및 반복되는 내용은 생략했습니다.

Bean 생명주기와 콜백 호출

Bean 의 생명주기는 다음과 같다.

  1. Spring container 생성
  2. Spring Bean 생성(+ 생성자 주입)
  3. 의존관계 주입
  4. 초기화 콜백
  5. Bean이 여기저기에 사용되다가
  6. 소멸 전 콜백
  7. 소멸

초기화 콜백
Spring Bean 이 생성되고 의존관계 주입까지 끝나면 ‘나 준비됐어’ 라는 콜백을 준다.
이 콜백은 객체를 초기화 할 수 있도록 Bean이 준비가 된 직후 타이밍을 잡을 수 있게 해준다.
객체를 초기화 할거면 생성자에서 하면 되는데, 왜 따로 메서드를 작성해서 초기화를 할까?
일단 생성자에서 초기화 하는 것들은 비교적 간단한 것들만 하는 것이 권장된다.
예를 들어, DB 연결 설정과 같이 시간이 오래 걸리거나 복잡한 초기화 작업은 비교적 무거운 작업이므로 생성자에서 초기화 하지않고 별도로 분리해두는 것이 유지보수 측면에서 더 바람직하다.

소멸 전 콜백
빈이 소멸되기 직전에 호출된다. 뒤에서 다룬다.

생명주기를 확인하기 위해 다음 예제코드를 두 개 작성한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package hello.core.lifecycle;

public class NetworkClient {
    private String url; // 접속해야할 서버의 url

    public NetworkClient() {
        System.out.println("url = " + url);
        connect();
        call("초기화 연결 메세지");
    }

    public void setUrl(String url) {
        this.url = url;
    }

    // 서비스 시작시 호출되는 메서드
    public void connect(){
        System.out.println(url + " 에 연결을 시도합니다.");
    }

    public void call(String msg){
        System.out.println("call: " + url + " 에 " + msg + " 메시지를 전송합니다.");
    }

    // 서비스 종료시 호출
    public void disconnect() {
        System.out.println(url + " 과 연결을 종료합니다.");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class BeanLifeCycleTest {
    @Test
    public void lifeCycleTest() {
        ConfigurableApplicationContext ac = new AnnotationConfigApplicationContext(LifeCycleConfig.class);
        NetworkClient client = ac.getBean(NetworkClient.class);
        ac.close(); // ApplicationContext 가 ConfigurableApplicationContext 인 경우 close() 메서드를 호출할 수 있다.

    }

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

ac 가 종료되는 시점을 의도적으로 보기 위해 close 메서드를 호출한다.
다만 close 메서드는 ConfigurableApplicationContext 에서 호출할 수 있으므로 자료형을 ConfigurableApplicationContext로 해준다.
실행할 필요 없이 코드만 봐도 알겠지만 print 문은 다 null 과 관련된 내용으로 나온다.
코드의 원래 의도는 url 과 연결이 완료된 후(setUrl 메서드 호출 후)에 connect() 와 call() 을 호출하려는 것이다.
위 코드에서는 그냥 생성자에 url 을 건네주면 되는데 왜 이렇게 해? 라는 의문이 생긴다.
이건 예시를 간단히 만들다보니 그런거고 접속하려는 url 은 생성자로 건네줄 수 없고 반드시 setUrl 메서드를 통해 전달받을 수 있다고 생각한다.
앞으로 배울 초기화 콜백을 위해 외부에서 줄 수 밖에 없는 상황이라고 설정한 것이다.

생명주기 콜백을 지원하는 방법

  1. 인터페이스(InitializingBean, DisposableBean)
  2. 설정 정보에 초기화 메서드, 종료 메서드 지정
  3. @PostConstruct, @PreDestroy 지원

InitializingBean, DisposableBean

위 예제 코드에 ‘초기화 콜백’과 ‘소멸전 콜백’을 넣는 방법 중 하나이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
public class NetworkClient implements InitializingBean, DisposableBean{
  // 생략
  @Override
  public void afterPropertiesSet() throws Exception {
    connect();
    call("초기화 연결 메세지");
  }

  @Override
  public void destroy() throws Exception {
    disconnect();
  }
}

‘초기화 콜백’과 ‘소멸전 콜백’을 넣는 방법은 간단하다. InitializingBean 과 DisposableBean 을 implements 하면된다.
그리고 destroy() 와 afterPropertiesSet() 메서드를 오버라이딩한다.
메서드 이름을 보면 유추할 수 있듯이 afterPropertiesSet 은 프로퍼티 설정이 끝난 후(생성자 호출 후)를 의미하고 destroy 는 소멸 전 호출되는 메서드이다.
위 코드대로 수정 후 실행해보면 setUrl() 이 먼저 호출된 후 afterPropertiesSet() 이 수행되고, 마지막으로 destroy() 가 수행되는 것을 확인할 수 있다.

이 방법은 아주 오래전에 고안된 방법이며, 스프링 전용 인터페이스에 의존한다는 단점이 있다.

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

특정 메서드를 초기화 콜백, 소멸전 콜백으로 등록하는 방법이다.

1
2
3
4
5
6
7
8
9
10
11
public class NetworkClient{
  // 생략
  public void myInit() throws Exception {
    connect();
    call("초기화 연결 메세지");
  }

  public void myClose() throws Exception {
    disconnect();
  }
}
1
2
3
4
5
6
7
8
9
10
// 생략
@Configuration
static class LifeCycleConfig {
  @Bean(initMethod = "myInit", destroyMethod = "myClose")
  public NetworkClient networkClient() {
    NetworkClient networkClient = new NetworkClient();
    networkClient.setUrl("http://hello-spring.dev");
    return networkClient;
  }
}

NetworkClient 코드를 보면 더이상 InitializingBean 과 DisposableBean 을 implements 할 필요가 없으며, 메서드 이름도 내가 원하는대로 작성할 수 있다.
내가 정의한 메서드를 초기화 콜백, 소멸전 콜백으로 등록하기 위해서는 @Bean(initMethod="", destroyMethod="") 를 사용해 명시해주면 된다.

정리

  • 스프링에 의존하지 않음
  • 이름을 원하는대로 지을 수 있음
  • 외부 라이브러리의 메서드도 초기화 콜백 및 소멸전 콜백으로 등록 가능
  • destroyMethod 는 추론기능이 있음

destroyMethod 는 값을 생략하면 객체 내의 close 메서드 또는 shutdown 메서드를 종료할 때 사용한다고 추론하여 자동으로 소멸전 콜백에 등록한다.

@PostConstruct, @PreDestroy

가장 간단하면서도 최신 스프링에서 추천하는 방법이다.

1
2
3
4
5
6
7
8
9
10
11
12
public class NetworkClient{
  // 생략
  @PostConstruct
  public void myInit() {
    connect();
    call("초기화 연결 메세지");
  }
  @PreDestroy
  public void myClose() {
    disconnect();
  }
}

엥? 애노테이션을 쓰면 스프링에 종속적인거 아닌가?? 싶지만 두 애노테이션은 JSR-250 이라는 Java 표준이다.
패키지도 javax(java 상위 버전에선 jakarta) 에 속해있다.

장점

  • 간단하다.
  • 이 방법은 @Bean 이 필요 없으므로 컴포넌트 스캔과 잘 어울린다.
  • 최신 스프링에서 권장한다.
  • 자바 표준이므로 스프링뿐만 아니라 다른 컨테이너에서도 지원한다.

단점

  • 외부 라이브러리에 사용할 수 없다.

외부 라이브러리 코드는 수정할 수 없으므로 외부 라이브러리의 메서드를 콜백에 등록하고 싶다면 @Bean(initMethod="", destroyMethod="") 를 사용하는 방법밖에 없다.



이전 포스팅 : 자동,수동 빈 등록 기준
다음 포스팅 : 빈 스코프

This post is licensed under CC BY 4.0 by the author.

자동, 수동 빈 등록 기준

Github actions 으로 구현하는 Spring -> AWS CI/CD 자동화