스프링 빈은 객체 생성 및 DI 주입이 끝난 후 부터 사용할 수 있다.
싱글톤 빈의 경우, 아래와 같은 Lifecycle을 가진다.
개발자가 빈을 사용할 수 있는 것은 '5'번에서 뿐이다.
위 Lifecycle에서, 개발자는 콜백 함수들에 대해 접근하여 빈의 Lifecycle을 커스터마이징할 수 있다.
SOLID 중 S. Single responsibility 원칙에 의해 빈의 생성과 초기화는 분리하여 구현하는 것이 좋다. (보통 생성은 가볍고 초기화는 무거운 작업이므로, 분리하는게 유지보수에 좋다.)
스프링에서는 아래 3가지 방식을 통해 빈 생명주기 콜백을 지원한다,
public class NetworkClient implements InitializingBean, DisposableBean {
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);
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("NetworkClient.afterpropertiesSet");
connect();
call("초기화 연결 메시지");
}
@Override
public void destroy() throws Exception {
System.out.println("NetworkClient.destroy");
disconnect();
}
}
위와 같이, InitializingBean 인터페이스를 상속받아 afterPropertieSet() 메소드를 오버라이드하여 초기화 콜백 함수를 구현할 수 있다.
또한 DisposableBean 인터페이스를 상속받고, destroy() 메소드를 오버라이드해 소멸 전 콜백함수를 구현할 수 있다.
하지만, 해당 방식의 경우 아래 3가지 단점이 있다.
최근에는 단점들로 인해 해당 방식은 사용하지 않는다.
class NetworkClient {
void init(){}
void close(){}
}
@Configuration
static class LifeCycleConfig {
@Bean(initMethod = "init", destroyMethod = "close")
public NetworkClient networkClient() {
NetworkClient networkClient = new NetworkClient();
networkClient.setUrl("http://hello-spring.dev");
return networkClient;
}
}
위와 같이, 클래스 내부에 초기화/종료 메소드를 구현해놓고 @Bean(initMethod = "init", destroyMethod = "close") 처럼 Bean Annotation에 추가 아규먼트들을 설정해주는 방식으로 콜백 메소드들을 사용할 수 있다.
해당 방식의 장점은 아래와 같다.
@Bean(destroyMethod)에는 아주 특별한 기능이 있다. 별도의 종료 메소드를 지정해주지 않더라도, 'close', 'shutdown' 이름의 메소드들을 자동으로 호출해준다.
실제로, destroyMethod의 스프링 코드를 까보면 아래와 같다.//Bean.java의 destroyMethod 선언부 String destroyMethod() default AbstractBeanDefinition.INFER_METHOD; //AbstractBeanDefinition.java의 INFER_METHOD 선언부 public static final String INFER_METHOD = "(inferred)";
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@PostConstruct
public void init() throws Exception {
System.out.println("NetworkClient.afterpropertiesSet");
connect();
call("초기화 연결 메시지");
}
@PreDestroy
public void close() throws Exception {
System.out.println("NetworkClient.destroy");
disconnect();
}
단순 어노테이션을 통해 콜백 함수들을 커스터마이징해준다.
해당 방식의 장점은 아래와 같다.
하지만 아래와 같은 단점이 있다.
코드 수정이 불가능한 외부 라이브러리에는 @Bean(initMethod="XX", destroyMethod="XX") 방식을 사용하자.
그 외의 경우엔 @PostConstruct, @PreDestroy를 사용하자.