빈 생명주기 콜백
어플리케이션 시작 시에 데이터베이스, 네트워크 소켓 등을 연결 하고, 어플리케이션 종료 시에 연결을 종료하기 위해서는
객체의 초기화, 소멸 작업이 필요하다.
스프링은 빈 객체를 생성하고, 의존관계 주입이 다 끝난 뒤에 필요한 데이터를 사용할 수 있는 준비가 완료된다.
따라서 객체의 초기화 작업은 의존관계 주입이 끝난 후 이뤄져야한다.
스프링은 의존관계 주입 후, 초기화 콜백을 호출하여 객체의 초기화 작업을 지원한다.
또한 스프링 빈이 소멸되기 직전에 소멸 콜백을 호출하여 객체의 소멸 작업을 지원한다.
초기화 콜백(초기화 메서드), 소멸 콜백(소멸 메서드)을 빈 생명주기 콜백이라고 부르며,
스프링은 크게 세 가지 방법을 통해 빈 생명주기 콜백을 지원한다.
인터페이스(InitilizingBean, DisposableBean)
아래의 코드는 인터페이스(InitilizingBean, DisposableBean)를 통해 빈 생명주기 콜백을 사용한다.
InitializingBean은 afterPropertiesSet을 통해 객체 초기화를 지원하며,
DisposableBean은 destroy를 통해 객체 소멸을 지원한다.
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 {
connect();
call("초기화 연결 메시지");
}
@Override
public void destroy() throws Exception {
disConnect();
}
}
인터페이스를 활용한 방법은 아래와 같은 단점들 때문에 현재는 잘 사용되지 않는다.
- 스프링 전용 인터페이스들이기 때문에, 스프링 의존적인 코드가 작성된다.
- 초기화, 소멸 메서드의 이름을 변경할 수 없다.
- 개인이 코드를 수정할 수 없는 외부 라이브러리에 적용할 수 없다.
설정 정보 활용
아래의 코드는 스프링 빈 설정 정보에 초기화, 소멸 메서드를 지정하여 빈 생명주기 콜백을 사용하는 방법이다.
@Bean을 통해 스프링 빈을 지정할 때 initMethod, destroyMethod를 지정하여
객체의 초기화, 소멸 시에 해당 메소드를 호출할 수 있다.
// <LifeCycleConfig.java>
@Configuration
static class LifeCycleConfig {
@Bean(initMethod = "init", destroyMethod = "close")
public NetworkClient networkClient() {
NetworkClient networkClient = new NetworkClient();
networkClient.setUrl("http://hello-spring.dev");
return networkClient;
}
}
// <NetworkClient.java>
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);
}
public void init() {
System.out.println("NetworkClient.init");
connect();
call("초기화 연결 메시지");
}
public void close() {
System.out.println("NetworkClient.close");
disConnect();
}
}
애노테이션(@PostConstruct, @PreDestroy)
마지막으로 @PostConstruct, @PreDestroy를 활용한 방법은 가장 간단하며, 자주 사용되는 방법이다.
사용할 초기화 메서드, 소멸 메서드에 각각 @PostConstruct, @PreDestory를 지정해주면 된다.
아래는 예시 코드이다.
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() {
System.out.println("NetworkClient.init");
connect();
call("초기화 연결 메시지");
}
@PreDestroy
public void close() {
System.out.println("NetworkClient.close");
disConnect();
}
}
애노테이션을 활용한 방법은 매우 편리하며, 자바 표준 기술이기 때문에 스프링 종속적이지 않다.
유일한 단점은 외부 라이브러리에 활용하지 못한다는 것인데,
외부 라이브러리를 초기화, 종료해야한다면 설정 정보(initMethod, destroyMethod)를 활용하자.