1. 빈 생명주기 콜백의 필요성
빈 생명주기 콜백은 스프링 애플리케이션에서 빈(Bean)의 초기화와 소멸 과정을 관리하는 중요한 기능입니다.
이러한 콜백이 필요한 이유는 다음과 같습니다.
- 자원 관리 및 초기화: 빈 생명주기 콜백을 활용하여 빈이 생성된 후에 초기화 로직을 수행하거나, 빈이 소멸되기 전에 리소스를 정리하는 등의 작업을 수행할 수 있습니다.
- 의존성 주입 이후 로직 실행: 빈이 생성된 후에 의존성 주입(Dependency Injection)이 완료된 시점에서 특정 로직을 실행하고자 할 때 빈 생명주기 콜백이 유용합니다.
- 외부 리소스 관리: 외부 리소스를 사용하는 빈의 경우, 초기화 시점에 리소스를 할당하고 소멸 시점에 해당 리소스를 정리하는 작업을 수행할 수 있습니다.
스프링 빈의 간단한 라이프사이클은 (객체생성 => 의존관계 주입)입니다. (생성자 의존관계 주입은 예외)
객체를 생성하고 의존관계 주입이 완료되어야 필요한 데이터를 사용할 수 있는 준비가 됩니다.
따라서 초기화 작업은 의존관계 주입까지 모두 완료가 되고 난 후에 호출해야 합니다.
그 시점을 어떻게 알까요?
스프링에서 다양한 기능을 제공합니다.
스프링 빈의 이벤트 라이프사이클은 다음과 같습니다.
스프링 컨테이너 생성 => 스프링 빈 생성 => 의존 관계 주입 => 초기화 콜백 => 사용 => 소멸전 콜백 => 스프링 종료
여기서 눈여겨 보아야 할 것이
초기화 콜백은 빈이 생성되고 빈의 의존 관계가 주입이 완료된 후에 호출 되며
소멸전 콜백은 빈이 소멸되기 직전에 호출된다는 것입니다.
****참고
객체의 생성과 동시에 초기화를 하면 안될까? 생각이 들 수 도 있습니다.
하지만 이는 좋은 방법이 아닙니다.
객체의 생성과 초기화를 분리하는 것이 좋습니다.
생성자는 필수 정보만 파라미터로 받아서 메모리를 할당하는 객체 생성 책임이 있습니다.
반면 초기화는 이렇게 생성된 데이터들을 이용해 외부 커넥션을 연결하는 등의 무거운 동작을 수행합니다.
따라서 생성자 안에서 무거운 초기화 작업을 함께 하는 것보다는
객체의 생성과 초기화 부분을 명확하게 나누는 것이 유지보수 관점에서 좋습니다.
물론 초기화 작업이 내부 값들을 약간만 변경하는 식의 단순한 경우라면 생성자에서 한번에 처리하는 것이
효율적인 경우도 있습니다.
****참고
이 글은 싱글톤 빈에 관한 내용입니다.
싱글톤 빈들은 스프링 컨테이너가 종료될 때 함께 종료되기 때문에
스프링 컨터이너가 종료되기 직전에 소멸전 콜백이 일어납니다.
하지만 싱글톤처럼 컨테이너의 시작과 종료까지 생존하지 않는 빈들도 있습니다.
생명주기가 짧은 빈들은 컨테이너와 무관하게 해당 빈이 종료되기 직전에 소멸전 콜백이 일어납니다.
이 내용은 다른 글에서 다루도록 하겠습니다.
2. 빈 생명주기 콜백 소개
스프링에서는 빈(Bean)의 생명주기를 관리하기 위해 다양한 방법을 제공합니다.
빈 생명주기란 빈이 생성되고 초기화되는 과정부터 소멸될 때까지의 전체 과정을 의미합니다.
다음의 세 가지 주요한 방법으로 빈 생명주기 콜백을 지원합니다.
- 인터페이스(InitializingBean, DisposableBean)
- 설정 정보에 초기화 메서드(initMethod), 종료 메서드 지정(destroyMethod)
- @PostConstruct, @PreDestroy 애노테이션 지원
결론부터 말씀드리면 1번 방법은 옛날 방식이라 요즘엔 거의 사용되지 않습니다.
3번 방식을 사용하면 됩니다.
3번 방식이 안되는 경우에는 2번 방식을 사용하시면 됩니다.
코드를 고칠 수 없는 외부 라이브러리를 초기화, 종료해야하는 경우 등 입니다.
3. 세 가지 빈 생명주기 콜백 방법
3-1. 인터페이스를 활용한 방법 (옛날 방식)
- InitializingBean, DisposableBean 인터페이스 활용
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
public class MyBean implements InitializingBean, DisposableBean {
@Override
public void afterPropertiesSet() throws Exception {
// 빈 초기화 로직
}
@Override
public void destroy() throws Exception {
// 빈 소멸 로직
}
}
위 코드에서 보듯이 InitializingBean, DisposableBean 인터페이스를 implements 하여
각각의 afterPropertiesSet, destroy 메서드를 override 해서 사용하면 됩니다.
따라서 메서드 이름은 바꿀 수 없습니다.
3-2. 설정 정보에 초기화 및 소멸 메서드 지정
- initMethod, destroyMethod 속성 활용
@Configuration
public class Config {
@Bean(initMethod = "init", destroyMethod = "close")
public MyBean myBean() {
MyBean myBean = new MyBean();
myBean.setUrl("http://hello-spring");
return networkClient;
}
}
public class MyBean {
public void init() {
// 빈 초기화 로직
}
public void close() {
// 빈 소멸 로직
}
}
위 코드에서 보듯이 이 방법은 설정 정보에 초기화, 소멸 메서드를 지정해서 사용합니다.
메서드 이름이 자유롭고
스프링 빈이 스프링 코드에 의존하지 않습니다.
코드가 아니라 설정 정보를 사용하기 때문에
코드를 고칠 수 없는 외부 라이브러리에도 초기화, 종료 메서드를 적용 할 수 있습니다.
여기서 신기한 기능이 있는데요
종료 메서드를 지정하는 destroyMethod에 추론 기능이 있습니다.
라이브러리는 대부분 종료 메서드를 close , shutdown이라는 이름으로 사용합니다.
@Bean의 destroyMethod 는 직접 들어가보시면 알겠지만 기본값이 (inferred) (추론)으로 등록되어 있습니다.
이 추론 기능은 close , shutdown이라는 이름의 메서드를 자동으로 호출해줍니다.
말 그대로 종료 메서드를 추론하는 것입니다.
따라서 직접 스프링 빈으로 등록하게 되면 종료 메서드를 따로 적어주지 않아도 동작을 하게 됩니다.
추론 기능이 싫다면 (종료하기 싫다면 ㅋㅋ) destroyMethod="" 이렇게 빈 공백을 지정하면 됩니다.
3. 애노테이션을 활용한 방법 (권장 방식)
- @PostConstruct, @PreDestroy 애노테이션 활용
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
public class MyBean {
@PostConstruct
public void init() {
// 빈 초기화 로직
}
@PreDestroy
public void close() {
// 빈 소멸 로직
}
}
최신 스프링에서도 가장 권장하는 방법입니다.
가장 편리하기도 하구요.
애노테이션 이름을 잘 지어놨죠?construct 이후에, destroy 이전에 사용하시면 됩니다.
import된 패키지를 잘 보면 javax.annotation.PostConstruct 입니다.
스프링에 종속적인 기술이 아니라 JSR-250 라는 자바 표준인 것입니다.
따라서 스프링이 아닌 다른 컨테이너에서도 동작합니다.
컴포넌트 스캔과 잘 어울립니다.
4. 마무리
위 세 가지 방법 중에서도 최신의 스프링에서는 애노테이션을 활용한 방법을 권장합니다.
- 인터페이스 활용 (옛날 방식): 현재는 사용을 지양하는 방식으로 간주됩니다.
- 설정 정보에 메서드 지정: 외부 라이브러리 등 코드를 수정할 수 없는 경우에만 사용하는 것이 좋습니다.
- 애노테이션 활용 (권장): @PostConstruct와 @PreDestroy를 사용하여 간편하게 빈의 초기화와 소멸을 처리할 수 있습니다.
스프링에서는 가능한 애노테이션을 활용하여 간결하고 명시적인 방식으로
빈 생명주기 콜백을 구현하는 것을 권장합니다.
이를 통해 코드의 가독성을 높이고 관리를 용이하게 할 수 있습니다.
<김영한 선생님의 스프링 핵심원리 기본편 예제 및 설명이 들어갔습니다.>
'Spring(boot)' 카테고리의 다른 글
Springboot-Gradle 프로젝트 build, out 폴더 (1) | 2024.02.06 |
---|---|
Logging (SLF4J) 사용하기 (1) | 2024.02.02 |
스프링의 웹 스코프(Web Scope) 이해하기: Request Scope (0) | 2024.01.19 |
스프링의 빈 스코프(bean scope) 이해하기 (0) | 2024.01.18 |
SOLID: 좋은 객체 지향 설계의 5가지 원칙 (0) | 2024.01.05 |