본문 바로가기

SpringFramework Core - I. IoC 컨테이너/6. Bean 커스터마이징하기

1.6.1.5. Startup과 Shutdown 콜백

Lifecycle 인터페이스는 스스로의 라이프사이클 요구사항을 가진 객체들을 위한 본질적인 메소드를 정의한다 (몇몇 백그라운드 프로세스를 시작시키고 정지시키는 등).  

public interface Lifecycle {

    void start();
    
    void stop();
    
    boolean isRunning();
}

스프링의 관리를 받는 모든 객체는 Lifecycle 인터페이스를 구현하고 있을 것이다. ApplicationContext가 시작과 정지 신호를 받으면 해당 context 내에 정의된 모든 Lifecycle 구현체에 그 신호를 전달한다. 다음에서 보여주는 LifecycleProcessor에게 위임함으로써 그 일을 해낸다.

public interface LifecycleProcessor extends Lifecycle {

    void onRefresh();
    
    void onClose();
}

LifecycleProcessor는 Lifecycle 인터페이스를 extend한다는 것을 주의하라. LifecycleProcessor는 context가 새로고침되고 닫히는 것에 대해 반응하기 위해 두 가지 메소드를 추가하고 있다. 

 

  보통의 org.springframework.context.Lifecycle 인터페이스는 명확한 시작/정지 신호에 대해서만 작동한다는 점을 주의하라. 그리고 context가 새로고침될 때 저절로 startup하지 않는다. 특정 bean에 대해 자동 startup을 포함한 정교한 제어를 위해서는 org.springframework.context.SmartLifecycle을 대신 구현해야한다는 점을 고려하라.

 

또한, 소멸 전에 정지 신호가 도착하는 것이 보장되지 않는다는 점을 주의하라. 보통의 shutdown에서는 일반적인 소멸 콜백이 전파되기 전에 먼저 모든 Lifecycle bean들이 정지 신호를 받는다. 그러나 context의 라이프타임동안 hot한 새로고침이나 실패한 새로고침 시도에서는 오직 소멸 메소드만이 호출을 받는다.

 

startup과 shutdown 호출의 순서는 중요하다. 만약 어떤 두 bean 사이의 "depends-on" 관계가 존재한다면 의존하는 쪽이 의존되는 쪽보다 나중에 시작한다. 그리고 의존하는 쪽이 먼저 정지한다. 그러나 가끔, 직접적인 의존성들이 미상인 경우가 있다. 단지 어떤 타입의 객체가 또다른 타입의 객체보다 먼저 시작해야한다는 사실만 알고 있을지 모른다. 그런 경우에는 SmartLifecycle 인터페이스가 다른 옵션을 정의해준다. Phased라는 슈퍼 인터페이스로부터 정의된 getPhase()메소드라는 옵션이다. 다음은 Phased 인터페이스의 정의를 보여준다.

public interface Phased {
    
    int getPhase();
}

다음 예시는 SmartLifecycle 인터페이스의 정의를 보여준다.

public interface SmartLifecycle extends Lifecycle, Phased {

    boolean isAutoStartup();
    
    void stop(Runnable callback);
    
}

시작할 때, 가장 낮은 단계의 객체들이 먼저 시작한다. 정지할 때는, 반대의 순서를 따른다. 그러므로 Integer.MIN_VALUE를 반환하는 getPhase() 메소드를 가진 SmartLifecycle을 구현한 객체는 가장 먼저 시작하고 가장 나중에 정지할 것이다. 그 반대로 Integer.MAX_VALUE 값을 가진 단계에서는 객체가 가장 늦게 시작하고 가장 빨리 멈춘다(다른 프로세스들의 작동이 의존하고 있기 때문이다). 해당 단계의 값을 고려해보자면, SmartLifecycle을 구현하지 않는 보통의 Lifecycle 객체의 기본 단계 값은 0이라는 사실을 알아두는 것도 중요하다. 그러므로 음수의 단계 값은 그 객체가 기본값의 컴포넌트보다 먼저 시작하고 나중에 정지한다는 것을 알려준다. 양수의 단계 값은 그 반대로 작용할 것이다.

 

SmartLifecycle에 의해 정의된 정지 메소드는 콜백을 받는다. 어떤 실행문이든 shutdown 프로세스가 완료된 후에는 콜백의 run() 메소드를 호출한다. 이는 필요한 곳에서 비동기적으로 shutdown이 가능하게끔 해준다. LifecycleProcessor 인터페이스의 기본적인 구현체인 DefaultLifecycleProcessor는 콜백을 부를 각각의 단계에 있는 객체 그룹들을 정해진 시간동안 기다려주기 때문이다. 기본적인 단계 별 대기시간은 30초이다. context 내에서 lifecycleProcessor라는 bean을 정의함으로써 기본적인 라이프사이클 프로세서 인스턴스를 오버라이드할 수 있다. 만약 단순히 대기시간만 바꾸고 싶다면, 다음 정의로도 충분할 것이다.

<bean id="lifecycleProcessor" class="org.springframework.context.support.DefaultLifecycleProcessor">
    <!-- millisecond로 작성된 timeout 값 -->
    <property name="timeoutPerShutdownPhase" value="10000" />
</bean>

 일찍이 언급했듯이, LifecycleProcessor 인터페이스는 context를 새로고침하거나 닫기위한 콜백 메소드 또한 정의한다. 후자는 명확하게 stop()이 호출된 것처럼 shutdown 프로세스를 진행시킨다. 하지만 그 과정은 context가 닫힐 때 일어난다. 한편 '새로고침' 콜백은 SmartLifecycle bean의 또다른 기능을 가능하게 한다. 모든 bean들이 인스턴스화되고 초기화된 후 context가 새로고침될 때 해당 콜백이 호출된다. 그때, 기본적인 라이프사이클 프로세서는 각각의 SmartLifecycle 객체의 isAutoStartup() 메소드를 통해 boolean 값을 반환받아 체크한다. 만약 true라면, 객체가 context의 명확한 호출이나 스스로가 가진 start() 메소드를 기다리기보다 그 시점에 즉시 시작한다 (context 새로고침과는 달리, 기본적인 context 실행에서는 context의 시작이 자동으로 이루어지지 않는다). phase의 값과 의존관계들은 먼저 설명한 것처럼 startup 순서를 결정한다.