Java Spring Core

Bean Life Cycle

Spring Bean은 객체가 생성된 이후에 의존관계 주입을 거쳐 사용 준비 상태가 됩니다. 이때 부터, 객체는 초기화 작업을 진행할 수 있으며, 객체의 사용이 종료가 되었으면 객체를 정상적으로 반환해야한다.

가령 외부와 연결해야되는(DB 연결) 객체를 생성해야 된다고 하면, app 시작 당시에 사전에 연결되 객체가 필요하며, app이 종료하고자 할때, 객체 또한 정상적인 종료작업을 거쳐야한다. 이를 지원하기 위해 Spring에서는 초기화 함수, 종료함수를 제공한다.

Life Cycle Test

Network Client

public class NetworkClient{
    private String url;

    public NetworkClient() {
        System.out.println("Constructor Method executed,url = " + url);
        connect();
        call("Calling~");
    }

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

    //서비스 시작시 호출
    public void connect(){
        System.out.println("Connection: ..."+url);
    }
    //서비스 진행 동안 호출
    public void call(String message) {
        System.out.println("Call: " + url + " message = " + message);
    }
    //서비스 종류 후 호출
    public void disconnect(){
        System.out.println("Disconnected..." + url);
    }
}

Test

public class BeanLifeCycleTest{
    @Test
    public void lifeCycleTest(){
        ConfigurableApplicationContext ac = new AnnotationConfigApplicationContext(LifeCycleConfig.class);
        NetworkClient networkClient = 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;
        }
    }
}

*ConfigurableApplicationContext는 AnnotationConfigApplicationContext 상위의 클래스인데, application의 종료를 호출할 수 있다.

Results

Constructor Method executed,url = null
Connection: ...null
Call: null message = Calling~

위의 Bean 동작 과정을 보면, 객체 생성은 문제 없이 되지만, url 설정을 객체 생성 이후에 진행하게 되어 값이 null값이 저장되는 것을 확인할 수 있고, application이 언제 종료함에 따라 객체의 종료를 강제할 수 없다.

그래서 객체를 이용한 작업 수행 전에 초기화 작업이 이루어져야 하며, 또한, application 종료 전에 객체의 종료함수 또한 호출되도록 해야한다. 이를 위해 Spring에서는 객체 생성 및 의존성 주입이 모두 완료된 후 호출되는 초기화 콜백, 스프링 컨테이너가 종료되기 직전에 호출되는 소멸 콜백이 있다.

Spring Bean Life Cycle Structure

  1. Spring Container create
  2. Spring Bean create
  3. DI
  4. Initialiazable Callback(초기화 콜백)
  5. Using Spring Beans
  6. Disposable Callback(소멸 콜백)
  7. Spring Container End

Spring Bean에서는 위의 Life Cycle에 따라 사용자에게 필요한 콜백들을 제공한다.

Seperate Creation from Initialization

객체 생성을 담당하는 부분과 객체를 초기화하는 부분은 분리해서 설계해야한다.

생성자와 같이 객체 생성을 담당하는 함수는 오로지 객체 생성을 하는 역할을 수행하며,

초기화하는 별도의 setter함수를 이용해서 수행한다.

생성과 초기화의 분리는 유지보수 관점에서 좋다.

추가로, DB 연결과 같은 부분을 담당하는 초기화가 있을때, 초기화 함수를 분리하므로써, DB 연결이 꼭 필요할때까지 DB 연결을 뒤로 미룰 수 있는 지연 실행의 장점 또한 취할 수 있다.

Callback Types

InitializingBean, DisposableBean Interfaces

인터페이스 내에 생성,종료 함수를 구현하는 방식이 있다.

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

    public NetworkClient() {
        System.out.println("Constructor Method executed,url = " + url);
    }

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

    //서비스 시작시 호출
    public void connect(){
        System.out.println("Connection: ..."+url);
    }
    //서비스 진행 동안 호출
    public void call(String message) {
        System.out.println("Call: " + url + " message = " + message);
    }
    //서비스 종류 후 호출
    public void disconnect(){
        System.out.println("Disconnected..." + url);
    }

    //의존관계 주입이 완료되면 초기화 콜백이 호출된다.
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("Initializable Callback");
        connect();
        call("Calling~");
    }
    //스프링 컨테이너 종료되기 전에 호출된다.
    @Override
    public void destroy() throws Exception {
        System.out.println("Disposable Callback");
        disconnect();
    }
}

위의 클래스를 보면,

  1. 초기화 콜백
    • InitializableBean -> afterPropertiesSet
  2. 소멸 콜백
    • DisposableBean->destroy

메소드를 구현하고 있다. 생성/초기화 작업이 모두 완료된 다음, 초기화 콜백이 호출되고, 스프링 컨테이너 종료 전에 소멸 콜백이 호출된다.

Results

Constructor Method executed,url = null
Initializable Callback
Connection: ...http://hello-spring.dev
Call: http://hello-spring.dev message = Calling~
Disposable Callback
Disconnected...http://hello-spring.dev

Lifecycle에 맞게 수행되는 것을 확인해볼 수 있다.

위 방식의 특징으로는

  1. Spring 전용 interface로 java 코드 상에서는 테스트를 진행할 수 없다.
  2. 초기화, 소멸 함수에 대한 이름을 수정할 수 없다
  3. 수정 불가능한 외부 라이브러리에 적용할 수 없다.(애초에 코드의 수정이 필요하기 때문)

@Bean(initMethod = “init”, destroyMethod = “close”)

@Bean의 option을 이용하는 방식이다. initMethod에 초기화 함수의 이름을, destroyMethod에 소멸 함수의 이름을 넣는다. 이를 위해 기존 클래스의 초기화,종료 함수의 이름을 수정한다.

NetworkClient Class

public void init() throws Exception {
    System.out.println("Initializable Callback");
    connect();
    call("Calling~");
}
public void close() throws Exception {
    System.out.println("Disposable Callback");
    disconnect();
}

Test

@Bean(initMethod = "init",destroyMethod = "close")
public NetworkClient networkClient(){
    NetworkClient networkClient = new NetworkClient();
    networkClient.setUrl("http://hello-spring.dev");
    return networkClient;
}

위와 같이 실행하더라도 1번 방식과 동일한 결과가 나온다.

위 방식의 특징으로는

  1. 초기화/소멸 함수의 이름을 자유롭게 할 수 있다.
  2. 스프링 빈(NetworkClient)가 Spring 에 의존적이지 않다(InitializableBean,DisposableBean과 같이 Spring에 의존적인 코드를 활용하지 않았다.)
  3. 수정 불가능한 외부 라이브러리에 적용 가능 (코드 레벨의 수정이 아니라, 외부 설정값을 바꾸는 것이기 때문)
  4. destroyMethod=”inferred”으로 default 처리 되어 있는데, 이렇게 되면 close,shutdown 과 같은 method의 이름을 자동으로 추정해서 호출한다는 특징이 있다.

@PostConstruct, @PreDestroy

이름에서 잘 들어나듯, PostConstruct가 초기화 콜백, PreDestroy가 소멸 콜백이다.

이 Annotation을 초기화 함수, 소멸 함수에 표기한다.

Network Client

@PostConstruct
public void init() throws Exception {
    System.out.println("Initializable Callback");
    connect();
    call("Calling~");
}
@PreDestroy
public void close() throws Exception {
    System.out.println("Disposable Callback");
    disconnect();
}

위와 같이 Annotation만을 이용해 간편하게 사용가능

위 방식의 특징으로는

  1. 초기화/소멸 함수의 이름을 자유롭게 할 수 있다.
  2. 해당 annotation들은 javax 패키지로, java에서 공식적으로 제공하는 라이브러리에 속한다. 따라서 Spring 외의 컨테이너에서도 활용가능
  3. 수정 불가능한 외부 라이브러리에 적용 불가능 (Spring Bean에 대한 Annotation 표기와 같은 코드레벨의 수정이 수반된다.)

Best Option

기본적으로 @PostConstruct, @PreDestroy annotation을 사용하도록 하며, 코드 수정이 불가능한 외부 라이브러리에 대해서는 @Bean(initMethod=”“,destroyMethod=”“)을 이용한다.

References

link: inflearn

link:springcore

link:spring_framework

댓글남기기