본문 바로가기

JAVA/Spring

IoC 컨테이너와 DI - 토비의 스프링

IoC 컨테이너와 DI

스프링 애플리케이션에서는 오브젝트의 생성과 관계설정, 사용 제거 등의 작업을 애플리케이션 코드 대신 독립된 컨테이너가 담당한다.
컨테이너가 코드 대신 오브젝트에 대한 제어권을 갖고 있다고 해서 IoC라고 부른다.

스프링 컨테이너 = IoC 컨테이너

IoC를 담당하는 컨테이너를 빈 팩토리 또는 애플리케이션 컨텍스트라고 부른다.

스프링의 IoC 컨테이너는 일반적으로 애플리케이션 컨텍스트를 말한다.

  • BeanFactory 인터페이스
  • ApplicatioinContext 인터페이스

빈 컨테이너

StaticApplicationContext ac = new StaticApplicationContext();

여기에 IoC 컨테이너로서 동작하려면? 클래스와 설정메타정보가 필요하다.

 

IoC 컨테이너의 가장 기초적인 역할은 오브젝트를 생성하고 이를 관리하는것이다.

애플리케이션 컨텍스트는 BeanDefinition으로 만들어진 메타정보를 담은 오브젝트를 사용해 IoC와 DI 작업을 수행한다.

BeanDefinition으로 정의되는 스프링의 설정 메타정보의 내용을 표현하는것이 있다면 무엇이든 사용가능하다.

스프링 IoC 컨테이너는 각 빈에 대한 정보를 담은 설정 메타정보를 읽어들인 뒤에 이를 참고해서 빈 오브젝트를 생성하고 프로퍼티나 생성자를 통해 의존 오브젝트를 주입해주는 DI 작업을 수행한다.

스프링 애플리케이션이란 POJO클래스와 설정 메타정보를 이용해 IoC 컨테이너가 만들어주는 오브젝트의 조합이라고 할수 있다.

 

Hello 클래스 빈 등록

StaticApplicationContext ac = new StaticApplicationContext();
ac.registerSingleton("hello1", Hello.class); //싱글톤 빈으로 컨테이너에 등록.

Hello hello1 = ac.getBean("hello1", Hello.class);
assertThat(hello1, is(notNullValue()));

BeanDefinition을 이용한 빈 등록

BeanDefinition helloDef = new RootBeanDefinition(Hello.class);
helloDef.getPropertyValues().addPropertyValue("name", "String");

ac.registerBeanDefinition("hello2", helloDef);

Hello hello2 = ac.getBean("hello2", Hello.class);
assertThat(hello2.sayHello(), is("Hello Spring"));

assertThat(hello1, is(not(hello2)));

assertThat(ac.getBeanFactory().getBeanDefinitionCount(), is(2));
@Test
public void registerBeanWithDependency() {
    StaticApplicationContext ac = new StaticApplicationContext();

    ac.registerBeanDefinition("printer", new RootBeanDefinition(StringPrinter.class));

    BeanDefinition helloDef = new RootBeanDefinition(Hello.class);
    helloDef.getPropertyValues().addPropertyValue("name", "Spring");
    helloDef.getPropertyValues().addPropertyValue("printer", new RuntimeBeanReference("printer"));

    ac.registerBeanDefinition("Hello", helloDef);

    Hello hello = ac.getBean("hello", Hello.class);
    hello.print();

    assertThat(ac.getBean("printer").toString(), is("Hello Spring"));
}

IoC 컨테이너가 클래스와 설정 메타 정보를 이용해 애플리케이션 런타임 오브젝트를 만들어내는 방법.

 
ApplicatioinContext 구현 클래스 종류
  1. StaticApplicationContext
  • 코드를 통해 빈 메타정보를 등록하기 위해 사용.
  • 스프링 기능에 대한 학습 테스트를 만들때 제외하면 사용되지 않음.
  1. GenericApplicationContext
  • 가장 일반적인 애플리케이션 컨텍스트 구현 클래스.
  • 컨테이너의 주요 기능을 DI를 통해 확장할수 있도록 설계됨.
  • XML파일과 같은 외부 리소스에 있는 빈 설정 메타정보를 리더를 통해 읽어들여서 메타정보로 전환해서 사용함.
@Test
public void genericApplicationContext() {
    GenericApplicationContext ac = new GenericApplicationContext();

    XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(ac);
    reader.loadBeanDefinitions("springbook/learnigtest/spring/ioc/genericApplicationContext.xml");

    ac.refresh();

    Hello hello = ac.getBean("hello", Hello.class);
    hello.print();

    assertThat(ac.getBean("printer").toString, is("Hello Spring"));
}    

스프링 IoC 컨테이너가 사용할수 있는 BeanDefinition 오브젝트로 변환만 할수 있다면 설정 메타 정보는 어떤 포맷으로 만들어져도 된다.
XML파일, 자바소스 코드 애너테이션, 자바클래스 3가지 방식으로 빈 설정 메타 정보를 작성할수 있다.

 

  1. GenericXmlApplicationContext
  • 두 개의 클래스가 결합된 GenericXmlApplicationContext 클래스.
  • XML파일을 읽어들이고 refresh()를 통해 초기화하는 것까지.
GenericApplicationContext ac = 
    new GenericXmlApplicationContext("springbook/learnigtest/spring/ioc/genericApplicationContext.xml")

Hello hello = ac.getBean("hello", Hello.class);

 

  1. WebApplicationContext
  • 가장 많이 사용되는 애플리케이션 컨텍스트.

  • ApplicatioinContext를 확장한 인터페이스.

  • 웹 환경에서 사용할때 필요한 기능이 추가된 애플리케이션 컨텍스트.

  • XML 설정 파일을 사용하도록 만들어진 XmlWebApplicationContext

  • 애너테이션을 이용한 설정 리소스만 사용한다면 AnnotationConfigWebApplicationContext

IoC 컨테이너의 역할은 초기에 빈 오브젝트를 생성하고 DI 한 후에 최초로 애플리케이션 기동할 빈 하나를 제공해주는것까지다.
테스트나, 독립형 애플리케이션말고 웹 애플리케이션은 동작하는 방식이 근본적으로 다르다.

독립 자바 프로그램은 자바 VM에게 main() 메서드를 가진 클래스를 시작시켜 달라고 요청할수 있다.

그러나 웹에서는 main() 메서드를 호출할 방법이 없다. 사용자도 여럿이며 동시에 웹 애플리케이션을 사용한다.
웹 환경에서는 main() 메서드 대신 서블릿 컨테이너가 브라우저로부터 오는 HTTP 요청을 받아서 해당 요청에 매핑되어 있는 서블릿을 실행해주는 방식으로 동작한다.
서블릿이 일종의 main() 메서드와 같은 역할을 하는 셈이다.

main() 메서드 역할을 하는 서블릿을 만들어주고 미리 애플리케이션 컨텍스트를 생성해 둔 다음, 요청이 서블릿이 들어올때마다 getBean()으로 필요한 빈을 가져와 정해진 메서드를 실행해주면된다.

 

웹 환경에서 스프링 애플리케이션 기동하는 방식

클라이언트 --요청---> 서블릿컨테이너(웹 애플리케이션) --생성------> WebApplicationContext ---참조-----> 설정메타정보
                                                                   ^ 조회          | 생성
                                                                   |               V                                     
                                                                서블릿  -호출-> POJO 빈 오브젝트

서블릿 컨테이너는 브라우저와 같은 클라이언트로부터 들어오는 요청을 받아서 서블릿을 동작시켜주는 일을 맡는다.
서블릿은 웹 애플리케이션이 시작될때 미리 만들어둔 웹 애플리케이션 컨텍스트에게 빈 오브젝트로 구성된 애플리케이션의 기동역할을 해줄 빈을 요청해서 받아둔다.
그리고 미리 지정된 메서드를 호출함으로써 스프링 컨테이너가 DI방식으로 구성해둔 애플리케이션의 기능이 시작되는 것이다.

스프링은 이런 웹 환경에서 애플리케이션 컨텍스트를 생성하고 설정 메타 정보로 초기화해주고, 클라이언트로부터 들어오는 요청마다 적절한 빈을 찾아서 이를 실행해주는 기능을 가진 DispatcherServlet이라는 이름의 서블릿을 제공한다.

스프링이 제공해주는 서블릿은 web.xml에 등록하는 것만으로 웹 환경에서 스프링 컨테이너가 만들어지고 애플리케이션을 실행하는데 필요한 대부분의 준비는 끝이다.

웹 애플리케이션에서 만들어지는 스프링 IoC 컨테이너는 WebApplicationContext 인터페이스를 구현한 것임을 기억하자.

WebApplicationContext 특징은 자신이 만들어지고 동작하는 환경인 웹 모듈에 대한 정보에 접근할수 있다.
이를 이용해 웹 환경으로부터 필요한 정보를 가져오거나, 웹 환경에 스프링 컨테이너 자신을 노출할수 있다.
컨테이너가 웹 환경에 노출되면 같은 웹 모듈에 들어있는 스프링 빈이 아닌 일반 오브젝트와 연동될수 있다.