빈 생명주기 콜백
- 스프링 빈의 경우, 생성자 주입을 제외하고 이같은 라이프 사이클을 가진다.(객체 생성 -> 의존관계 주입)
- 스프링은 의존관계 주입이 완료되면 스프링 빈에게 콜백 메서드를 통해 초기화 시점을 알려줌.
- 또한 스프링 컨테이너가 종료되기 직전에 소멸 콜백을 줌. 따라서 안전하게 종료 작업을 진행할 수 있음.
- 스프링 빈의 이벤트(생명주기) 사이클는 아래와 같다.
- (스프링 컨테이너 생성 -> 스프링 빈 생성 -> 의존관계 주입 -> 초기화 콜백 -> 사용 -> 소멸 전 콜백 -> 스프링 종료)
- 객체의 생성(메모리 할당)과 초기화(동작 행위)를 분리해서 설계하는 것이 유지보수에 좋다.
InitializingBean, DisposableBean
- 의존관계 설정 후와 스프링 빈 소멸 시 호출되는 함수의 추상화 인터페이스. 이런 것도 있다 정도로 넘어가자.
- 코드가 전용 인터페이스에 의존하고 초기화, 소멸 메서드의 이름을 변경할 수 없는 단점이 있다.
- 또한 코드를 고칠 수 없는 외부 라이브러리에 적용할 수 없다. 이러한 단점들 때문에 거의 사용하지 않는다.
public class NetworkClient implements InitializingBean, DisposableBean {
private String url;
public NetworkClient() {
System.out.println("생성자 호출, url = " + url);
connect();
call("초기화 연결 메시지");
}
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 {
connect();
call("초기화 연결 메시지");
}
// 빈 소멸 시 호출
@Override
public void destroy() throws Exception {
disconnect();
}
}
빈 등록 초기화, 소멸 메서드
- 메서드 이름을 자유롭게 변경 가능하고, 스프링 빈이 스프링 코드에 의존하지 않음.
- 코드가 아니라 설정 정보를 사용하기 때문에, 코드를 고칠 수 없는 외부 라이브러리에도 적용 가능.
- 종료 메서드에는 추론 기능이 있는데, @Bean의 destroyMethod의 기본값이 "(inferred)"로 등록되어 있음. 그래서 "close", "shutdown" 라는 이름의 메서드르 자동으로 호출해준다.(빈으로 등록되어 있는 경우에만).
- 추론 기능을 사용하기 싫으면 destroyMethod = "" 공백으로 해주면 된다.
public class NetworkClient {
private String url;
public NetworkClient() {
System.out.println("생성자 호출, url = " + url);
connect();
call("초기화 연결 메시지");
}
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);
}
public void init() {
System.out.println("NetworkClient.init");
connect();
call("초기화 연결 메시지");
}
public void close() {
System.out.println("NetworkClient.close");
disconnect();
}
}
// -------------------------------------------
public class BeanLifeCycleTest {
@Test
public void lifeCycleTest() {
ConfigurableApplicationContext ac = new AnnotationConfigApplicationContext(LifeCycleConfig.class);
NetworkClient client = ac.getBean(NetworkClient.class);
ac.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;
}
}
}
@PostConstruct, @PreDestroy
- 최신 스프링에서 가장 권장하는 방식. 외부 라이브러리에는 적용하지 못함. 외부 라이브러리에서 초기화, 종료 해야 하면 이전의 @Bean 기능을 사용하자.
- 스프링의 종속적인 기술이 아니라, 스프링이 아닌 다른 컨테이너에서도 잘 동작한다.
- 컴퍼넌트 스캔과도 잘 어울린다.
public class NetworkClient {
private String url;
public NetworkClient() {
System.out.println("생성자 호출, url = " + url);
connect();
call("초기화 연결 메시지");
}
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() {
System.out.println("NetworkClient.init");
connect();
call("초기화 연결 메시지");
}
@PreDestroy
public void close() {
System.out.println("NetworkClient.close");
disconnect();
}
}
// ---------------------------------------------------
public class BeanLifeCycleTest {
@Test
public void lifeCycleTest() {
ConfigurableApplicationContext ac = new AnnotationConfigApplicationContext(LifeCycleConfig.class);
NetworkClient client = ac.getBean(NetworkClient.class);
ac.close();
}
@Configuration
static class LifeCycleConfig {
@Bean
public NetworkClient networkClient() {
NetworkClient networkClient = new NetworkClient();
networkClient.setUrl("http://hello-spring.dev");
return networkClient;
}
}
}