들어가며

  • 이전 글에서 IoC의 개념과 IoC 컨테이너(또는 DI 컨테이너)에 대해서 알아보았다.
  • 스프링 프레임워크는 자체적인 DI 컨테이너를 가지고 있으며 런타임 시점에서 스프링 빈(Bean)으로 등록된 모든 객체들을 가지고 시작한다.
  • 이번 포스트에서는 스프링 컨테이너가 생성되는 과정과 이를 코드로 어떻게 표현하는지에 대해 알아본다.

 

 

스프링 컨테이너의 생성 방법

  • 이전에 보았던 AppConfig를 생각해보자.
public class AppConfig{
    public MemberService memberService(){
        return new MemberServiceImpl(memberRepository());
    }

    public OrderService orderService(){
        return new OrderServiceImpl(memberRepository(), discountPolicy());
    }
    
    public MemberRepository memberRepository(){
        return new MemoryMemberService()
    }
    
    public DiscountPolicy discountPolicy(){
        return new FixDiscountPolicy();
    }
}
  • 스프링 프레임워크에서 이제 이 AppConfig 클래스를 이용하여 스프링 컨테이너를 만들 것이다. 하지만 지금 코드 그대로는 스프링이 인식할 수 없고 다음과 같은 몇가지 어노테이션(Annotation)을 사용하여 인식하도록 할 것이다.
@Configuration
public class AppConfig{

    @Bean
    public MemberService memberService(){
        return new MemberServiceImpl(memberRepository());
    }

    @Bean
    public OrderService orderService(){
        return new OrderServiceImpl(memberRepository(), discountPolicy());
    }
    
    @Bean
    public MemberRepository memberRepository(){
        return new MemoryMemberService()
    }
    
    @Bean
    public DiscountPolicy discountPolicy(){
        return new FixDiscountPolicy();
    }
}
  • @Bean 어노테이션은 해당 메서드에서 반환되는 객체를 스프링 빈(Spring Bean)으로 등록하겠다는 의미로 넣는 어노테이션이다.
    • 이때 빈(Bean)은 스프링에서 생성하고, 관리하는 객체(Object)를 의미한다.
  • 이후에 이 클래스파일이 스프링 컨테이너를 만드는데 사용된다는 의미의 @Configuration을 달면 스프링 컨테이너에서 이 클래스파일을 사용할 준비가 되었다.
  • AppConfig를 사용하여 스프링 컨테이너를 생성하는 방법은 다음과 같은 코드를 사용하여 수행할 수 있다.
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
  • 이때  ApplicationContext는 인터페이스로, 구현체로 AnnotationConfigApplicationContext를 사용한다. 이 구현체는 어노테이션으로 설정된 파일을 기반으로 스프링 컨테이너를 만드는 구현체이다.
    • 어노테이션으로 만들 수도 있으나 XML, 또는 다른 형식을 통하여도 스프링 컨테이너를 만드는 설정 클래스파일을 만들 수 있다. 실무에서는 대부분 어노테이션 기법을 사용한다.

 

 

스프링 컨테이너의 생성 과정

  • 스프링 컨테이너를 처음 생성하면 컨테이너 내부는 비어있다. 컨테이너는 <빈 이름, 빈 객체>로 이루어진 저장소가 존재하며 key로 빈 이름의 String을, value로 해당 객체를 가지고 있게 된다.
  • 스프링 컨테이너에 우리가 아까 만들었던 AppConfig.class를 지정해주면 다음과 같은 일이 일어난다.

  • 먼저 스프링 컨테이너에서 각 메서드의 이름들을 빈 이름에 등록한다.(이때 @Bean으로 등록되어 있어야 한다)
  • 이후에 이 메서드들을 모두 실행한다. 각 메서드들은 AppConfig를 보면 알겠지만 객체들을 반환하게 되어 있다. 스프링 컨테이너는 이 메서드들을 모두 실행한 뒤에 얻는 빈 객체들을 들고 있게 된다.
  • 만약 어떠한 메서드를 실행하여 객체를 받는데 이 객체가 생성되는데 다른 객체가 필요하다면 스프링 컨테이너에서 객체를 찾아 넣어준다. 
  • 위의 과정이 끝난 후에 스프링 컨테이너는 다음과 같은 그림이 된다.

  • 이 이후에 스프링 컨테이너는 자동적으로 의존관계 주입이 필요한 곳에 의존관계 주입을 수행한다. 그렇다. 의존관계 주입(DI) 과정은 스프링 컨테이너가 생성되고 난 이후에 수행된다.
    • 조금 특수한 경우로는 Constructor Injection라고 하는 생성자 주입이 있는데 생성자 주입은 말 그대로 객체가 생성되는 생성자에서 주입을 받는다.
    • 그런데 스프링 컨테이너에서 각 메서드들이 모두 한번씩 실행된다고 하였기 떄문에 이 과정에서 자동적으로 생성자에 의해 DI가 수행된다.
  • Setter Injection, Field Injection, Method Injection이 컨테이너가 생성되고 난 이후에 수행되며 각 과정들의 세부사항은 이후에 다루도록 한다.

 

 

정리

  • 스프링 컨테이너에서 생성하고 관리하는 객체들을 스프링 빈(Bean)이라고 한다.
  • 스프링 컨테이너는 처음에는 비어있으며 Bean이름을 key로, Bean 객체를 value로 하는 저장소를 가지고 있다. 모든 메서드를 스프링 컨테이너가 실행함으로서 각 메서드는 Bean 객체를 생성하여 반환하고 스프링 컨테이너는 이 객체들을 value로 하여 관리를 시작한다.
  • 이후에 DI가 필요한 장소에 스프링 컨테이너가 개입하여 자동으로 DI를 수행해준다.

 

  • 이 다음으로는 각 빈들을 조회하는 방법과 문제점, 해결 방법에 대해 알아본다.
복사했습니다!