Yebali

Spring Bean의 생명주기 본문

Spring

Spring Bean의 생명주기

예발이 2021. 10. 10. 21:59

Bean이란?

Bean이란 Spring IoC 컨테이너가 생성하고 관리하는 자바 객체를 말한다.

흔히 Java에서 new 연산자로 객체를 생성했을 때 만들어지는 객체는 빈이 아니다.

Spring Framework를 사용할 때 Spring Bean을 얻기 위해서는 ApplicationContext.getBean()와 같은 메서드를 통해 Bean을 얻을 수 있다.

 

Bean의 생명주기

Spring에서 관리하는 Bean들은 아래와 같은 생명주기를 가진다.

Spring 시작 -> Spring 컨테이너 생성 -> Spring Bean생성 -> 의존관계 주입 -> 초기화 콜백
-> Bean 사용 -> 소멸 전 콜백 -> 스프링 종료

Spring Bean은 객체 생성과 의존관계 주입이 끝나야 사용할 준비가 완료된다.
따라서 초기화 작업은 의존관계 주입 후 호출된다.

 

Spring은 Bean을 사용 전, 후 Bean에 대해 특정 작업을 하고 싶을 때
의존관계가 주입된 직후, 또는 Bean이 소멸하기 전 시점을 알 수 있도록 '초기화 콜백'과 '소멸 전 콜백'을 제공한다.

 

해당 콜백을 사용하는 방법 3가지를 알아보자.

1. InitializingBean, DisposableBean 인터페이스

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 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 메서드를 구현하여 각각 초기화 콜백, 소멸 전 콜백을 사용할 수 있다.
스프링 전용 인터페이스라 스프링에 의존하며 메서드 이름을 변경할 수 없으며 외부 라이브러리에 적용할 수 없다
지금은 거의 사용하지 않는 방식이다.

2. Bean의 설정 정보에 초기화, 종료 메서드 지정

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;
        }
    }
}
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();
    }
}

@Bean 애너테이션의 'initMethod', 'destroyMethod' 옵션에 초기화 콜백, 소멸 전 콜백에 사용할 메서드를 명시해준다.
명시되는 메서드 명은 TestBean에 있는 메서드 이름이다.

 

명시된 메서드를 통해 초기화 콜백, 소멸 전 콜백을 사용할 수 있으며 메서드 명을 자유롭게 지정할 수 있고 스프링에 의존적이지도 않다.
+ 외부 라이브러리에도 적용할 수 있다.

 

@Bean의 distroyMethod는 추론 기능을 제공하기 때문에 흔히 사용되는 'close()', 'shutdown()'를 메서드 명으로 사용하면 개발자가  'destroyMethod'에 명시하지 않아도 해당 메서드를 소멸 전 콜백에 사용한다.

3. @PostConstruct, @PreDestory

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을 사용하면 된다.

 

참고 : 객채 생성과 초기화는 분리하자.

생성자는 필수 정보(파라미터)를 받고 메로리를 할당해서 객체를 생성하는 일을 한다.
반면에 초기화는 생성된 값들을 활용해서 외부 커넥션을 연결하는 등 무거운 동작을 수행한다.

따라서 생성자가 하는 일과 초기화 작업을 하는 것을 분리하는 것이 유지보수 관점에서 좋다.

'Spring' 카테고리의 다른 글

Spring을 이용한 API개발 - 기본  (0) 2021.10.11
Spring JPA 준영속 엔티티 수정하기  (0) 2021.10.11
Spring JPA Entity 설계 시 주의점  (0) 2021.10.11
Spring JPA 1:N 관계 설정  (0) 2021.10.10
Spring Bean 등록/설정 방법  (0) 2021.10.04