애플리케이션 시작 시점에 데이터베이스, 네트워크 소켓 등을 연결하는 작업을 초기화라고 한다.
초기화를 한 뒤에는 애플리케이션 종료 시점에 해당 연결을 모두 끊는 작업이 필요하다.
객체의 초기화는 어떻게 할 수 있을까?
객체(빈)가 생성되는 시점은 해당 객체의 생성자가 호출될 때이다.
그럼 생성자 안에 초기화 코드를 넣으면 될까?
아래 코드를 보자.
public class NetworkClient implements InitializingBean, DisposableBean {
private String server;
public NetworkClient() {
// 초기화
connect();
}
public void setServer(String server) {
this.server = server;
}
public void connect() {
System.out.println("connect to: " + server);
}
public void disconnect() {
System.out.println("close: " + server);
}
}
결과는 【 connect to: null 】 이 출력될 것이다.
객체를 생성한 후에 setter를 사용해야 server
필드에 값이 들어가기 때문이다.
개발자의 의도는 객체 생성 직후에 초기화를 하고 싶은 것인데...
스프링 빈은 다음과 같은 라이프사이클을 가진다.
객체 생성 ➜ 의존관계 주입
(생성자 주입의 경우 예외적으로 생성 & 주입이 동시에 발생)
의존관계 주입이 끝나야 객체를 사용할 수 있다.
개발자는 의존관계 주입이 끝난 직후에 초기화를 하고자 한다.
어떻게 이 시점을 알 수 있을까?
✔︎ 스프링이 알려준다.
스프링 빈의 이벤트 라이프사이클은 다음과 같다.
- 스프링 컨테이너 생성
- 빈 생성
- 의존관계 주입
- 초기화 콜백 메소드
- 사용
- 소멸전 콜백 메소드
- 스프링 종료
콜백 메소드란, 객체의 초기화/종료 시점에 호출되도록 지정된 메소드이다.
스프링은 의존관계 주입이 완료되면 스프링 빈에게 초기화 시점을 알려준다.
그럼 지정된 콜백 메소드를 통해서 초기화를 자동으로 진행한다.
또한, 컨테이너가 종료되기 직전에 빈에게 종료 시점을 알려주고 종료 콜백 메소드가 호출된다.
초기화/종료 시점에 호출될 콜백 메소드는 개발자가 지정할 수 있다.
다음과 같이 3가지 방법이 지원된다.
InitializingBean, DisposableBean은 스프링 전용 인터페이스이다.
현재는 잘 사용되지 않는다!!
사용 방법
public class NetworkClient implements InitializingBean, DisposableBean {
private String server;
public NetworkClient() {
// 생성자에서는 아무것도 안함
}
public void setServer(String server) {
this.server = server;
}
public void connect() {
System.out.println("connect to: " + server);
}
public void disconnect() {
System.out.println("close: " + server);
}
@Override
public void afterPropertiesSet() throws Exception {
// 초기화 콜백 메소드
// 초기화 시점(의존관계 주입 직후)에 자동으로 호출됨
connect();
}
@Override
public void destroy() throws Exception {
// 종료 콜백 메소드
// 종료 시점(컨테이너 종료 직전)에 자동으로 호출됨
disconnect();
}
}
테스트 코드
class NetworkClientTest {
@Configuration
static class BeanLifeCycleConfig {
@Bean
public NetworkClient networkClient() {
NetworkClient networkClient = new NetworkClient();
networkClient.setServer("http://my-spring");
return networkClient;
}
}
@Test
public void lifeCycleTest() {
ConfigurableApplicationContext ac =
new AnnotationConfigApplicationContext(BeanLifeCycleConfig.class);
NetworkClient client = ac.getBean(NetworkClient.class);
ac.close();
}
}
new NetworkClient()
로 객체를 생성한 뒤에, setServer
함수로 필드에 값을 주입하면 networkClient 빈을 사용할 수 있다.
이 시점에 afterPropertiesSet()
메소드가 호출되어 초기화가 자동으로 진행된다.
ac.close()
호출 시, 컨테이너가 종료되면서 destroy()
메소드가 자동으로 호출된다.
설정 정보에
@Bean(initMethod = "init", destroyMethod = "close")
처럼 초기화, 소멸 메소드를 지정할 수 있다.
// NetworkClient 클래스에 추가
public void init() {
connect();
}
public void close() {
disconnect();
}
// 테스트 코드에 추가
@Bean(initMethod = "init", destroyMethod = "close")
public NetworkClient networkClient() {
NetworkClient networkClient = new NetworkClient();
networkClient.setServer("http://my-spring");
return networkClient;
}
라이브러리는 대부분 close
, shutdown
이라는 이름의 종료 메소드를 사용한다.
destroyMethod를 따로 적지 않더라도, 스프링 빈이 종료 메소드가 close
, shutdown
일 것이라 추론하고 자동으로 호출한다.
위의 어노테이션을 적용하면, 콜백 메소드로 지정할 수 있다.
최신 스프링에서 가장 권장하는 방법이다.
// networkClient 클래스 수정
@PostConstruct
public void init(){
connect();
}
@PreDestroy
public void close(){
disconnect();
}
@Bean(initMethod, destroyMethod)
를 사용하자.