스프링 빈은 생성부터 종료까지 일정한 생명주기를 가진다.
스프링 컨테이너 생성 -> 스프링빈 객체 생성 -> 의존 관계 주입 -> 기능 동작 -> 스프링 빈 종료 -> 프로그램 종료
스프링 빈으로 등록된 객체를 정상 사용하려면 의존 관계까지 완벽하게 주입 완료된 상태여야 한다.
만약 의존 관계 주입이 완료되지 않은 상태에서 의존 관계 주입 대상을 사용한 로직을 수행한다면 NULL 값이 뜰 수 밖에 없다. 또한 스프링 빈으로 등록된 객체에서 네트워크 소켓 연결이나 DB 연결을 사용한다면 어플리케이션 종료 시점에 커넥션을 잘 끊어줘야 자원의 낭비나 오류를 막을 수 있다.
이를 해결하려면 우리는 스프링 빈의 의존관계 주입 완료 시점과 스프링빈 종료 시점을 파악할 수 있어야 한다. 스프링에서는 이 시점들을 알려주는 기능을 제공한다. 3가지의 방법을 순서대로 알아보자.
InitializingBean, DisposableBean 인터페이스 사용
제일 처음 등장한 방법으로 인터페이스를 사용하는 방법이 있다. 내가 스프링 빈으로 등록하려는 클래스에 implements로 위의 두 인터페이스를 추가해주면 된다.
public class NetWorkClientBlog implements InitializingBean, DisposableBean {
@Override
public void destroy() throws Exception {
}
@Override
public void afterPropertiesSet() throws Exception {
}
IntializingBean을 구현하면 afterPropertiesSet 메서드를 override한다. 이 메서드는 스프링 빈 객체가 생성되고 의존관계가 모두 주입된 이후에 바로 호출된다.
DisposableBean을 구현하면 destroy 메서드를 override한다. 이 메서드는 스프링 빈 객체가 종료될때 먼저 호출된다.
인터페이스를 구현하는 방법은 몇가지 문제점이 있다.
- 스프링 종속적이기 때문에 스프링에만 의존한 개발을 하게 된다.
- 외부 라이브러리에는 사용할 수 없으므로 유연성이 떨어진다.
- 매번 인터페이스를 implements 하고 새로운 메서드들을 override 해야 한다.
초기에 나온 방법이기도 하고 다른 좋은 방법들이 많이 등장했기 때문에 이 방법은 이제 사용하지 않는것이 좋다.
initMethod, destroyMethod를 사용한 방법
따로 메서드를 override 하지 않고 기존의 스프링 빈 내부에 존재하는 메서드를 가지고 초기화, 종료하는 방법이 있다.
@Bean(initMethod = "init", destroyMethod = "close")
public NetWorkClientBlog netWorkClient()
{
NetWorkClientBlog netWorkClient = new NetWorkClientBlog();
netWorkClient.setUrl("http://devjem.tistory.com");
return netWorkClient;
}
@Bean 어노테이션 내부에 initMethod와 destroyMethod를 추가한다. 각각 스프링 빈 객체 내부에 있는 메서드 명을 뒤에 명시해주면 된다. 이 방법을 사용하면 불필요한 implements와 메서드 overriding을 최소화 할 수 있다.
또한 destroyMethod일 경우 default 값이 (inferred)로 되어 있다. 이 의미는 흔히 종료의 의미로 사용되는 close나 shotdown으로 메서드 명이 설정되어 있다면 자동으로 종료 메서드로 인식 후 소멸시점에 호출해준다는 것이다.
실제 외부 라이브러리들도 종료 메서드는 close라는 이름을 많이 사용하기에 외부 라이브러리에 썼을때의 조합도 좋다. 이 방식도 좋지만 가장 최근에 나온 어노테이션 기반의 방식이 가장 많이 사용되고 스프링 진영에서도 추천하는 방식이다.
@PostConstruct, @PreDestroy
이름에서도 쉽게 기능을 유추할 수 있는 어노테이션 기반 방식이다.
public class NetWorkClientBlog {
@PostConstruct
public void init() {
}
@PreDestroy
public void close() {
}
스프링 빈 내부의 메서드에 두 에노테이션을 붙여주기만 하면 정상 작동한다. 가장 편한 방식이지만 유일한 단점은 외부 라이브러리에 사용이 불가하다. 그러므로 대부분의 경우 어노테이션 기반 방식을 사용하고, 외부 라이브러리에 사용이 필요한 경우에만 initMethod, destroyMethod 방식을 사용하면 된다.
학생으로서 공부하는 상황에서 빈 생명주기를 직접 활용할 기회는 아직 만나보지 못한것 같다. 예전에 React를 공부하면서 생명 주기에 따라 적절한 메서드들을 추가한 경험이 있다. 복잡한 실무 환경이 되면 스프링에서도 생명주기에 따른 세세한 작업이 필요할 것이라 생각한다.