스프링 프레임워크는 스프링 컨테이너를 이용하여 @Bean 혹은 @Component 를 적용한 객체를 빈으로 등록한다.
스프링은 의존관계 주입 완료 후 시점과 스프링 컨테이너가 종료되기 직전 시점에 어떠한 동작(콜백)을 수행할 수 있도록 지원한다.
스프링 싱글톤 빈의 이벤트 라이프 사이클
스프링 컨테이너 생성 → 스프링 빈 생성 → 의존관계 주입 → 초기화 콜백 → 사용 → 소멸 전 콜백 → 스프링 종료
초기화 콜백: 빈이 생성되고, 빈의 의존관계 주입이 완료된 후 호출
소멸전 콜백: 빈이 소멸되기 직전에 호출
스프링 빈 생성주기 콜백 지원
InitializingBean, DisposableBean 인터페이스 상속
public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}
public interface DisposableBean {
void destroy() throws Exception;
}
위처럼 InitializingBean 인터페이스와 DisposableBean 인터페이스는 정말 심플하다. InitializingBean 을 상속 받고 afterPropertiesSet 메서드를 구현하면 의존관계 주입 완료 후 초기화 작업을 한다. DisposableBean 을 상속 받은 후 destroy 메서드를 구현하면 컨테이너가 내려가기 전 싱글톤 빈들이 종료되면서 종료 작업을 수행한다.
이 두 인터페이스에는 단점이 있다. 스프링에서 제공하기 때문에 다른 프레임워크나 순수 자바 환경에서는 사용하기 어렵다. 또한, 인터페이스를 상속받아 해당 메서드를 구현해야 하기 때문에 메서드의 이름을 변경할 수 없다. 지금은 거의 사용하지 않는다.
빈 등록 초기화, 소멸 메서드 지정
//클래스 구현
class BeanLifeCycle {
public void init() {
System.out.println("######################## init ########################");
}
public void close() {
System.out.println("######################## close ########################");
}
}
//빈 등록
@Configuration
static class LifeCycleConfig {
@Bean(initMethod = "init", destroyMethod = "close")
public NetworkClient networkClient() {
NetworkClient networkClient = new NetworkClient();
networkClient.setUrl("http://hello-spring");
return networkClient;
}
}
@Bean을 등록할 때 초기화 메서드, 소멸 메서드를 지정할 수 있다. 메서드 이름을 자유롭게 줄수 있는 장점과 스프링 빈이 스프링 코드에 의존하지 않는 장점이 있다. destroyMetohd 속성에는 디폴트로 (inferred) 라는 값을 갖게 된다. 추론 기능으로 불리며 별도로 메서드 이름을 지정해주지 않는다면 close 혹은 shutdown 으로 된 이름의 메서드를 자동 호출해준다. 스프링 빈을 등록했는데 만약 추론 기능을 사용하고 싶지 않다면 destroyMethod="" 로 지정해줘야 한다.
@PostConstruct, @PreDestory 애노테이선 적용
class BeanLifeCycle {
@PostConstruct
public void init() {
System.out.println("######################## init ########################");
}
@PreDestroy
public void close() {
System.out.println("######################## close ########################");
}
}
해당 방법은 스프링에서 가장 권장하는 방법이다. 애노테이션만 붙이면 되므로 매우 편리하고 두 애노테이션은 스프링에 종속적인 기술이 아니라 자바 표준(JSR-250)이다. 하지만 이 방법은 명시적으로 애노테이션을 적용해주어야 함으로 외부 라이브러리에는 적용하지 못한다는 단점이 있다. 만약 외부 라이브러리를 사용한다면 @Bean으로 등록 후 적용시켜주는게 좋다.
@PostConstruct 주의사항
@PostConstruct는 해당빈의 AOP 적용을 보장하지 않는다. 예를 들면 초기화 콜백을 할 때 @Transactional 을 사용해 데이터를 가공하는 일을 할 경우 트랜잭션이 적용 안될 위험이 있다. 그 이유는 @PostConstruct는 해당 빈 자체만 생성되었다고 가정하고 호출되기 때문에 AOP 프록시가 생성되기 전 호출될 수 있다. 만약 초기화 시점에 AOP 프록시를 적용하고 싶다면 @EventListener 를 사용해 애플리케이션 컨텍스트가 완전히 초기화된 이후 호출을 하거나 초기화 콜백 내부에 트랜잭션 코드를 직접 입력해야 한다.