Customizing the Nature of a Bean

2024. 11. 14. 14:25Spring Framework/Spring IoC

Lifecycle Callbacks

컨테이너의 빈 생명주기 관리와 상호 작용하려면, 스프링의 InitializingBean과 DisposableBean 인터페이스를 구현할 수 있습니다. 컨테이너는 전자에 대해서는 afterPropertiesSet()을, 후자에 대해서는 destroy()를 호출하여, 빈의 초기화와 소멸 시 특정 행동을 수행할 수 있게 합니다.

현재 Spring 애플리케이션에서 생명주기 콜백을 받기 위해 JSR-250의 @PostConstruct와 @PreDestroy 어노테이션을 사용하는 것이 일반적으로 최선의 방법으로 간주됩니다. 이 어노테이션들을 사용하면, 빈이 Spring 특정 인터페이스에 결합되지 않습니다. 자세한 내용은 @PostConstruct와 @PreDestroy 사용하기를 참조하세요. JSR-250 어노테이션을 사용하고 싶지 않지만 여전히 결합을 제거하고 싶다면, init-method와 destroy-method 빈 정의 메타데이터를 고려하세요.

 

JSR-250이란?
JSR-250은 Java Community Process(JCP)를 통해 개발된 Java 표준 기술의 일부입니다. JSR(Java Specification Request)은 Java 플랫폼에 추가하고자 하는 새로운 사양이나 기존 사양의 변경을 제안하는 공식 문서입니다. JSR-250은 "Common Annotations for the Java Platform"으로 명명되어 있으며, 자바 플랫폼 전반에 걸쳐 공통적으로 사용되는 어노테이션들을 정의합니다. JSR-250에 포함된 주요 어노테이션들 중 일부는 다음과 같습니다: @PostConstruct: 객체가 생성되고 의존성 주입이 완료된 후 실행되어야 하는 메소드에 사용됩니다. 초기화 작업을 위해 종종 사용됩니다.
@PreDestroy: 객체가 소멸되기 전에 실행되어야 하는 메소드에 사용됩니다. 주로 정리 작업을 위해 사용됩니다.
@Resource: 의존성 주입을 위해 사용됩니다. 특정 자원에 대한 참조를 주입하는 데 사용됩니다.
JSR-250은 Java EE 표준의 일부로 채택되었으며, 이후 Spring과 같은 프레임워크에서도 널리 사용되고 있습니다. 이 어노테이션들은 플랫폼에 종속되지 않는 방식으로 어플리케이션의 구성 요소를 정의하고 관리하는 데 도움을 줍니다.

 

내부적으로, Spring 프레임워크는 BeanPostProcessor 구현을 사용하여 찾을 수 있는 모든 콜백 인터페이스를 처리하고 적절한 메소드를 호출합니다. 기본적으로 Spring이 제공하지 않는 사용자 정의 기능이나 다른 생명주기 행동이 필요한 경우, 직접 BeanPostProcessor를 구현할 수 있습니다. 자세한 정보는 Container Extension Points를 참조하세요.

init 및 destroy 콜백 외에도, Spring 관리 객체는 Lifecycle 인터페이스를 구현하여 컨테이너의 자체 생명주기에 의해 주도되는 startup 및 shutdown 과정에 참여할 수 있습니다.

이 섹션에서는 생명주기 콜백 인터페이스에 대해 설명합니다.

 

Initialization Callbacks

org.springframework.beans.factory.InitializingBean 인터페이스는 컨테이너가 빈에 필요한 모든 속성을 설정한 후 초기화 작업을 수행할 수 있도록 합니다. InitializingBean 인터페이스는 단 하나의 메소드를 명시합니다:

void afterPropertiesSet() throws Exception;

InitializingBean 인터페이스의 사용은 권장하지 않습니다. 이는 코드를 Spring에 불필요하게 결합시키기 때문입니다. 대신, @PostConstruct 어노테이션을 사용하거나 POJO init 메소드를 지정하는 것이 좋습니다. XML 기반 구성 메타데이터의 경우, init-method 속성을 사용하여 void 파라미터 없는 시그니처를 가진 메소드의 이름을 지정할 수 있습니다. Java 구성에서는 @Bean의 initMethod 속성을 사용할 수 있습니다.  Receiving Lifecycle Callbacks을 참조하세요. 다음 예를 고려해 보세요:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {

    @Bean(initMethod = "init")
    public ExampleBean exampleInitBean() {
        return new ExampleBean();
    }
}
public class ExampleBean {

    public void init() {
        // do some initialization work
    }
}

앞서 제시된 예시는 다음 예시(두 개의 목록으로 구성됨)와 거의 동일한 효과를 가집니다:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {

    @Bean
    public AnotherExampleBean exampleInitBean() {
        return new AnotherExampleBean();
    }
}
public class AnotherExampleBean implements InitializingBean {

    @Override
    public void afterPropertiesSet() {
        // do some initialization work
    }
}

 

그러나 앞서 언급된 두 예시 중 첫 번째 예시는 코드를 Spring에 결합시키지 않습니다.

(이 문장의 의미는 첫 번째 예시가 Spring 프레임워크에 특정한 인터페이스나 클래스에 의존하지 않는다는 것입니다. 즉, 해당 코드는 Spring에 독립적으로 작성되었으며, 다른 환경이나 프레임워크로 이전하는 데에 있어서도 더 유연하게 대처할 수 있습니다.

이는 일반적으로 좋은 소프트웨어 설계 원칙을 따르는 것으로, 특정 프레임워크나 라이브러리에 강하게 결합(tight coupling)되는 것을 피하고, 대신 유지 보수성과 확장성을 높이기 위해 느슨한 결합(loose coupling)과 모듈성을 추구하는 것을 의미합니다. 첫 번째 예시에서는 @PostConstruct, @PreDestroy 어노테이션 또는 POJO 초기화 메소드를 사용하여 Spring에 특화된 인터페이스(InitializingBean, DisposableBean)를 사용하지 않음으로써 이를 달성하고 있습니다)

@PostConstruct 및 일반적인 초기화 메서드는 컨테이너의 싱글톤 생성 잠금(singleton creation lock) 내에서 실행된다는 점에 유의해야 합니다. @PostConstruct 메서드가 리된 후에야 빈 인스턴스는 완전히 초기화되고 다른 객체에 공개될 준비가 된 것으로 간주됩니다. 이러한 개별 초기화 메서드는 구성 상태를 검증하고 주어진 구성에 따라 데이터 구조를 준비하는 용도로만 사용해야 하며, 외부 빈에 접근하는 추가 작업을 수행해서는 안 됩니다. 그렇지 않으면 초기화 교착 상태(initialization deadlock)가 발생할 위험이 있습니다.

비용이 많이 드는 후속 초기화(post-initialization) 작업(예: 비동기 데이터베이스 준비 단계)을 트리거해야 하는 경우, 빈은 SmartInitializingSingleton.afterSingletonsInstantiated()를 구현하거나 Context Refresh Event를 활용해야 합니다. 이를 위해 ApplicationListener<ContextRefreshedEvent>를 구현하거나 애노테이션 기반 방식인 @EventListener(ContextRefreshedEvent.class)를 선언할 수 있습니다. 이러한 방식은 모든 일반 싱글톤 초기화 이후에 실행되며, 따라서 어떤 싱글톤 생성 잠금 외부에서 실행됩니다.

또는 (Smart)Lifecycle 인터페이스를 구현하여 컨테이너의 전체 라이프사이클 관리와 통합할 수 있습니다. 여기에는 자동 시작(auto-startup) 메커니즘, 파괴 이전의 정지 단계(pre-destroy stop step), 그리고 잠재적인 정지/재시작(stop/restart) 콜백 등이 포함됩니다(아래를 참조).

 

Destruction Callbacks

org.springframework.beans.factory.DisposableBean 인터페이스를 구현하면, 빈을 포함하는 컨테이너가 Destroy 될 때 콜백을 받을 수 있습니다. DisposableBean 인터페이스는 단 하나의 메소드를 명시합니다:

void destroy() throws Exception;

 

DisposableBean 콜백 인터페이스의 사용은 권장하지 않습니다. 이는 코드를 Spring에 불필요하게 결합시키기 때문입니다. 대신, @PreDestroy 어노테이션을 사용하거나, 빈 정의에서 지원되는 일반적인 메소드를 지정하는 것이 좋습니다. XML 기반 구성 메타데이터에서는 태그에 destroy-method 속성을 사용할 수 있습니다. Java 구성에서는 @Bean의 destroyMethod 속성을 사용할 수 있습니다. 생명주기 콜백 수신을 참조하세요. 다음 정의를 고려해 보세요:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {

    @Bean(destroyMethod = "cleanup")
    public ExampleBean exampleDestructionBean() {
        return new ExampleBean();
    }
}
public class ExampleBean {

    public void cleanup() {
        // do some destruction work (like releasing pooled connections)
    }
}

앞서 제시된 정의는 다음 정의와 거의 동일한 효과를 가집니다:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {

    @Bean
    public AnotherExampleBean exampleDestructionBean() {
        return new AnotherExampleBean();
    }
}
public class AnotherExampleBean implements DisposableBean {

    @Override
    public void destroy() {
        // do some destruction work (like releasing pooled connections)
    }
}

그러나 두 가지 이전 정의 중 첫 번째 정의는 코드를 Spring과 결합시키지 않습니다.

또한 Spring은 destroy 메서드를 추론하는 기능을 지원하며, public close 또는 shutdown 메서드를 감지합니다. 이것은 Java 구성 클래스의 @Bean 메서드에 대한 기본 동작이며, 자동으로 java.lang.AutoCloseable 또는 java.io.Closeable 구현과 일치하며, 이로 인해 destory 로직이 Spring과 결합되지 않습니다.

XML을 사용하여 destroy 메서드 추론을 수행하려면 요소의 destroy-method 속성에 특별한 (추론된) 값을 할당하면 됩니다. 이 값은 Spring에게 특정 빈 정의에 대해 빈 클래스에서 public close 또는 shutdown 메서드를 자동으로 감지하도록 지시합니다. 또한 요소의 default-destroy-method 속성에 이 특별한 (추론된) 값을 설정하여 이 동작을 여러 빈 정의 집합에 적용할 수도 있습니다.

아래는 이를 자바 기반 구성 정보로 변환한 것입니다:

1. 특정 빈 정의에 대한 destroy 메서드 추론:

@Configuration
public class AppConfig {

    @Bean(destroyMethod = "close")
    public MyBean myBean() {
        return new MyBean();
    }
}

위의 코드에서 @Bean 어노테이션 내에서 destroyMethod 속성을 설정하여 빈이 destroy될 때 호출할 메서드를 지정할 수 있습니다. 위 예제에서는 close라는 이름의 메서드가 빈 destory 시 자동으로 호출됩니다.

2. 여러 빈 정의에 대한 default-destroy-method 설정:

@Configuration
public class AppConfig {

    @Bean
    public MyBean myBean1() {
        return new MyBean();
    }

    @Bean
    public MyBean myBean2() {
        return new MyBean();
    }

    // 다른 빈들의 destroy 메서드를 설정하지 않고, 기본적으로 "close" 메서드를 사용하도록 설정
    @Bean(destroyMethod = "")
    public MyBean myBean3() {
        return new MyBean();
    }
}

위의 코드에서 @Bean(destroyMethod = "")을 사용하여 myBean3() 메서드에 대한 빈 정의에서 destory 메서드를 설정하지 않고, 대신 기본 값인 close 메서드를 사용하도록 설정합니다. 이것은 여러 빈 정의에 동일한 destroy 메서드를 적용하려는 경우에 유용합니다.

Default Initialization and Destroy Methods

Spring의 InitializingBean 및 DisposableBean 콜백 인터페이스를 사용하지 않고 init 및 destory 메서드 콜백을 작성할 때, 일반적으로 init(), initialize(), dispose() 등과 같은 이름의 메서드를 작성합니다. 이러한 라이프사이클 콜백 메서드의 이름은 이 프로젝트 내에서 표준화되어야 하므로, 모든 개발자가 동일한 메서드 이름을 사용하여 일관성을 유지할 수 있도록 합니다.

Spring 컨테이너를 설정하여 모든 빈에 대한 init 및 destroy 콜백 메서드 이름을 "찾도록" 구성할 수 있습니다. 이것은 응용 프로그램 개발자로서 init()라는 초기화 콜백을 작성하고 각 빈 정의에 init-method="init" 속성을 구성할 필요 없이 사용할 수 있음을 의미합니다. Spring IoC 컨테이너는 해당 메서드를 빈이 생성될 때 호출하며(이전에 설명한 표준 라이프사이클 콜백 계약에 따라), 이 기능은 init 및 destroy 메서드 콜백에 대한 일관된 명명 규칙을 강제합니다.

만약 초기화 콜백 메서드가 init()로 명명되고 destroy 콜백 메서드가 destroy()로 명명되었다면, 아래 예제와 같이 클래스가 보일 것입니다:

public class DefaultBlogService implements BlogService {

    private BlogDao blogDao;

    public void setBlogDao(BlogDao blogDao) {
        this.blogDao = blogDao;
    }

    // this is (unsurprisingly) the initialization callback method
    public void init() {
        if (this.blogDao == null) {
            throw new IllegalStateException("The [blogDao] property must be set.");
        }
    }
}

그런 다음 다음과 유사한 형태의 빈에서 해당 클래스를 사용할 수 있습니다:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {

    @Bean(initMethod = "init")
    public DefaultBlogService blogService() {
        DefaultBlogService blogService = new DefaultBlogService();
        blogService.setBlogDao(blogDao());
        return blogService;
    }

    @Bean
    public BlogDao blogDao() {
        return new BlogDao();
    }
}

 

Combining Lifecycle Mechanisms

Spring 2.5부터, 빈 라이프사이클 동작을 제어하기 위한 세 가지 옵션이 제공됩니다:

  1. InitializingBean 및 DisposableBean 콜백 인터페이스
  2. 사용자 정의 init() 및 destroy() 메서드
  3. @PostConstruct 및 @PreDestroy 어노테이션

이러한 메커니즘을 결합하여 특정 빈의 라이프사이클을 제어할 수 있습니다.

빈에 대해 여러 라이프사이클 메커니즘이 구성되고 각 메커니즘이 서로 다른 메서드 이름으로 설정된 경우, 아래에 나열된 순서에 따라 각 설정된 메서드가 실행됩니다. 하지만, 동일한 메서드 이름(예: 초기화 메서드로 init())이 여러 라이프사이클 메커니즘에 설정된 경우, 해당 메서드는 앞서 설명한 것처럼 한 번만 실행됩니다.

같은 빈에 대해 여러 라이프사이클 메커니즘이 구성되고 서로 다른 초기화 메서드가 있는 경우, 메서드는 다음 순서로 호출됩니다:

  1. @PostConstruct로 어노테이션된 메서드
  2. InitializingBean 콜백 인터페이스에서 정의된 afterPropertiesSet() 메서드
  3. 사용자 정의 초기화 메서드(init() 등)

destroy 메서드도 동일한 순서로 호출됩니다:

  1. @PreDestroy로 어노테이션된 메서드
  2. DisposableBean 콜백 인터페이스에서 정의된 destroy() 메서드
  3. 사용자 정의 소멸 메서드(destroy() 등)

 

Startup and Shutdown Callbacks

Lifecycle 인터페이스는 백그라운드 프로세스를 시작하거나 중지해야 하는 객체의 라이프사이클 요구사항을 정의하는 필수 메서드를 제공합니다:

public interface Lifecycle {

    void start();

    void stop();

    boolean isRunning();
}

Spring에서 관리되는 모든 객체는 Lifecycle 인터페이스를 구현할 수 있습니다. 이렇게 하면 애플리케이션 컨텍스트(ApplicationContext) 자체가 start 및 stop 신호를 받을 때(예: 런타임 중지/재시작 시나리오), 해당 컨텍스트 내에 정의된 모든 Lifecycle 인터페이스를 구현한 모든 빈에 이러한 호출을 전파합니다. 이는 다음과 같이 정의된 LifecycleProcessor에 위임하여 수행됩니다:

public interface LifecycleProcessor extends Lifecycle {

    void onRefresh();

    void onClose();
}

LifecycleProcessor는 Lifecycle 인터페이스의 확장으로, 컨텍스트가 갱신(refresh)되거나 닫힐(close) 때 반응하는 두 가지 메서드(onRefresh, onClose)를 추가합니다.

주의:org.springframework.context.Lifecycle 인터페이스는 명시적인 start 및 stop 알림을 위한 단순한 계약(인터페이스)이며, 컨텍스트 갱신 시 자동 시작(auto-startup)을 암시하지 않습니다. 특정 빈의 자동 시작 및 정교한 종료를 제어하려면 확장된 org.springframework.context.SmartLifecycle 인터페이스를 구현하는 것을 고려하세요.
또한, 정지(stop) 알림이 파괴(destroy) 전에 발생한다고 보장되지 않습니다. 일반적인 종료(shutdown)시 모든 Lifecycle 빈은 일반적인 destroy 콜백이 전파되기 전에 먼저 stop 알림을 받습니다. 그러나 컨텍스트 수명 중 핫 갱신(hot refresh) 또는 정지된 상태에서의 업데이트 시도시에는 destroy 메서드만 호출됩니다.

 

Start 및 Stop 호출 순서

시작 및 종료 호출 순서는 중요할 수 있습니다. 두 객체 간에 "종속성(depends-on)" 관계가 존재하는 경우, 종속된 객체는 의존 대상이 시작된 후에 시작되고, 의존 대상이 종료되기 전에 종료됩니다. 하지만 때로는 직접적인 종속성이 명확하지 않을 수 있습니다. 특정 유형의 객체가 다른 유형의 객체보다 먼저 시작되어야 한다는 것만 알 수도 있습니다.

이러한 경우, SmartLifecycle 인터페이스는 Phased 슈퍼 인터페이스에 정의된 getPhase() 메서드를 통해 다른 옵션을 제공합니다. 아래는 Phased 인터페이스의 정의입니다:

public interface Phased {

    int getPhase();
}

 

다음은 SmartLifecycle 인터페이스의 정의입니다:

public interface SmartLifecycle extends Lifecycle, Phased {

    boolean isAutoStartup();

    void stop(Runnable callback);
}

 

getPhase() 및 실행 순서

  • 시작 시: getPhase() 메서드가 반환하는 값이 가장 낮은 객체가 먼저 시작됩니다.
  • 종료 시: 반대로, 가장 높은 getPhase() 값을 가진 객체가 가장 먼저 종료됩니다.

따라서, getPhase() 메서드가 Integer.MIN_VALUE를 반환하는 객체는 가장 먼저 시작되고, 가장 마지막에 종료됩니다. 반면, Integer.MAX_VALUE를 반환하는 객체는 가장 마지막에 시작되고, 가장 먼저 종료됩니다. 이는 해당 객체가 다른 프로세스가 실행 중인 상태에 의존하기 때문일 가능성이 높습니다.

 

디폴트 값:
일반적인 Lifecycle 객체의 디폴트 getPhase() 값은 0입니다. 따라서 음수의 phase 값은 해당 객체가 표준 구성 요소보다 먼저 시작되고 나중에 종료되어야 함을 나타냅니다. 반대로, 양수의 phase 값은 표준 구성 요소보다 나중에 시작되고 먼저 종료되어야 함을 나타냅니다.

 

SmartLifecycle의 stop() 메서드와 비동기 종료

SmartLifecycle의 stop() 메서드는 Runnable 콜백을 받습니다. 구현체는 종료 프로세스가 완료된 후 해당 콜백의 run() 메서드를 호출해야 합니다. 이는 필요한 경우 비동기 종료를 가능하게 합니다.

LifecycleProcessor 인터페이스의 디폴트 구현체인 DefaultLifecycleProcessor는 각 phase의 객체 그룹이 해당 콜백을 호출할 때까지 최대 타임아웃 값만큼 대기합니다. 디폴트 타임아웃 값은 30초입니다.

타임아웃 값을 수정하려면 다음과 같이 lifecycleProcessor 빈을 정의할 수 있습니다:

<bean id="lifecycleProcessor" class="org.springframework.context.support.DefaultLifecycleProcessor">
    <!-- 타임아웃 값 (밀리초 단위) -->
    <property name="timeoutPerShutdownPhase" value="10000"/>
</bean>

 

SmartLifecycle과 컨텍스트 갱신(refresh)

LifecycleProcessor 인터페이스는 컨텍스트 갱신과 종료에 대한 콜백 메서드를 정의합니다. 종료 시에는 컨텍스트가 닫히면서 stop()이 명시적으로 호출된 것처럼 동작합니다. 반면, 갱신(refresh) 콜백은 SmartLifecycle 빈의 또 다른 기능을 활성화합니다.

컨텍스트가 갱신되면(모든 객체가 인스턴스화되고 초기화된 후), SmartLifecycle 객체의 isAutoStartup() 메서드에서 반환된 boolean 값이 확인됩니다. true를 반환하면, 해당 객체는 컨텍스트의 시작(start()) 메서드가 명시적으로 호출될 때까지 기다리지 않고 바로 시작됩니다.

 

Shutting Down the Spring IoC Container Gracefully in Non-Web Applications

이 섹션은 비웹 애플리케이션에만 적용됩니다. Spring의 웹 기반 ApplicationContext 구현체는 관련 웹 애플리케이션 종료 시 Spring IoC 컨테이너를 우아하게 종료하기 위한 코드를 이미 포함하고 있습니다.

Spring의 IoC 컨테이너를 비웹 애플리케이션 환경(예: 리치 클라이언트 데스크톱 환경)에서 사용하는 경우, JVM에 종료 후크(shutdown hook)를 등록해야 합니다. 이를 통해 우아한 종료를 보장하며, 싱글톤 빈의 관련 destroy 메서드를 호출하여 모든 리소스를 해제합니다. 이 destroy 콜백을 올바르게 구성하고 구현해야 한다는 점은 여전히 중요합니다.

종료 후크를 등록하려면 ConfigurableApplicationContext 인터페이스에 선언된 registerShutdownHook() 메서드를 호출해야 합니다. 다음 예제는 그 방법을 보여줍니다.

Java 코드

import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public final class Boot {

    public static void main(final String[] args) throws Exception {
        ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");

        // 위의 컨텍스트에 종료 후크 추가...
        ctx.registerShutdownHook();

        // 애플리케이션 실행 중...

        // main 메서드 종료, 애플리케이션 종료 전에 후크가 호출됨...
    }
}

 

Thread Safety and Visibility

Spring 코어 컨테이너는 생성된 싱글톤 인스턴스를 thread-safe 하게 공개하며, 싱글톤 잠금(singleton lock)을 통해 접근을 보호하고 다른 스레드에서의 가시성을 보장합니다.

결과적으로, 애플리케이션에서 제공하는 빈 클래스는 초기화 상태의 가시성에 대해 걱정할 필요가 없습니다. 초기화 단계 동안에만 수정되는 일반적인 구성 필드는 volatile로 표시할 필요가 없으며, 초기화 단계 동안 수정 가능한 setter 기반 구성 상태에 대해서도 final과 유사한 가시성을 제공합니다. 이러한 필드가 빈 생성 단계와 초기 공개 이후에 변경되는 경우, 해당 필드는 volatile로 선언되거나 접근 시 공통 잠금으로 보호되어야 합니다.

컨트롤러 인스턴스나 리포지토리 인스턴스와 같은 싱글톤 빈 인스턴스의 구성 상태에 대한 동시 접근은 컨테이너 측에서 안전한 초기 공개 이후에는 완벽히 스레드 안전합니다. 여기에는 일반 싱글톤 FactoryBean 인스턴스도 포함되며, 이는 일반 싱글톤 잠금 내에서 처리됩니다.

파괴 콜백(destruction callback)의 경우, 구성 상태는 스레드 안전하게 유지되지만, 초기화와 파괴 사이에 누적된 런타임 상태는 공통적인 Java 가이드라인에 따라 스레드 안전한 구조(간단한 경우에는 volatile 필드)로 유지해야 합니다.

위에서 설명한 것처럼 더 깊은 라이프사이클 통합에는 런타임에서 변경 가능한 상태(예: runnable 필드와 같은)가 포함되며, 이는 volatile로 선언해야 합니다. 일반적인 라이프사이클 콜백은 특정 순서를 따릅니다. 예를 들어, start 콜백은 완전한 초기화 이후에만 발생하도록 보장되며, stop 콜백은 초기 start 이후에만 발생합니다. 그러나 일반적인 stop 이후 destroy 순서에서는 특별한 예외가 있습니다. 취소된 부트스트랩 이후의 비정상적인 종료나 다른 빈에 의한 stop 타임아웃 발생 시, 선행 stop 없이도 즉시 destroy 콜백을 허용할 수 있도록 내부 상태를 구성하는 것이 강력히 권장됩니다.

 

ApplicationContextAware and BeanNameAware

org.springframework.context.ApplicationContextAware 인터페이스를 구현하는 객체 인스턴스를 ApplicationContext가 생성할 때, 해당 인스턴스는 해당 ApplicationContext에 대한 참조를 제공받습니다. 다음은 ApplicationContextAware 인터페이스의 정의를 보여줍니다:

public interface ApplicationContextAware {

    void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}

 

이렇게 하면 빈은 ApplicationContext 인터페이스를 통해, 또는 이 인터페이스의 하위 클래스(추가 기능을 노출하는 ConfigurableApplicationContext 등)로 참조를 캐스팅하여 자신을 생성한 ApplicationContext를 프로그래밍 방식으로 조작할 수 있습니다. 이 기능의 한 가지 사용 사례는 다른 빈을 프로그래밍 방식으로 검색하는 것입니다. 때로는 이러한 기능이 유용할 수 있지만, 일반적으로 사용을 피하는 것이 좋습니다. 이 방식은 코드를 Spring에 결합시키며, 협력자를 속성으로 빈에 제공하는 제어 역전(Inversion of Control) 스타일을 따르지 않기 때문입니다.

ApplicationContext의 다른 메서드를 통해 파일 리소스에 대한 액세스, 애플리케이션 이벤트 발행, MessageSource 접근과 같은 기능을 사용할 수 있습니다. 이러한 추가 기능에 대한 자세한 내용은 ApplicationContext의 추가 기능에서 설명합니다.

ApplicationContext에 대한 참조를 얻는 또 다른 대안은 autowiring입니다. 전통적인 컨스트럭터 및 byType 자동 와이어링 모드(Autowiring Collaborators에서 설명)로 컨스트럭터 아규먼트나 세터 메서드 파라미터에 ApplicationContext 유형의 종속성을 제공할 수 있습니다. 더 많은 유연성을 원한다면, 필드 및 다중 파라미터 메서드를 자동 와이어링할 수 있는 애노테이션 기반 자동 와이어링 기능을 사용하십시오. 이를 사용할 경우, @Autowired 애노테이션이 있는 필드, 컨스트럭터 아규먼트 또는 메서드 파라미터에 ApplicationContext가 자동으로 와이어링됩니다. 자세한 내용은 Using @Autowired를 참조하십시오.

org.springframework.beans.factory.BeanNameAware 인터페이스를 구현하는 클래스를 ApplicationContext가 생성할 때, 해당 클래스는 연관된 객체 정의에서 정의된 이름에 대한 참조를 제공받습니다. 다음은 BeanNameAware 인터페이스의 정의를 보여줍니다:

public interface BeanNameAware {

    void setBeanName(String name) throws BeansException;
}

이 콜백은 일반 빈 속성의 초기화가 완료된 후, InitializingBean.afterPropertiesSet() 또는 사용자 정의 초기화 메서드(init-method)와 같은 초기화 콜백 전에 호출됩니다.

 

Other Aware Interfaces

이전에 논의한 ApplicationContextAwareBeanNameAware 외에도, Spring은 빈이 특정 인프라 종속성을 필요로 한다는 것을 컨테이너에 알릴 수 있도록 다양한 Aware 콜백 인터페이스를 제공합니다. 일반적으로 인터페이스 이름은 종속성의 유형을 나타냅니다. 다음 테이블은 가장 중요한 Aware 인터페이스를 요약합니다:

Table 1. Aware interfaces

Name Injected Dependency Explained in…​
ApplicationContextAware Declaring ApplicationContext. ApplicationContextAware and BeanNameAware
ApplicationEventPublisherAware Event publisher of the enclosing ApplicationContext. Additional Capabilities of the ApplicationContext
BeanClassLoaderAware Class loader used to load the bean classes. Instantiating Beans
BeanFactoryAware Declaring BeanFactory. The BeanFactory API
BeanNameAware Name of the declaring bean. ApplicationContextAware and BeanNameAware
LoadTimeWeaverAware Defined weaver for processing class definition at load time. Load-time Weaving with AspectJ in the Spring Framework
MessageSourceAware Configured strategy for resolving messages (with support for parameterization and internationalization). Additional Capabilities of the ApplicationContext
NotificationPublisherAware Spring JMX notification publisher. Notifications
ResourceLoaderAware Configured loader for low-level access to resources. Resources
ServletConfigAware Current ServletConfig the container runs in. Valid only in a web-aware Spring ApplicationContext. Spring MVC
ServletContextAware Current ServletContext the container runs in. Valid only in a web-aware Spring ApplicationContext. Spring MVC

다시 한 번 주의해야 할 점은 이러한 인터페이스를 사용하면 코드가 Spring API에 종속되며 제어 역전(Inversion of Control) 스타일을 따르지 않는다는 것입니다. 따라서 이러한 인터페이스는 컨테이너에 대한 프로그래밍 방식의 접근이 필요한 인프라 빈에 사용하는 것을 권장합니다.

 

'Spring Framework > Spring IoC' 카테고리의 다른 글

Container Extension Points  (3) 2024.11.14
Classpath Scanning and Managed Components  (1) 2024.11.14
Composing Java-based Configurations  (1) 2024.11.14
Using the @Configuration annotation  (0) 2024.11.14
Using the @Bean Annotation  (0) 2024.11.14