빈 생명주기 콜백

  • 스프링 빈의 경우, 생성자 주입을 제외하고 이같은 라이프 사이클을 가진다.(객체 생성 -> 의존관계 주입)
  • 스프링은 의존관계 주입이 완료되면 스프링 빈에게 콜백 메서드를 통해 초기화 시점을 알려줌.
  • 또한 스프링 컨테이너가 종료되기 직전에 소멸 콜백을 줌. 따라서 안전하게 종료 작업을 진행할 수 있음.
  • 스프링 빈의 이벤트(생명주기) 사이클는 아래와 같다.
  • (스프링 컨테이너 생성 -> 스프링 빈 생성 -> 의존관계 주입 -> 초기화 콜백 -> 사용 -> 소멸 전 콜백 -> 스프링 종료)
  • 객체의 생성(메모리 할당)초기화(동작 행위)분리해서 설계하는 것이 유지보수에 좋다.

 

 

InitializingBean, DisposableBean

  • 의존관계 설정 후와 스프링 빈 소멸 시 호출되는 함수의 추상화 인터페이스. 이런 것도 있다 정도로 넘어가자.
  • 코드가 전용 인터페이스에 의존하고 초기화, 소멸 메서드의 이름을 변경할 수 없는 단점이 있다.
  • 또한 코드를 고칠 수 없는 외부 라이브러리에 적용할 수 없다. 이러한 단점들 때문에 거의 사용하지 않는다.
public class NetworkClient implements InitializingBean, DisposableBean {

    private String url;

    public NetworkClient() {
        System.out.println("생성자 호출, url = " + url);
        connect();
        call("초기화 연결 메시지");
    }

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

 

 

 

 

빈 등록 초기화, 소멸 메서드

  • 메서드 이름을 자유롭게 변경 가능하고, 스프링 빈이 스프링 코드에 의존하지 않음.
  • 코드가 아니라 설정 정보를 사용하기 때문에, 코드를 고칠 수 없는 외부 라이브러리에도 적용 가능.
  • 종료 메서드에는 추론 기능이 있는데, @Bean의 destroyMethod의 기본값이 "(inferred)"로 등록되어 있음. 그래서 "close", "shutdown" 라는 이름의 메서드르 자동으로 호출해준다.(빈으로 등록되어 있는 경우에만).
  • 추론 기능을 사용하기 싫으면 destroyMethod = "" 공백으로 해주면 된다.
public class NetworkClient {

    private String url;

    public NetworkClient() {
        System.out.println("생성자 호출, url = " + url);
        connect();
        call("초기화 연결 메시지");
    }

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



// -------------------------------------------



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(initMethod = "init", destroyMethod = "close")
        public NetworkClient networkClient() {
            NetworkClient networkClient = new NetworkClient();
            networkClient.setUrl("http://hello-spring.dev");

            return networkClient;
        }
    }
}

 

 

 

 

@PostConstruct, @PreDestroy

  • 최신 스프링에서 가장 권장하는 방식. 외부 라이브러리에는 적용하지 못함. 외부 라이브러리에서 초기화, 종료 해야 하면 이전의 @Bean 기능을 사용하자.
  • 스프링의 종속적인 기술이 아니라, 스프링이 아닌 다른 컨테이너에서도 잘 동작한다.
  • 컴퍼넌트 스캔과도 잘 어울린다.
public class NetworkClient {

    private String url;

    public NetworkClient() {
        System.out.println("생성자 호출, url = " + url);
        connect();
        call("초기화 연결 메시지");
    }

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

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

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



// ---------------------------------------------------



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://hello-spring.dev");

            return networkClient;
        }
    }
}