6. 빈 생명주기 콜백
2025. 4. 14. 16:40ㆍSpring/Core
1. 스프링 빈 생명주기 흐름
1. 스프링 컨테이너 생성
2. 컴포넌트 스캔 및 BeanDefinition 등록
3. 객체 생성 및 의존성 주입
4. 초기화 콜백
5. 빈 사용
6. 스프링 컨테이너 종료 시, 소멸 콜백
7. 빈 소멸, 스프링 컨테이너 종료
1) 스프링 컨테이너 생성
new AnnotationConfigApplicationContext(설정정보.class)
2) 컴포넌트 스캔 및 빈 등록(BeanDefinition 등록)
- 설정정보 클래스에 @ComponentScan이 붙어 있으면, 스프링이 클래스패스를 뒤져서 @Component, @Service, @Repository, @Controller 등 스캔 대상이 되는 클래스들을 찾아냄.
- 찾은 클래스들은 BeanDefinition이라는 이름표(설계도) 형태로 스프링 컨테이너의 빈 저장소에 저장됨
이건 아직 객체를 생성한 건 아니고, 클래스의 메타정보를 기반으로 “어떤 클래스를, 어떤 이름으로, 어떤 방식으로 빈으로 만들 건지”에 대한 정의만 저장된 상태.
3) 객체 생성 및 의존성 주입
- 정의된 빈을 실제 객체로 만들고 생성자, setter, field 등에 붙어있는 @Autowired를 보고 의존성을 주입함.
이때, 어떤 방식으로 의존성을 주입하는지에 따라 라이프 사이클이 달라진다! - 생성자로 의존관계 주입하는 경우: 객체의 생성, 의존관계 주입이 동시에 일어남
- setter, field로 의존관계 주입하는 경우: 객체의 생성 -> 의존관계 주입으로 라이프 사이클이 나누어져 있음
@Component
public class OrderService { //1. 생성자 주입
private final MemberRepository memberRepository;
//의존관계가 없으면 애초에 객체 생성 자체가 안 됨.
@Autowired
public OrderService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}
@Component
public class OrderService { //2. setter 주입
private MemberRepository memberRepository;
//일단 객체 생성 되고 -> 이후에 의존관계 자동주입됨
@Autowired
public void setMemberRepository(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}
4) 초기화 콜백
- 객체 생성과 의존성 주입까지 모두 완료되면 이제 객체를 사용할 준비는 됐고, 이후에 초기 셋업을 하는 단계가 초기화 단계이다.
- 객체의 생성과 초기화를 분리하는 것이 권장되는 이유
- 생성자는 필수 정보(파라미터)를 받고, 메모리를 할당해서 객체를 생성하는 책임을 가진다.
- 반면에 초기화에서는 이렇게 생성된 값들을 활용해서 DB연결과 같은 외부 커넥션을 연결하거나, 캐시 초기화, 설정값 로딩과 같은 무거운 동작을 수행한다.
- 따라서 생성자 안에서 무거운 초기화 작업을 함께 하는 것 보다 객체를 생성하는 부분과 초기화 하는 부분을 명확하게 나누는 것이 유지보수 관점에서 좋다.
- 물론 초기화 작업이 내부 값들만 약간 변경하는 정도로 단순한 경우 에는 생성자에서 한번에 다 처리하는게 더 나을 수 있다.
5) 빈 사용
6) 스프링 컨테이너 종료 시, 소멸 콜백
- 스프링은 스프링 컨테이너가 종료되어 빈이 소멸되기 전에 소멸 콜백을 통해 프로그램이 안전하게 종료 될 수 있도록 한다.
- DB 연결 끊기, 쓰레드 종료, 파일 닫기와 같은 뒷정리를 할 수 있는 기회를 줘야 리소스 누수나 장애를 막을 수 있기 때문이다.
7) 빈 소멸, 스프링 컨테이너 종료
2. 스프링의 초기화/소멸 콜백 지원 방법
1) 스프링이 초기화/소멸 콜백을 지원하는 이유
- 스프링 빈의 초기화 작업은 객체를 생성하고, 의존관계 주입이 다 끝난 다음에 호출되어야 하고,
소멸 작업은 애플리케이션이 종료되거나 컨테이너가 내려갈 때 호출되어야 한다. - 그런데 개발자가 초기화/소멸 작업을 직접 타이밍 맞춰 호출하는 건 번거롭고 오류가 나기 쉽다.
- 따라서 스프링은 객체 생성 및 의존성 주입 끝난 딱 그 순간, 컨테이너 종료 직전 딱 그 타이밍, 이 두 순간을 정확히 캐치해서 개발자가 정의한 초기화, 소멸 메서드를 자동으로 콜백해주는 기능을 제공한다.
2) 스프링의 초기화/소멸 콜백 지원 방법 3가지
1. 인터페이스(InitializingBean, DisposableBean)
2. 설정 정보에 초기화/소멸 메서드 지정
3. @PostConstruct, @PreDestroy 애노테이션 지원
코드로 예시
public class NetworkClient {
private String url;
public NetworkClient() {
System.out.println("생성자 호출, url = " + url);
}
public void setUrl(String url) {
this.url = url;
}
}
public class BeanLifeCycleTest {
@Test
public void lifeCycleTest() {
ConfigurableApplicationContext ac =
new AnnotationConfigApplicationContext(LifeCycleConfig.class);
NetworkClient client = ac.getBean(NetworkClient.class);
ac.close(); //스프링 컨테이너 종료, ConfigurableApplicationContext 필요
}
@Configuration
static class LifeCycleConfig {
@Bean //수동 빈 등록
public NetworkClient networkClient() {
NetworkClient networkClient = new NetworkClient(); //객체 생성, 이때는 아무런 값X
networkClient.setUrl("http://hello-spring.dev"); //값 설정
return networkClient;
}
}
}
방법 1. 인터페이스(InitializingBean, DisposableBean)
public class NetworkClient implements InitializingBean, DisposableBean {
...
@Override
public void afterPropertiesSet() throws Exception {
//초기화 작업 수행
}
@Override
public void destroy() throws Exception {
//소멸 전 정리 (clean-up) 작업 수행
}
}
방법 2. 설정 정보에 초기화/소멸 메서드 지정
public class NetworkClient{
...
public void init(){
//초기화 작업 수행
}
public void destroy(){
//소멸 전 정리 (clean-up) 작업 수행
}
}
@Configuration
static class LifeCycleConfig {
@Bean(initMethod = "init", destroyMethod = "close")
public NetworkClient networkClient() {
return new NetworkClient();
}
}
방법 3. @PostConstruct, @PreDestroy 애노테이션 지원
public class NetworkClient{
...
@PostConstruct
public void init(){
//초기화 작업 수행
}
@PreDestroy
public void destroy(){
//소멸 전 정리 (clean-up) 작업 수행
}
}
@Configuration
static class LifeCycleConfig {
public NetworkClient networkClient() {
return new NetworkClient();
}
}
3가지 방법 비교
| 방법 | 설명 | 장점 | 단점 |
| InitializingBean, DisposableBean |
인터페이스 구현 | 명확함 | 스프링 전용, OOP에 불리함, 외부 라이브러리에 적용 못함 |
| 초기화/소멸 메서드 지정 | XML/JavaConfig에서 명시 | 외부 라이브러리에도 가능 | 설정에 의존 |
| @PostConstruct, @PreDestroy |
표준 애노테이션 | 가장 직관적, 많이 쓰고 권장됨 스프링종속X 자바표준임 |
외부 라이브러리에 적용 못함 |
결론
- @PostConstruct, @PreDestroy 애노테이션을 사용하자
- 코드를 고칠 수 없는 외부 라이브러리를 초기화, 종료해야 하면 @Bean의 initMethod, destroyMethod 를 사용하자