빈 생명주기(Bean LifeCyle)

일반적인 싱글톤 타입의 스프링 빈의 생명주기는 다음과 같다

스프링 컨테이너 생성 → 스프링 빈 생성 → 의존관계 주입 → 초기화 콜백 → 사용 → 소멸 전 콜백 → 스프링 종료

  • 스프링 빈 인스턴스가 생성되기 전에 빈 정의 자체를 재정의할 수도 있고
  • 초기화 콜백, 소멸전 콜백을 정의할 수 있는 다양한 방법도 있고
  • 컨텍스트 내의 빈 스코프와 생성 소멸시점을 설정할 수도 있다

BeanFactoryPostProcessor & BeanPostProcessor

Spring은 기본적으로 객체로 만들 빈 설정정보(BeanDefinition)를 만들어둔 다음에 이를 바탕으로 객체를 생성한다. 이때 빈 설정정보(BeanDefinition)를 불러오고 조작하는 것이 빈팩토리 후처리기(BeanFactoryPostProcessor)이며, 모든 빈들이 만들어진 직후에 객체의 내용이나 객체 자체를 변경하기 위한 것이 빈 후처리기(BeanPostProcessor) 이다.

BeanFactoryPostProcessor 구현체를 빈으로 등록해 주어야 하는 경우는 대표적으로 @Value 를 이용하는 경우가 있다

BeanFactoryPostProcessor

  • 빈팩토리 후처리기(BeanFactoryPostPostProcessor)의 구현체들은 빈 정보를 불러와서 객체로 만들어지기 전에 빈 설정정보 자체를 조작하기 위해 사용된다
  • 빈의 정의 자체를 재정의하거나 수정하기 위해 사용됨
  • 빈 객체들이 생성되기 이전에 후처리기의 동작이 필요한 경우 후처리기가 먼저 생성되어 있어야한다 ⇒ 빈을 등록하는 메소드에 static을 붙혀 컨테이너의 라이프사이클 초기에 다른 빈들보다 먼저 등록되도록 한다.
  • 다른 빈보다 먼저 생성되기 때문에 다른 빈들을 호출해서 사용할 수 없다.
  • postProcessBeanFactoy(ConfigurableListableBeanFactory beanFactory) 메서드를 구현해서 사용한다
    
    public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
            stream(beanFactory.getBeanDefinitionNames())
                    .map(beanFactory::getBeanDefinition)
                    .filter(beanDefinition -> beanClassNameContains(beanDefinition, "module01.question13.beans"))
                    .map(BeanDefinition::getBeanClassName)
                    .forEach(System.out::println);
        }
    
        private boolean beanClassNameContains(BeanDefinition beanDefinition, String subString) {
            return beanDefinition.getBeanClassName() != null && beanDefinition.getBeanClassName().contains(subString);
        }
    }
    빈팩토리 후처리기 예시 ⇒ 특정 이름의 빈을 필터링 하는 후처리기

BeanPostProcessor

  • BeanPostProcessor 인스턴스는 bean 인스턴스들에 대해 작동하며 초기화 전후에 개입하는 메소드 postProcessBeforeInitialization(), postProcessAfterInitialization() 두가지를 제공한다
  • postProcessBeforeInitialization() : 초기화 이전 전처리
  • postProcessAfterInitialization() : 초기화 이후 전처리
  • static을 붙혀 컨테이너의 라이프사이클 초기에 다른 빈들보다 먼저 등록되도록 한다.

Bean Initialization & Destroy Method

Initialization Method : 빈 초기화 시점에 프로퍼티 및 의존성 주입에 대한 조작을 위한 메서드

Destory Method : 애플리케이션이 종료되고 빈이 소멸되기 전에 실행되는 메서드

Initialization Method 생성방법

  • 빈 내부의 초기화 메서드로 설정할 메서드에 @PostConstruct 어노테이션을 붙혀준다
  • InitializingBean 인터페이스 구현 (InitializingBean::afterPropertiesSet)
  • 빈 선언시 초기화 메서드 지정(클래스 내부의 메서드명) @Bean(initMethod=””)

Destroy Method 생성방법

  • 빈 내부의 초기화 메서드로 설정할 메서드에 @PreDestroy 어노테이션을 붙혀준다
  • DisposableBean인터페이스 구현 (Disposable::destroy)
  • 빈 선언시 초기화 메서드 지정(클래스 내부의 메서드명) @Bean(destroyMethod=””)

beanPostProcessor, Initialization & Destroy Method 호출 시점

public class Runner {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfiguration.class);
        context.registerShutdownHook();
    }
}
public class CustomBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessBeforeInitialization on " + bean.getClass().getSimpleName());
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessAfterInitialization on " + bean.getClass().getSimpleName());
        return bean;
    }
}
@ComponentScan
public class ApplicationConfiguration {
    @Bean
    public static CustomBeanPostProcessor customBeanPostProcessor() {
        return new CustomBeanPostProcessor();
    }
    @Bean(initMethod = "initMethod", destroyMethod = "destroyMethod")
    public SpringBean1 springBean1() {
        return new SpringBean1();
    }
}
public class SpringBean1 implements InitializingBean, DisposableBean {

    @PostConstruct
    public void postConstruct() {
        System.out.println(getClass().getSimpleName() + "::postConstruct");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println(getClass().getSimpleName() + "::afterPropertiesSet");
    }

    public void initMethod() {
        System.out.println(getClass().getSimpleName() + "::initMethod");
    }

    @PreDestroy
    public void preDestroy() {
        System.out.println(getClass().getSimpleName() + "::preDestroy");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println(getClass().getSimpleName() + "::destroy");
    }

    public void destroyMethod() {
        System.out.println(getClass().getSimpleName() + "::destroyMethod");
    }
}

Runner 실행결과

...
postProcessBeforeInitialization on SpringBean1
SpringBean1::postConstruct
SpringBean1::afterPropertiesSet
SpringBean1::initMethod
postProcessAfterInitialization on SpringBean1
SpringBean1::preDestroy
SpringBean1::destroy
SpringBean1::destroyMethod

BeanScope & Loading Timing

빈 스코프(Bean Scope)

스프링 빈은 기본적으로 스프링 컨테이너가 생성될때 같이 생성되면서 컨테이너가 종료될때까지 유지되는 구조이다. 하지만 이는 스프링 빈의 기본이 싱글톤(Singleton)스코프로 생성되기 때문이다. 이때 스코프란 말 그대로 빈이 존재할 수 있는 범위 를 뜻한다.

@Component, @Bean 어노테이션과 함께 사용

ScopeDescription
Singleton(Default)컨테이너 내 단 하나의 인스턴스만 존재, 컨테이너 종료시 소멸Eager Loading Default
Prototype호출이 있을때 마다 생성되고 호출이 종료되면 소멸Lazy Loading Dafault
RequestHttp 호출이 있을때 마다 인스턴스가 생성되고 response 이후 소멸Lazy Loading Dafault
SessionHTTP 세션마다 하나의 빈을 생성 세션 종료시 소멸
ApplicationServeltContext라이프사이클 동안 한개의 빈만 사용
Websocketwebsocket 라이프사이클 안에서 한개의 빈만 사용

빈 로딩 시점(Bean Loading Timing)

어떤 이유로 특정 bean 은 늦게 초기화되기를 원한다면 다음 방법으로 빈의 로딩시점을 조정할 수 있다.

  • @ComponentScan의 attribute 중 lazyInit 값을 true로 수정(default ⇒ false)
    @ComponentScan(lazyInit = true)
    public class ApplicationConfiguration {
        @Bean
    		...
    }
  • @Component, @Bean 선언과 함께 @Lazy 를 붙혀 지연로딩으로 설정
    @Component
    @Lazy
    public class SpringBean {...}
    
    @ComponentScan
    public class ApplicationConfiguration {
        @Bean
    		@Lazy
    		...
    }

0개의 댓글