[Spring] Bean Lifecycle: 초기화와 소멸 콜백
정의
spring-ioc-di 가 Bean 등록과 주입 을 다룬다면, 이 페이지는 등록 이후 생성 → 사용 → 소멸 사이에 끼어드는 콜백을 다룬다. Spring 컨테이너가 단순히 객체를 만들고 끝내는 게 아니라, 의존성 주입 → 초기화 → BeanPostProcessor → 사용 → 소멸까지 여러 단계로 처리한다는 점이 핵심.
시각화
ApplicationContext refresh 시점에 1~6 단계를 모든 singleton Bean 에 대해 순차 실행. close 시점에 8 단계 (등록 역순).
전체 생명주기 단계
1. 컨테이너 시작
↓
2. Bean 메타데이터 등록 (BeanDefinition)
↓
3. BeanFactoryPostProcessor 실행 (BeanDefinition 수정)
↓
4. Bean 인스턴스화 (생성자 호출)
↓
5. 의존성 주입 (필드, setter)
↓
6. Aware 콜백
├─ BeanNameAware.setBeanName
├─ BeanFactoryAware.setBeanFactory
└─ ApplicationContextAware.setApplicationContext
↓
7. BeanPostProcessor.postProcessBeforeInitialization
↓
8. 초기화 콜백 (아래 3가지 모두 실행)
├─ @PostConstruct
├─ InitializingBean.afterPropertiesSet()
└─ @Bean(initMethod = "...") 또는 init-method
↓
9. BeanPostProcessor.postProcessAfterInitialization
└─ ← 여기서 AOP proxy 가 생성됨
↓
10. Bean 사용 (애플리케이션 코드)
↓
11. 컨테이너 종료
↓
12. 소멸 콜백 (등록 역순)
├─ @PreDestroy
├─ DisposableBean.destroy()
└─ @Bean(destroyMethod = "...") 또는 destroy-method
prototype scope 는 11~12 가 호출되지 않음 (컨테이너가 추적 안 함, spring-bean-scope 참고).
초기화 콜백 3가지
같은 시점에 호출되는 3가지 방법:
@PostConstruct (권장)
@Component
public class CacheLoader {
@Autowired DataSource dataSource;
Map<Long, User> cache;
@PostConstruct
public void warmup() {
cache = loadAll(dataSource);
}
}
JSR-250 표준 (Jakarta Annotations). framework 종속이 없어 가장 깔끔.
InitializingBean 인터페이스
@Component
public class CacheLoader implements InitializingBean {
@Override
public void afterPropertiesSet() {
cache = loadAll(dataSource);
}
}
Spring 인터페이스에 의존. 추천 안 함.
@Bean(initMethod) 또는 XML init-method
@Bean(initMethod = "warmup")
public CacheLoader cacheLoader() {
return new CacheLoader();
}
라이브러리 코드처럼 수정 불가능한 클래스 에 초기화 콜백을 붙일 때 유용.
소멸 콜백 3가지
@PreDestroy, DisposableBean.destroy(), @Bean(destroyMethod). 모두 컨테이너 종료 시 호출. AutoCloseable 을 구현한 Bean 은 close() 가 자동으로 destroyMethod 로 추론됨.
@Component
public class ConnectionPool implements AutoCloseable {
@Override
public void close() { // destroyMethod 자동 인식
// 풀 정리
}
}
비활성화하려면 @Bean(destroyMethod = "") 명시.
Aware 인터페이스
컨테이너 자체에 접근이 필요할 때:
@Component
public class BeanNameLogger implements BeanNameAware, ApplicationContextAware {
String myName;
ApplicationContext ctx;
@Override
public void setBeanName(String name) { this.myName = name; }
@Override
public void setApplicationContext(ApplicationContext ctx) { this.ctx = ctx; }
}
ApplicationContextAware 보다는 생성자 주입으로 ApplicationContext 받기 가 권장. Aware 는 framework-coupled 코드.
종료 순서
소멸은 등록의 역순. 의존성이 있는 Bean (A → B) 에서 A 가 먼저 소멸되어야 B 를 안전하게 사용할 수 있으므로:
init order: B → A (A 가 B 에 의존)
destroy order: A → B
@DependsOn 으로 명시할 수도 있지만, 일반적으로는 컨테이너가 의존성 그래프를 분석해 자동 처리.
SmartLifecycle
서버 시작/종료에 시점 제어가 필요한 컴포넌트 (Kafka consumer, scheduler 등):
@Component
public class KafkaConsumerWrapper implements SmartLifecycle {
boolean running;
@Override public void start() { /* consumer 시작 */ running = true; }
@Override public void stop() { running = false; /* 정리 */ }
@Override public boolean isRunning() { return running; }
@Override public int getPhase() { return 100; } // phase 낮을수록 먼저 시작 / 늦게 종료
@Override public boolean isAutoStartup() { return true; }
}
세 콜백을 한 클래스에 다 두면 호출 순서를 직접 확인할 수 있다:
@Component
public class CacheLoader implements InitializingBean, DisposableBean {
@PostConstruct
public void postConstruct() { System.out.println("1. @PostConstruct"); }
@Override
public void afterPropertiesSet() { System.out.println("2. InitializingBean"); }
public void initMethod() { System.out.println("3. @Bean(initMethod)"); }
@PreDestroy
public void preDestroy() { System.out.println("a. @PreDestroy"); }
@Override
public void destroy() { System.out.println("b. DisposableBean"); }
public void destroyMethod() { System.out.println("c. @Bean(destroyMethod)"); }
}
@Configuration
class Config {
@Bean(initMethod = "initMethod", destroyMethod = "destroyMethod")
CacheLoader cacheLoader() { return new CacheLoader(); }
}1. @PostConstruct
2. InitializingBean
3. @Bean(initMethod)
... 컨테이너 종료 ...
a. @PreDestroy
b. DisposableBean
c. @Bean(destroyMethod)SmartLifecycle 와 다른 점:
- 컨테이너의 모든 Bean 이 초기화된 후
start()호출 (refresh 완료 시점) - phase 로 시작/종료 순서 정밀 제어
stop()은 graceful shutdown 호출
함정과 베스트 프랙티스
- @PostConstruct 권장: 표준, 가독성, framework 독립
- 초기화 콜백에서 무거운 작업 주의: 컨테이너 시작 지연 + 실패 시 컨테이너 종료
- 소멸 콜백은 빠르게: graceful shutdown timeout 안에 끝나야
@Lazy의 함정: 첫 사용 시점에 초기화. 초기화 실패가 런타임에 터짐.- 순환 의존성: 생성자 주입에선 컨테이너 시작 시 즉시 에러. setter 주입에선 가능하지만 권장 안 됨.
- prototype scope 의 destroy 콜백은 호출 안 됨: 직접 cleanup 필요
@PreDestroy가 호출되려면 정상 종료: kill -9 / OOM 등은 불가
관련 위키
이 글의 용어 (6개)
- [Spring] @Autowired 해결 전략: @Qualifier, @Primary, byTypespring
- 정의 가 의 기초 (생성자/필드/setter 주입) 를 다룬다면, 이 페이지는 같은 타입의 Bean 이 여러 개일 때 어떻게 하나를 골라내는지 의 해결 전략을 다룬다. 기본 알고…
- [Spring] @Conditional, Profile, @ConfigurationPropertiesspring
- 정의 Spring Boot는 조건부 Bean 등록 메커니즘을 제공한다. classpath, property, 다른 Bean 유무 등에 따라 자동 구성을 활성화/비활성화. , 어노…
- [Spring] Bean Scope 심화: Singleton, Prototype, Web Scopespring
- 정의 가 5가지 scope 의 개요 를 다룬다면, 이 페이지는 그 중 함정 (prototype-into-singleton) 과 해결 패턴 (ScopedProxy, ObjectPr…
- [Spring] BeanPostProcessor와 BeanFactoryPostProcessorspring
- 정의 Spring 컨테이너의 2가지 확장점: - BeanFactoryPostProcessor (BFPP): Bean 인스턴스가 만들어지기 전, (메타데이터) 을 수정. 클래스명,…
- [Spring] IoC와 DI: Bean, ApplicationContext, @Autowiredspring
- 정의 IoC (Inversion of Control)는 객체 생성/생명주기 제어를 프레임워크가 가져가는 원칙. DI (Dependency Injection)는 그 구현 방법. S…
- [Spring] Stereotype 어노테이션: @Component, @Service, @Repository, @Controllerspring
- 정의 Stereotype 어노테이션 은 클래스가 Spring 컨테이너의 어떤 역할 (role) 인지를 의미적으로 표시한다. 모두 를 메타 어노테이션 으로 가진 specializa…
이 개념을 다룬 위키 페이지 (5)
- wiki[Spring] @Autowired 해결 전략: @Qualifier, @Primary, byType
- wiki[Spring] BeanPostProcessor와 BeanFactoryPostProcessor
- wiki[Spring] Bean Scope 심화: Singleton, Prototype, Web Scope
- wiki[Spring] IoC와 DI: Bean, ApplicationContext, @Autowired
- wiki[Spring] Stereotype 어노테이션: @Component, @Service, @Repository, @Controller
💬 댓글