Question
- 스프링 빈의 라이프사이클에 대해 설명하시오
- 생성자 부분과 객체 초기화 부분을 분리하는 이유를 설명하시오
- InitializingBean, DisposableBean을 활용하는 것의 단점을 설명하시오
- @Bean initMethod, destoryMethod의 장점을 설명하시오
- @Bean destoryMethod를 지정해주지 않으면 어떻게 동작하는가?
- @PostConstruct, @PreDestory 방식의 장단점을 설명하시오
스프링 빈의 라이프사이클
- 스프링 컨테이너 생성 -> 스프링 빈 생성 -> 의존관계 주입 -> 초기화 콜백 사용 -> 소멸전 콜백 -> 스프링 종료
- 초기화 콜백: 빈이 생성되고, 빈의 의존관계 주입이 완료된 후 호출
- 소멸전 콜백: 빈이 소멸되기 직전에 호출
- 객체의 생성과 초기화의 분리
- 생성자는 필수 정보(파라미터)를 받고, 메모리를 할당해서 객체를 생성하는 책임을 가짐
- 반면, 초기화는 생성된 값을 활용해 외부 커넥션을 연결하는 등 무거운 동작을 수행
- 따라서, 생성자 안에서 초기화 작업까지 모두 하는게 아니라 생성자의 역할과 초기화의 역할을 나누는게 좋음
- 만약 초기화 작업이 매우 간단한 작업이라면 생성자 안에서 하는 것도 무방함
스프링에서 지원하는 3가지 빈 생명주기 콜백
- 인터페이스(InitializingBean, DisposableBean)
- 설정 정보에 초기화 메서드, 종료 메서드 지정
- @PostConstruct, @PreDestroy 애노테이션 지원
인터페이스 InitializingBean, DisposableBean
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();
}
}
- 위와 같은 NetworkClient라는 클래스를 스프링 빈으로 등록해서 사용하고자 할 때 초기화 콜백, 소멸전 콜백을InitializingBean, DisposableBean 인터페이스를 구현해서 적용할 수 있음.
- 앞서 말한 스프링 빈의 라이프 사이클에 따라, 의존관계 주입 이후에 afterPropertiesSet() 함수가 자동으로 호출되고, 빈이 제거되기 직전에 destroy() 함수가 호출됨.
- 이 인터페이스는 스프링 전용 인터페이스라, 스프링 전용 인터페이스에 의존함
- 초기화, 소멸 메서드의 이름을 변경할 수 없음( afterPropertiesSet, destroy)
- implement를 추가해 override를 넣어줘야 하기 때문에 내가 코드를 고칠 수 없는 외부 라이브러리에 적용 불가
- 이 방법은 스프링 매우 초창기에 나온 방법이라 현재는 더 나은 방법들 때문에 거의 사용하지 않음.
빈 등록 초기화, 소멸 메서드 지정(@Bean initMethod, destoryMethod)
@Configuration
static class LifeCycleConfig {
@Bean(initMethod = "init", destroyMethod = "close")
public NetworkClient networkClient() {
NetworkClient networkClient = new NetworkClient();
networkClient.setUrl("http://hello-spring.dev");
return networkClient;
}
}
- 위와 같이 Config 파일에서 빈으로 등록할 때 초기화 콜백, 소멸자 콜백 함수 명을 직접 입력해주면, 해당 콜백 메소드가 해당 시점에 불리도록 설정해줄 수 있음
- 이전 방법과 비교하여 메서드 이름을 자유롭게 설정할 수 있음
- 스프링 빈이 스프링 코드에 의존하지 않음
- Bean을 등록해주는 부분에서 설정해주기 때문에 코드를 고칠 수 없는 외부 라이브러리를 사용하더라도 적용해줄 수 있음
- destroyMethod="" 처럼 지정해주지 않은 경우 기본 값으로 (inferred)로 등록되어 있음.
- 라이브러리는 대부분 소멸자 콜백으로 close, shutdown이라는 이름의 종료 메서드를 사용하므로, destoryMethod를 지정해주지 않으면 close, shutdown 메소드를 자동으로 호출해줌.
@PostConstruct, @PreDestroy
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();
}
}
- 직접 초기화 콜백, 소멸자 콜백 함수를 정의해 주고 @PostConstruct, @PreDestory 어노테이션만 붙여주면 해당 시점에 불림
- 최신 스프링에서 가장 권장하는 방법이며, 매우 편리함
- javax 패키지라 스프링에 종속적인 방법이 아니라 자바 표준임
- 컴포넌트 스캔과 잘 어울림
- 유일한 단점은, 외부 라이브러리에 적용하지 못하므로 이 경우 이전의 @Bean 방법을 활용해야함