오늘은 월요일이니 간단히.

필자의 지인 중 N사에 면접에 나온 질문 중 하나다. 나도 솔직히 가물가물해서 다시 알아보기로 했다. 예전에 살짝 글로만 읽어서 그런지 까먹어서 다시 글로 남긴다. ApplicationContext 인터페이스는 Spring을 사용한다면 누구나 잘 알려져 있는 그런 인터페이스이다. Spring에서도 아주 핵심적인 인터페이스라고도 불릴 수 있다.(필자생각)

ApplicationContext 를 역할을 간단히 설명하자면 오브젝트 생성, 관계설정, 만들어지는 방식, 자동생성, 후처리등 여러가지 일들을 한다. 아주 많은 일을 하니 이것은 따로 살펴보도록하자. 또 한 BeanFactory를 상속받고 있다.

그럼 ApplicationContextBeanFactory 차이점은 뭘까? 실제 차이점을 검색해보면 대부분 이런글들이 많다. ApplicationContext는 Pre-loading을 하고 즉, 즉시 인스턴스를 만들고 BeanFactory는 lazy-loading을 하여 실제 요청 받는 시점에서 인스턴스를 만든다고 한다. 정리하자면 두 인터페이스의 차이점은 인스턴스화 시점이 다르다는 것이다.

헌데 조금 이상하다. 어차피 ApplicationContextBeanFactory를 상속받고 있지 않는가? BeanFactory를 사용하고 ApplicationContext의 구현체를 사용하면 Pre-loading이 아닌거 아닌가? 즉 이런코드를 말한다.

BeanFactory beanFactory = new AnnotationConfigApplicationContext(Config.class);
final Context bean = beanFactory.getBean(Context.class);

위와 같이 사용하면 즉시 인스턴스를 만든다. 구현체는 ApplicationContext이지만 실제 사용하는 getBean 메서드는 BeanFactory 인터페이스의 메서드이다.

그럼 다시 코드를 보자.

BeanFactory xmlBeanFactory = new XmlBeanFactory(new ClassPathResource("application.xml"));
System.out.println("before getBean");
final Context bean = xmlBeanFactory.getBean(Context.class);

public class Context implements InitializingBean {

  public String hello(){
    return "hello";
  }

  @Override
  public void afterPropertiesSet() throws Exception {
    System.out.println("init");
  }
}

위의 코드는 XmlBeanFactory 사용해서 BeanFactory를 생성해보자. 그리고 application.xml에는 다음과 같이 빈으로 등록하자.

<bean class="me.wonwoo.Context"/>

위와 같이 빈으로 등록하면 어떤일이 발생할까? 실행시켜보자. 그럼 다음과 같은 로그가 발생한다.

before getBean
//중간생략 
init
22:41:46.804 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanFactory - Finished creating instance of bean 'me.wonwoo.Context#0'

XmlBeanFactory는 인스턴스를 먼저 만들지 않고 호출시에 인스턴스를 만드는 것을 알 수 있다. 먼저 만들어졌다면 init이 먼저 출력 됐을 것이다. 이제 다음과 같이 변경해보자.

BeanFactory classPathXmlApplicationContext =
  new ClassPathXmlApplicationContext(new String[]{"application.xml"});
System.out.println("before getBean");
final Context bean = classPathXmlApplicationContext.getBean(Context.class);

XmlBeanFactory를 ClassPathXmlApplicationContext 변경하여 다시 코드를 실행하면 다음과 같은 로그가 출력된다.

init
//중간 생략
before getBean
22:44:24.978 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'me.wonwoo.Context#0'

이번에는 init이 먼저 출력되었다. 인스턴스를 먼저 생성한 것이다. 어차피 똑같은 BeanFactory를 사용하고 구현체만 변경하였는데 다르게 동작한다.

또 어떻게 보면 맞는말 같기도 하다. BeanFactory가 붙은 클래스들은 lazy이고 ApplicationContext 구현체들은 모두 pre loading이니 틀린말은 아닌 것 같다. 그렇다고 아주 맞는말도 아닌거 같다.

실제 Spring 문서를 찾아봤는데 위와 같은 언급은 딱히 없는 거 같고 이런내용만 있다.
대충 간단히 정리하자면 다음과 같다.
딱히 특별한 이유가 없다면 BeanFactory 보다는 ApplicationContext를 써라. 여기서 특별한이란 메모리 소비가 중요하거나 리소스가 제한된 장치들을 말한다. 일반적인 엔터프라이즈 어플리케이션은 ApplicationContext를 사용하는 게 낫다. 그 이유는 BeanPostProcessor 확장 포인트를 사용 할 수도 있고, 트랜잭션과 AOP와 같은 상당한 양의 지원을 받을 수 있다고 하는데 맞는건지는 문서를 보면 되겠다.

Feature BeanFactory ApplicationContext
Bean instantiation/wiring Yes Yes
Automatic BeanPostProcessor registration No Yes
Automatic BeanFactoryPostProcessor registration No Yes
Convenient MessageSource access (for i18n) No Yes
ApplicationEvent publication No Yes

위와 같은 차이점이 있다고 한다. ApplicationContext 인터페이스가 좀 더 확장된 인터페이스라고 생각하면 될 듯 싶다. 그치만 아직까지 lazy loading과 pre loading은 의문이다. 아닌거 같으면서도 맞는거 같고.. 구현체에 따라 달라지는거는 맞는데.. 그렇다고 딱 BeanFactory 가 lazy loading이다 라고 하기엔 아닌거 같고.. 이건 좀 더 생각을 해보자.