Spring
빈 생명주기 콜백
S2채닝S2
2025. 2. 4. 16:56
빈 생명주기 콜백
- DBCP(Database Connection Poll)이나, Network Socket 처럼 애플리케이션 시작 시점에 필요한 연결을 미리 해두고, 종료 시점에 연결을 모두 종료하는 작업을 하려면 객체 초기화와 종료 작업이 필요하다.
- 보통은 시작 시점에 객체 초기화를 위한 메서드를 호출하고, 종료 시점에 연결 종료를 위한 메서드를 호출한다.
스프링 빈 이벤트 라이프사이클
스프링 빈은 객체를 생성한 후 의존관계를 주입하고, 사용하는 라이프사이클을 가진다. 객체 생성 및 의존관계 주입까지 마쳐에 필요한 데이터를 사용할 수 있는 준비가 완료되는 것이다. 따라서 초기화 작업은 의존관계 주입이 완료된 후에 호출하게 된다. 이 완료 시점은 콜백 메서드를 통해 알 수 있다.
스프링은 의존관계 주입이 완료되면 스프링 빈에게 콜백 메서드를 통해서 초기화 시점을 알려주는 기능을 제공한다. 또한 스프링은 스프링 컨테이너가 종료되기 직전 소멸 콜백을 준다. 이를 통하여 안전하게 종료 작업을 진행할 수 있다
스프링 컨테이너 생성 → 스프링 빈 생성 → 의존관계 주입 → 초기화 콜백 → 사용 → 소멸 전 콜백 → 스프링 종료
- 초기화 콜백: 스프링 빈 생성 및 의존관계 주입 완료 후 호출
- 소멸 전 콜백: 스프링 빈이 소멸되기 직전 호출
객체 생성과 초기화 분리
생성자는 필수 정보(파라미터)를 받고, 메모리를 할당해서 객체를 생성하는 책임을 가진다. 반면 초기화는 생성된 값들을 활용하여 외부 커넥션 연결 등 무거운 동작을 수행한다. 따라서 생성자 안에서 무거운 초기화 작업을 하는 것 보다는 **객체를 생성하는 부분과 초기화하는 부분을 명확하게 나누는 것이 유지보수 관점에서 (훨씬) 좋다.
스프링 빈 생명주기 콜백
스프링은 크게 3가지 방법으로 빈 생명주기 콜백을 지원한다
- 인터페이스 상속(InitializingBean, DisposableBean)
InitializingBean.afterPropertiesSet()
메서드로 초기화 지원DisposableBean.destroy()
메서드로 소멸 지원- 인터페이스 상속 방식은 스프링 전용 인터페이스이며, 초기화 및 소멸 메서드를 상속받아야하기 때문에 메서드 이름을 변경할 수 없고, (내가 코드를 고칠 수 없는) 외부 라이브러리에 적용할 수 없다는 단점이 있다. 이 방법은 초창기에 사용되었고, 현재는 거의 사용하지 않는다.
- 설정 정보에 초기화 메서드, 종료 메서드 지정
- 설정 정보(Confuguration 클래스)에
@Bean(initMethod = "초기화 메서드명", destroyMethod = "소멸 메서드명"
과 같이 초기화 및 소멸 메서드를 메서드명을 기준으로 설정할 수 있다. destroyMethod
는 기본값이(inferred)
(추론)으로, 메서드 명이close
또는shutdown
인 메서드를 자동으로 호출해준다.
- 설정 정보(Confuguration 클래스)에
- @PostConstruct, @PreDestroy 애노테이션
- 생성 및 종료 메서드에 애노테이션을 붙여 사용한다.
- 최신 스프링에서 가장 권장하는 방법이며, 애노테이션 하나만 붙이면 되므로 매우 편리하다.
- 자바 표준 패키지이기 때문에 스프링 종속적이지 않고, 스프링이 아닌 다른 컨테이너에서도 동작한다.
- 외부 라이브러리에는 적용하지 못하기 때문에 외부 라이브러리 적용 시에는
@Bean
의 기능을 사용하면 된다.
@PostConstruct, @PreDestroy 코드
public class NetworkClient {
private String url;
public NetworkClient() {
System.out.println("생성자 호출, url = " + url);
}
public void setUrl(String url){
this.url = url;
}
// 서비스 시작 시 호출
public void connect(){
System.out.println("connect: " + url);
}
public void call(String message){
System.out.println("call: " + url + " message = " + message);
}
// 서비스 종료시 호출
public void disconnect(){
System.out.println("close " + url);
}
// 의존관계 주입이 끝나면 호출
@PostConstruct // 최신 스프링 권장
public void init() throws Exception {
// 기존에 생성자에 포함되었던 객체 초기화 부분을 가져온다. 생성과 초기화는 분리하는 것이 유지보수 측면에서 좋음.
System.out.println("NetworkClient.init");
connect();
call("초기화 연결 메시지");
}
// 객체 소멸 시 호출
@PreDestroy // 최신 스프링 권장
public void close() throws Exception {
System.out.println("NetworkClient.close");
disconnect();
}
정리
- @PostConstruct, @PreDestroy 애노테이션을 사용하자.
- 코드를 고칠 수 없는 외부 라이브러리의 초기화, 종료 시에는 @Bean의 initMethod, destroyMethod를 사용하자.