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

JUJU·2024년 2월 26일
0

Spring

목록 보기
8/21
본 포스트는 김영한 개발자님의 스프링 핵심 원리 강의를 듣고 정리한 것입니다.
※ 코드는 강의에서 사용된 것과 다릅니다.
jaewon-ju Github Address

✏️ 객체의 초기화 & 종료

애플리케이션 시작 시점에 데이터베이스, 네트워크 소켓 등을 연결하는 작업을 초기화라고 한다.
초기화를 한 뒤에는 애플리케이션 종료 시점에 해당 연결을 모두 끊는 작업이 필요하다.

객체의 초기화는 어떻게 할 수 있을까?

객체(빈)가 생성되는 시점은 해당 객체의 생성자가 호출될 때이다.
그럼 생성자 안에 초기화 코드를 넣으면 될까?

아래 코드를 보자.

public class NetworkClient implements InitializingBean, DisposableBean {
    private String server;

    public NetworkClient() {
        // 초기화
        connect();
    }

    public void setServer(String server) {
        this.server = server;
    }
    
    public void connect() {
        System.out.println("connect to: " + server);
    }
    
    
    public void disconnect() {
        System.out.println("close: " + server);
    }
}

결과는 【 connect to: null 】 이 출력될 것이다.
객체를 생성한 후에 setter를 사용해야 server 필드에 값이 들어가기 때문이다.

개발자의 의도는 객체 생성 직후에 초기화를 하고 싶은 것인데...


■ 빈의 라이프사이클

스프링 빈은 다음과 같은 라이프사이클을 가진다.
객체 생성 ➜ 의존관계 주입
(생성자 주입의 경우 예외적으로 생성 & 주입이 동시에 발생)


의존관계 주입이 끝나야 객체를 사용할 수 있다.
개발자는 의존관계 주입이 끝난 직후에 초기화를 하고자 한다.
어떻게 이 시점을 알 수 있을까?
✔︎ 스프링이 알려준다.


■ 빈의 이벤트 라이프사이클

스프링 빈의 이벤트 라이프사이클은 다음과 같다.

  1. 스프링 컨테이너 생성
  2. 빈 생성
  3. 의존관계 주입
  4. 초기화 콜백 메소드
  5. 사용
  6. 소멸전 콜백 메소드
  7. 스프링 종료

콜백 메소드란, 객체의 초기화/종료 시점에 호출되도록 지정된 메소드이다.

스프링은 의존관계 주입이 완료되면 스프링 빈에게 초기화 시점을 알려준다.
그럼 지정된 콜백 메소드를 통해서 초기화를 자동으로 진행한다.

또한, 컨테이너가 종료되기 직전에 빈에게 종료 시점을 알려주고 종료 콜백 메소드가 호출된다.


■ 콜백의 3가지 방법

초기화/종료 시점에 호출될 콜백 메소드는 개발자가 지정할 수 있다.
다음과 같이 3가지 방법이 지원된다.

  1. 인터페이스 InitializingBean, DisposableBean
  2. @Bean 속성 사용
  3. 어노테이션 @PostConstruct, @PreDestroy



✏️ InitializingBean, DisposableBean

InitializingBean, DisposableBean은 스프링 전용 인터페이스이다.
현재는 잘 사용되지 않는다!!

사용 방법

  1. 초기화/종료 메소드를 넣고 싶은 객체에 위의 두 인터페이스를 implements 한다.
  2. afterPropertiesSet(), destroy() 메소드를 오버라이드 한다.
  3. 지정된 콜백 메소드는 생성 직후, 소멸 직전에 자동으로 호출된다.
public class NetworkClient implements InitializingBean, DisposableBean {
    private String server;

    public NetworkClient() {
        // 생성자에서는 아무것도 안함
    }

    public void setServer(String server) {
        this.server = server;
    }
    
    public void connect() {
        System.out.println("connect to: " + server);
    }
    
    
    public void disconnect() {
        System.out.println("close: " + server);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        // 초기화 콜백 메소드
        // 초기화 시점(의존관계 주입 직후)에 자동으로 호출됨
        connect();
    }

    @Override
    public void destroy() throws Exception {
    	// 종료 콜백 메소드
        // 종료 시점(컨테이너 종료 직전)에 자동으로 호출됨
        disconnect();
    }
}

테스트 코드

class NetworkClientTest {

    @Configuration
    static class BeanLifeCycleConfig {
        @Bean
        public NetworkClient networkClient() {
            NetworkClient networkClient = new NetworkClient();
            networkClient.setServer("http://my-spring");
            return networkClient;
        }
    }

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

new NetworkClient()로 객체를 생성한 뒤에, setServer 함수로 필드에 값을 주입하면 networkClient 빈을 사용할 수 있다.
이 시점에 afterPropertiesSet() 메소드가 호출되어 초기화가 자동으로 진행된다.

ac.close() 호출 시, 컨테이너가 종료되면서 destroy() 메소드가 자동으로 호출된다.

■ 문제점

  1. 스프링 전용 인터페이스이므로, 스프링에 의존한다.
  2. 초기화, 소멸 메소드의 이름을 변경할 수 없다.
  3. 외부 라이브러리는 코드를 고칠 수 없으므로, 외부 라이브러리에 콜백 메소드를 넣을 수 없다.



✏️ @Bean 속성

설정 정보에 @Bean(initMethod = "init", destroyMethod = "close") 처럼 초기화, 소멸 메소드를 지정할 수 있다.

// NetworkClient 클래스에 추가
public void init() {
    connect();
}

public void close() {
    disconnect();
}
// 테스트 코드에 추가
@Bean(initMethod = "init", destroyMethod = "close")
public NetworkClient networkClient() {
    NetworkClient networkClient = new NetworkClient();
    networkClient.setServer("http://my-spring");
    return networkClient;
}

■ 특징

  • 메소드 이름을 자유롭게 줄 수 있다.
  • 스프링 빈이 스프링 코드에 의존하지 않는다. (순수 자바 객체도 사용 가능하다.)
  • 외부 라이브러리에도 적용가능하다.

■ 종료 메소드의 추론

  • destroyMethod 속성에는 종료 메소드를 추론하는 기능이 있다.

라이브러리는 대부분 close, shutdown 이라는 이름의 종료 메소드를 사용한다.
destroyMethod를 따로 적지 않더라도, 스프링 빈이 종료 메소드가 close, shutdown 일 것이라 추론하고 자동으로 호출한다.




✏️ @PostConstruct, @PreDestroy

위의 어노테이션을 적용하면, 콜백 메소드로 지정할 수 있다.
최신 스프링에서 가장 권장하는 방법이다.

// networkClient 클래스 수정
@PostConstruct
public void init(){
    connect();
}

@PreDestroy
public void close(){
    disconnect();
}

■ 특징

  • 메소드 이름을 자유롭게 줄 수 있다.
  • @PostConstruct와 @PreDestroy는 자바 표준이다! 스프링에 종속적이지 않다.

■ 문제점

  • 어노테이션을 코드에 작성해야하므로, 외부 라이브러리에 적용이 불가능하다.



✏️ 정리

  • @PostConstruct, @PreDestroy 어노테이션을 사용하자
  • 외부 라이브러리를 초기화, 종료해야 하면, @Bean(initMethod, destroyMethod) 를 사용하자.



REFERENCE

스프링 핵심 원리 - 김영한 개발자님

profile
개발자 지망생

0개의 댓글

관련 채용 정보