Spring에서 관리하는 Bean들은 아래와 같은 생명주기를 가진다.
Spring 컨테이너 생성 -> Spring Bean생성 -> 의존관계 주입
-> 초기화 콜백 -> Bean 사용 -> 소멸 전 콜백 -> 스프링종료
Spring Bean은 객체 생성과 의존관계 주입이 끝나야 사용할 준비가 완료된다.
따라서 초기화 작업은 의존관계 주입 후 호출된다.
Spring은 Bean을 사용 전, 후 Bean에 대해 특정 작업을 하고 싶을 때
의존관계가 주입된 직후, 또는 Bean이 소멸하기 전 시점을 알 수 있도록 '초기화 콜백'과 '소멸 전 콜백'을 제공한다.
해당 콜백을 사용하는 방법 3가지를 알아보자.
public class BeanLifeCycleTest {
@Test
public void lifecycle() {
ConfigurableApplicationContext ac = new AnnotationConfigApplicationContext(LifeCycleConfig.class);
TestBean bean = ac.getBean(TestBean.class);
ac.close();
}
@Configuration
static class LifeCycleConfig {
@Bean
public TestBean networkClient() {
TestBean testBean = new TestBean();
return testBean;
}
}
}
TestBean 을 생성하고 ac.close()를 통해 Bean을 소멸시킨다.
public class TestBean implements InitializingBean, DisposableBean {
public TestBean() {
}
// 서비스 시작 시 호출
public void whenServiceStart() {
System.out.println("TestBean.whenServiceStart");
}
//서비스 종료 시 호출
public void whenServiceDone() {
System.out.println("TestBean.whenServiceDone");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("TestBean.afterPropertiesSet");
whenServiceStart();
}
@Override
public void destroy() throws Exception {
System.out.println("TestBean.destroy");
whenServiceDone();
}
}
InitializingBean, DisposableBean인터페이스의 afterPropertiesSet, destroy 메서드를 구현하여 각각 초기화 콜백, 소멸 전 콜백을 사용할 수 있다.
스프링 전용 인터페이스라 스프링에 의존하며 메서드 이름을 변경 할 수 없다.
내가 수정 할 수 없는 외부 라이브러리에는 적용 할 수 없다.
지금은 거의 안쓴다. 쓰지말자...
public class BeanLifeCycleTest {
@Test
public void lifecycle() {
ConfigurableApplicationContext ac = new AnnotationConfigApplicationContext(LifeCycleConfig.class);
TestBean bean = ac.getBean(TestBean.class);
ac.close();
}
@Configuration
static class LifeCycleConfig {
@Bean(initMethod = "init", destroyMethod = "close")
public TestBean networkClient() {
TestBean testBean = new TestBean();
return testBean;
}
}
}
@Bean 애너테이션에 initMethod, destroyMethod를 명시해준다.
메서드 이름은 TestBean에 있는 메서드 이름이다.
public class TestBean {
public TestBean() {
}
// 서비스 시작 시 호출
public void whenServiceStart() {
System.out.println("TestBean.whenServiceStart");
}
//서비스 종료 시 호출
public void whenServiceDone() {
System.out.println("TestBean.whenServiceDone");
}
public void init() {
System.out.println("TestBean.init");
whenServiceStart();
}
public void close() {
System.out.println("TestBean.close");
whenServiceDone();
}
}
TestBean 클래스는 평범해진다.
명시된 메서드를 통해 초기화 콜백, 소멸 전 콜백을 사용 할 수 있다.
메서드 이름을 자유롭게 지정할 수 있고 스프링에 의존적이지도 않다.
고칠 수 없는 외부 라이브러리에도 적용 할 수 있다.
@Bean의 distroyMethod는 추론 기능을 제공한다.
라이브러리 대부분이 'close()', 'shutdown()' 이라는 종료 매서드명을 사용하기 때문에 사용자가 따로 이름을 적지 않으면 알아서 찾아서 소멸 전 콜백을 사용한다.
public class BeanLifeCycleTest {
@Test
public void lifecycle() {
ConfigurableApplicationContext ac = new AnnotationConfigApplicationContext(LifeCycleConfig.class);
TestBean bean = ac.getBean(TestBean.class);
ac.close();
}
@Configuration
static class LifeCycleConfig {
@Bean
public TestBean networkClient() {
TestBean testBean = new TestBean();
return testBean;
}
}
}
public class TestBean {
public TestBean() {
}
// 서비스 시작 시 호출
public void whenServiceStart() {
System.out.println("TestBean.whenServiceStart");
}
//서비스 종료 시 호출
public void whenServiceDone() {
System.out.println("TestBean.whenServiceDone");
}
@PostConstruct
public void init() {
System.out.println("TestBean.init");
whenServiceStart();
}
@PreDestroy
public void close() {
System.out.println("TestBean.close");
whenServiceDone();
}
}
TestBean클래스 내부에 @PostConstruct, @PreDestory 어노테이션을 사용하여 초기화 콜백, 소멸 전 콜백을 사용한다.
스프링에서 가장 권장하는 방법이며, 간단하다.
'javax.annotation'을 사용하기 때문에 스프링에 종속적이지 않은 자바 표준이다.
단, 외부 라이브러리에 적용하지 못한다.
결과적으로 @PostConstruct, @PreDestory을 사용하는 것이 가장 좋다.
코드를 고칠 수 없는 외부 라이브러리를 사용한다면 @Bean의 initMethod, destroyMethod을 사용하면 된다.
3가지 방법 모두 아래와 같은 결과를 얻을 수 있다.
생성자는 필수 정보(파라미터)를 받고 메로리를 할당해서 객체를 생성하는 일을 한다.
반면에 초기화는 생성된 값들을 활용해서 외부 커넥션을 연결하는 등 무거운 동작을 수행한다.
따라서 생성자가 하는 일과 초기화 작업을 하는 것을 분리하는 것이 유지보수 관점에서 좋다.
물론 단순한 작업은 한번에 하는게 나을 수 있다.
해당 내용는 인프런에서 김영한님의 강의를 참고해서 작성하였다.
강의 진짜 좋았다.