Spring Core Technolohies 1. IoC Container (1) ApplicationContext와 Bean

#Spring#IoC#DI#Bean

2023-04-16 03:05

대표 이미지

스프링 공식 Document를 기반으로 스프링 코어 기술들에 대해 정리하는 시리즈물로써, IoC, DI, AOP 세 가지 개념을 중심으로 간단한 예시와 함께 스프링 컨테이너를 이해하고 코드 작성 방법을 익혀봅니다. 첫 번째 글에서는 스프링의 Inversion of Control (IoC) container인 ApplicationContext와 이 컨테이너에 의해 관리되는 Bean 인스턴스화에 대한 개념을 설명합니다.

개요


필자가 처음 자바 개발을 시작하며 맞닥뜨린 큰 산이라 함은 프레임워크, 스프링이였다. 스프링이 제공하는 다양한 기능들과 그것들의 작동 원리를 파악하는 것은 여간 힘든 일이 아니다. 하지만 자바 백엔드 개발자라면 반드시 알아야하는 것들이기에 마냥 손놓고 있을 수는 없는 노릇이다.

본 포스팅은 스프링 프레임워크를 이해하기 위한 기초로써 Spring Core를 다루는 시리즈 첫 글이며, 스프링을 처음 접하거나 아직은 미숙한 개발자들을 위해 쓰여졌다. Spring Core 모듈 구성과 정의, Spring Core 모듈에서 제공하는 Spring IoC Container와 기반 사상과 객체에 대한 소개로 구성된다.

Framework


구글에 프레임워크에 대해 검색하면 위키 백과에서 다음과 같이 정의된다.

컴퓨터 프로그래밍에서 소프트웨어 프레임워크(software framework)는 복잡한 문제를 해결하거나 서술하는 데 사용되는 기본 개념 구조이다. 간단히 뼈대, 골조(骨組), 프레임워크(framework)라고도 한다.

즉, 프레임워크는 소프트웨어 개발을 위한 뼈대를 제공하는 도구나 라이브러리 모음이라고 할 수 있다.

프레임워크라는 개념이 생기기 이전엔 개발자들은 모든 코드를 처음부터 새로 작성해야했다. 이를테면 알고리즘과 데이터 구조(Stack, Array 등)를 매번 수작업으로 작성하였으며 데이터베이스나 사용자 인터페이스와 같은 부분을 개발하기 위해 각자 입맛에 맞는 라이브러리나 모듈을 가져다쓰는 방식을 사용했다.

라이브러리 등을 사용하는 것만으로도 효율을 올릴 수 있지 않았을까하는 의문이 들텐데, 여기에는 또다른 함정이 있다.

  1. 이것들을 적절히 활용하고 조립하는데에 있어 개발자가 직접 코드를 작성해야 한다. 이는 여전히 많은 시간과 노력을 필요로 하며, 개발자마다 라이브러리나 모듈을 사용하는 방식이 상이함으로 코드의 일관성과 재사용성이 낮아질 수 있다.
  2. 라이브러리나 모듈을 다루는 방법에 대한 이해도가 필요하여 개발자들이 추가적인 학습에 많은 시간을 소비해야 할 수도 있다.
  3. 종속성 문제가 발생할 수 있다. 라이브러리나 모듈의 버전이나 종속성이 변경될 경우, 이를 사용하는 프로그램에서도 문제가 발생할 수 있다.

프레임워크는 이러한 문제들을 해결하기 위해 등장했다.

프레임워크는 개발자들이 자주 사용하는 기능들을 미리 구현하였으며, 어플리케이션의 전체적인 구조를 제공한다. 이를 통해 개발자는 어플리케이션의 기본적인 요소들을 쉽고 빠르게 구현할 수 있게 되었고 상대적으로 많은 시간을 오롯이 애플리케이션의 핵심 비즈니스 로직에 집중할 수 있게 되었다. 프레임워크의 일관된 코드 구조와 패턴은 개발자들이 더욱 일관성 있는 코드를 작성할 수 있게 도와주며, 코드의 가독성과 유지보수성을 높이고 종속성 문제를 해결할 수 있는 방법을 제공한다. 또한, 프레임워크는 보안, 성능, 확장성과 같은 다양한 요소들을 고려하여 구현되어 있기 때문에 개발자들이 이러한 부가적인 요소들을 고려하고 익히며 구현하는데 드는 시간과 노력을 크게 줄여주었다.

결론적으로 개발자들은 프레임워크의 등장과 함께 더욱 효율적으로 애플리케이션을 개발할 수 있게된 것이다.

핵심 개념


본문으로 넘어가기 전 계속해서 등장할 개념들에 대해 간단히 짚고 넘어가자.

IoC

  • Inversion of Control (제어의 역전)
  • 프로그램에서 모듈간의 의존성을 관리하기 위한 설계 원칙
  • 개발자는 프레임워크에 필요한 부품만 개발하며 코드의 호출은 개발자가 제어하는 것이 아닌 프레임워크의 내부에서 결정된 대로 이뤄진다. 즉, 객체의 생명 주기를 프레임워크에서 관리한다.

DI

  • Dependency Injection (의존성 주입)
  • IoC 패턴의 구현 방법 중 하나
  • 스프링에서만 사용되는 것은 아니다.
  • 의존성이 있는 객체를 개발자가 직접 생성하거나 참조하지 않고 컨테이너가 제공하는 DI 기능을 사용해 필요한 객체를 주입받을 수 있다.
  • 스프링에서 빈으로 정의된 객체를 @Autowired로 선언하면 자동으로 이를 주입해줘서 별다른 작업없이 바로 사용할 수 있는 것이 DI를 이용하는 예시이다.
  • DI 컨테이너에는 BeanFactory, ApplicationContext, Guice, Dagger 등이 있다.

Spring Framework의 IoC

  • BeanFactory와 ApplicationContext 인터페이스를 이용해 IoC를 구현한다. 이들은 DI 컨테이너라고 불린다.
  • BeanFactory는 가장 기본적인 IoC 컨테이너이며, ApplicationContext는 BeanFactory를 상속받아 추가적인 기능들을 제공하는 IoC 컨테이너이다.
  • BeanFactory가 프레임워크의 구성과 기본 기능을 제공한다고 하면, ApplicationContext는 용도에 따른 추가적인 기능을 제공한다.
    • ApplicationContext가 제공하는 것들
      • Spring의 AOP 기능과의 통합
      • 메시지 리소스 처리 (for use in internationalization)
      • 이벤트 발행
      • 웹 애플리케이션을 위한 WebApplicationContext와 같은 응용 프로그램 레이어별 Context에서 사용

Bean

  • Spring IoC 컨테이너에 의해 인스턴스화, 조립, 관리되는 객체
  • Bean으로 선언되지 않은 객체는 Spring IoC 컨테이너가 관리하지않는다.
    • Bean이 아닌 Dog Class가 존재한다고 할 때, Dog myDog = new Dog(); 으로 선언된 myDog의 LifeCycle은 개발자가 작성한 코드에 의해 결정된다.
  • Bean들 간의 의존성은 컨테이너에서 사용되는 configuration metadata에 반영된다.

Container


Spring Core 컨테이너는 스프링 프레임워크의 핵심이며, Bean / Core / Context 의 모듈로 나누어져 있고 이는 스프링 프레임워크에서 IoC Container를 구성하는데 사용하는 모듈이다.

ApplicationContext

애플리케이션의 설정 정보를 제공하는 인터페이스로써 아래와 같이 정의되어 있다.

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
		MessageSource, ApplicationEventPublisher, ResourcePatternResolver {

	@Nullable
	String getId();

	String getApplicationName();

	String getDisplayName();

	long getStartupDate();

	@Nullable
	ApplicationContext getParent();

	AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
}

ApplicationContext는 몇가지 interface를 상속함으로써 여러 기능을 제공한다.

  • ListableBeanFactory : 애플리케이션 컴포넌트에 엑세싱하는 빈 팩토리를 제공하는 메소드
  • ResourceLoader : 파일 리소스 로드 기능 (ResourcePatternResolverResourceLoader를 상속받는다.)
  • ApplicationEventPublisher : 등록된 리스너에 이벤트 발행 기능
  • MessageSource : 다국어 지원

상위(부모) 컨텍스트를 상속하나 자식 컨텍스트에서 정의된 내용이 항상 우선시된다. 즉, 어떤 상위 컨텍스트가 전체 웹 애플리케이션에서 사용되어도 각 서블릿마다 독립적인 자식 컨텍스트를 가질 수 있다.

사실상 ApplicationContext에서 가장 주목해야할 핵심 기능은 BeanFactory를 통한 Bean의 인스턴스화이다. BeanFactory는 스프링 프레임워크의 IoC/DI 기능을 구현하는 인터페이스로, 빈 객체를 생성하고 관리하는 기능을 제공한다. ApplicationContext는 이러한 BeanFactory 인터페이스를 상속하여 구현되었기 때문에, 빈 객체의 인스턴스화와 관리를 위해 BeanFactory의 모든 기능을 사용할 수 있다.

그렇다면 Bean을 인스턴스화한다.라는 것이 무슨 의미일까? 🤔 이는 스프링 컨테이너가 설정 파일에 등록된 빈 정보를 바탕으로 빈을 생성하고 런타임 시점에서 빈 객체의 참조를 관리할 수 있도록 스프링 내부에서 관리하는 것을 의미한다.

빈 정보는 XML, Java 어노테이션, Java 코드로 작성되며 애플리케이션을 구성하는 객체와 객체들 간의 상호 종속성을 포함한다. 작성한 애플리케이션 내의 클래스들은 이러한 설정 메타데이터를 기반으로 Spring Container에 의해 인스턴스화되는 것이다.

image.png

Configuration Metadata

객체의 생명 주기와 런타임 내 주입 시기에 대해서는 스프링에 작업을 위임하나 객체를 구성하는 데에 있어서는 개발자가 지시를 내려줘야한다. 간단하게 XML, Java Annotation, Java 기반의 Configuration 작성 방법을 알아보자

1) XML-based configuration

<bean id="myComponent" class="com.example.MyComponent">
    <property name="myService" ref="myService"/>
</bean>
<bean id="myService" class="com.example.MyService"/>
  • 가장 오래전부터 사용해온 방식이다.
  • 어노테이션 기반 설정이 각 클래스 파일 내부에 있어 관리가 어려운 것에 비해, 루트 디렉토리에 분류에 따라 여러 파일로 구성이 가능하여 프로젝트 관리가 쉬운 이점이 있다.
  • 반면에 구성 정보를 검증하기 어렵고 휴먼 에러가 발생할 가능성이 높다. XML 구성 파일을 읽고 파싱하는데 오버헤드가 발생할 수 있다.

2) Annotation-based configuration

@Component
public class MyComponent {
  //..
}
  • 스프링이 제공하는 @Component, @Service, @Repository 등과 같은 어노테이션을 이용해 설정을 정의한다.
  • 코드의 양을 줄이고 직관적인 코드로 작성할 수 있다.

3) Java-based configuration

public class MyComponent {
    //...
}

@Configuration
public class MyConfig {
    @Bean
    public MyComponent myComponent() {
        return new MyComponent();
    }
}
  • @Configuration 어노테이션을 사용하여 Configuration으로 사용할 클래스를 지정하고 내부에서 @Bean 어노테이션을 사용해 빈으로 등록할 클래스를 지정한다.

XML-based configuration vs. Annotation-based configuration vs. Java-based configuration

장점 단점
XML-based 설정 정보를 파일로 관리하기 때문에, 수정과 관리가 쉽다. XML 파일을 작성하는 것이 번거롭고, 오타 등의 실수가 발생할 수 있다.
스프링 컨테이너가 XML 파일을 읽어들이는 시간이 빠르다. XML 파일의 크기가 커지면 가독성이 떨어지고 유지보수가 어렵다.
설정 정보를 한눈에 파악할 수 있다.
Annotation-based 어노테이션을 사용하기 때문에 XML 파일보다 설정 정보가 간결하고 가독성이 좋다. 설정 정보가 흩어져있어 XML 파일보다 관리가 어렵다.
IDE 등의 도구를 이용하여 코드 작성이 용이하다. 애노테이션을 이용하여 설정 정보를 작성하면 코드가 복잡해질 수 있다.
컴파일 과정에서 오류를 확인할 수 있어 안정적이다.
Java-based 설정 정보를 자바 파일로 관리하기 때문에, 가독성이 좋고 수정 및 관리가 용이하다. Annotation 기반 설정보다 코드가 길어지기 때문에, 보다 복잡한 설정을 다루기 어렵다.
XML 파일에 비해 설정 정보의 양이 적어서 가벼운 애플리케이션에서 빠른 초기화 속도를 보장한다. XML 파일과 Annotation 방식보다 학습 곡선이 높아 사용하기 어렵다.
자바 코드에 대한 컴파일 타임 체크가 가능하여, 런타임에서 발생하는 예외를 사전에 방지할 수 있다.

빈 객체를 정의하기 위한 메타데이터를 제공했으니 ApplicationContext는 이를 바탕으로 빈을 만들어 낼것이다.

이 때 알고 넘어가야할 점이 있는데, ApplicationContext가 빈은 생성할 때 위의 metadata를 직독직해하는 것은 아니다. 말장난처럼 들릴 수도 있겠지만 쉽게 이야기하면 빈을 만들 때 사용자가 정의한 configuration을 읽어서 1:1로 대입하듯 바로 빈 객체를 생성하는 것이 아니란 것이다.

이해를 위해 새롭게 등장하는 개념인 BeanDefinition에 대해 먼저 짚고 넘어가자.

BeanDefinition

BeanDefinition은 스프링 컨테이너가 빈 객체를 생성하고, 관리하기 위해 필요한 정보를 담고 있는 객체이다. 빈 객체의 클래스 정보, 생성자 인자, 프로퍼티 값, 스코프 등의 정보가 포함되며 BeanDefinition은 XML 파일이나 자바 코드에서 설정 정보를 작성하여 생성된다. 즉, 우리가 작성한 Bean Configuration metadata 내 정보에 기반하여 객체인 BeanDefinition을 새로 생성하는 것이다.

Configuration metadata는 BeanDefinition을 만드는 데 사용되는 일종의 템플릿 역할을 한다고 말할 수 있다. 결론적으로 ApplicationContext는 BeanDefinition을 중간 매개체로 사용하여 Configuration metadata에 정의된 대로 빈을 생성하고 관리한다.

BeanDefinition이 포함하는 정보

  • 빈으로 정의한 클래스의 이름
  • 컨테이너 내에서의 빈의 동작 방식 (scope, lifecycle callback)
  • 다른 빈에 대한 종속성
  • 새로 생성된 객체에 사용될 다른 configuration 설정들

Bean 인스턴스화


Bean 인스턴스화란 스프링 컨테이너가 설정 파일에 등록된 빈 정보를 바탕으로 빈을 생성하고 런타임 시점에서 빈 객체의 참조를 관리할 수 있도록 스프링 내부에서 관리하는 것을 의미한다.

본격적으로 스프링에서 빈을 인스턴스화하는 방법에 대해 알아보도록 하자. 빈을 인스턴스화 하는 방법은 총 세가지가 있다.

  1. 생성자(Constructor)를 이용한 방법
    • 스프링 컨테이너가 빈을 생성할 때, 해당 빈 객체의 생성자를 호출하여 인스턴스를 생성한다.
    • 대부분의 빈 객체는 생성자를 이용하여 인스턴스화된다.
  2. 정적 팩토리 메소드를 이용한 방법
    • 스프링 컨테이너가 빈을 생성할 때, 해당 빈 객체의 정적 팩토리 메소드를 호출하여 인스턴스를 생성한다.
    • 일반적으로 외부 라이브러리나 다른 프레임워크에서 제공하는 객체를 스프링 빈으로 등록할 때 많이 사용된다.
  3. 인스턴스 팩토리 메소드를 이용한 방법
    • 스프링 컨테이너가 빈을 생성할 때, 해당 빈 객체의 인스턴스 팩토리 메소드를 호출하여 인스턴스를 생성한다.
    • 인스턴스 팩토리 메소드는 빈을 생성할 때 더 복잡한 로직이 필요할 때 사용된다.

1. 생성자를 사용한 인스턴스화

모든 클래스에 대해 사용 가능한 방식이다. 즉, 대상이 되는 클래스는 어떤 인터페이스의 구현체일 필요도 없으며 정해진 방식으로 코딩할 필요도 없다. Bean 객체로 지정되는 것만으로도 충분하다.

일반적으로 많이 하는 착각 중 하나가 빈 객체로 선언된 클래스는 반드시 기본 생성자가 있어야한다는 것이다. 개발자가 클래스에 파라미터가 있는 생성자를 직접 작성한 경우, 해당 클래스에는 기본 생성자가 없을 수 있다. 이럴 경우에도 스프링 IoC 컨테이너에서는 @Autowired 어노테이션을 이용하여 생성자 주입 방식으로 의존성을 주입할 수 있기에 빈 객체로 선언된 클래스에는 기본 생성자가 있어야 한다는 생각은 오류이다. (의존성 주입에 대해서는 추후 기술한다.)

Java-based configuration examples

기본 생성자가 없는 클래스

public class MyComponent {
   private final MyDependency dependency;

   public MyComponent(MyDependency dependency) {
       this.dependency = dependency;
   }
}

- 생성자를 이용한 빈 정의

@Configuration
public class MyConfig {
   @Bean
   public MyComponent myComponent(MyDependency myDependency) {
       return new MyComponent(myDependency);
   }

   @Bean
   public MyDependency myDependency() {
       return new MyDependency();
   }
}

- Setter를 이용한 빈 정의

@Configuration
public class MyConfig {
   @Bean
   public MyComponent myComponent() {
       MyComponent component = new MyComponent();
       component.setDependency(myDependency());
       return component;
   }

   @Bean
   public MyDependency myDependency() {
       return new MyDependency();
   }
}

Annotation-based configuration examples

어노테이션을 이용해서 빈을 정의하는 경우에는 기본 생성자를 사용하거나 @Autowired를 이용해야 한다.

@Component
public class MyComponent {
   private final MyDependency dependency;

   public MyComponent(MyDependency dependency) {
       this.dependency = dependency;
   }
}

@Component어노테이션이 사용된 클래스는 일괄적으로 스캔되어 빈으로 등록되는데 위와 같이 의존성이 있으나 @Autowired 를 이용한 의존성 주입 정의가 없는 경우에는 기본생성자를 호출 시도한다. 따라서 위 코드에는 기본생성자가 없기에 NoSuchBeanDefinitionException이 발생한다.

- 필드에 직접 어노테이션 사용 (Field Injection)

@Component
public class MyComponent {
   @Autowired
   private MyDependency dependency;
}

Field Injection을 사용하는 경우 기본 생성자를 호출하고 나서 dependency 필드에 MyDependency 객체를 주입한다.

- 기본생성자와 Setter 이용 (Setter Injection)

@Component
public class MyComponent {
   private MyDependency dependency;

   public MyComponent() {
   }

   @Autowired
   public void setDependency(MyDependency dependency) {
       this.dependency = dependency;
   }
}

- 생성자에 어노테이션 사용 (Constructor Injection)

@Component
public class MyComponent {
   private final MyDependency dependency;

   @Autowired
   public MyComponent(MyDependency dependency) {
       this.dependency = dependency;
   }
}

2. 정적 팩토리 메소드를 사용한 인스턴스화

생성자 대신에 정적 메소드를 이용하여 객체를 생성하는 방법. 이 방식은 객체 생성을 처리하는 코드를 별도의 클래스 또는 메소드로 분리하여 캡슐화하고, 이를 통해 객체 생성 방식을 유연하게 변경할 수 있는 장점이 있다.

Annotation-based configuration examples

@Configuration
public class MyConfig {
   @Bean
   public MyComponent myComponent() {
       return MyComponent.createInstance();
   }
}

@Component
public class MyComponent {
   private MyComponent() {
       // private constructor
   }
   public static MyComponent createInstance() {
       return new MyComponent();
   }
}

@Component어노테이션에 의해 스프링이 MyComponent를 스캔하여 인스턴스화 하는 것을 막기위해 기본 생성자를 명시적으로 private 선언하였다. 대신 @Configuration이 설정된 클래스에서 다시 빈으로 선언하고 정적 팩토리 메소드를 사용해 인스턴스화하도록 유도한다.

다른 빈에 종속성이 있는 경우에도 문제없다.

@Configuration
public class MyConfig {
   @Bean
   public MyComponent myComponent(MyDependency myDependency) {
       return MyComponent.createInstance(myDependency);
   }
}

@Component
public class MyComponent {
   private final MyDependency dependency;

   private MyComponent() {
       // private constructor
   }

   private MyComponent(MyDependency dependency) {
       this.dependency = dependency;
   }

   public static MyComponent createInstance(MyDependency dependency) {
       return new MyComponent(dependency);
   }
}

사실 어노테이션 기반으로 빈을 정의할 때는 @Component를 이용한 편리함이 큰 이점이라고 생각되는데, 팩토리 메소드를 사용해서 인스턴스화하는 경우에는 그 이점이 사라지고, 단지 “이 클래스는 빈이다”를 알리는 수준으로 어노테이션이 쓰이고 있다. 따라서 이 코드에서는 @Component가 직접적으로 수행하고 있는 것은 없기 때문에 @Component어노테이션을 제거하고 Java-based configuration 으로 작성해도 무관하다.

객체 생성을 담당하는 메소드를 별도의 클래스에 정의하고, 이를 이용하여 객체를 생성하는 방법이다. 이 방식은 정적 팩토리 메소드와 마찬가지로 객체 생성 방식을 유연하게 변경할 수 있는 장점이 있다.

Java-based configuration examples

public class MyComponentFactory {
    public MyComponent createMyComponent(MyDependency dependency) {
        return new MyComponent(dependency);
    }
}

public class MyComponent {
    private MyDependency dependency;
    
    public MyComponent(MyDependency dependency) {
        this.dependency = dependency;
    }
}

@Configuration
public class MyConfig {
    @Bean
    public MyComponentFactory myComponentFactory() {
        return new MyComponentFactory();
    }
    
    @Bean
    public MyComponent myComponent(MyComponentFactory myComponentFactory, MyDependency myDependency) {
        return myComponentFactory.createMyComponent(myDependency);
    }
}

실제로 사용하고자하는 클래스는 MyComponent이나 이를 생성하기 위한 MyComponentFactory 빈을 추가로 정의한다. MyComponentFactory에 인스턴스 메소드로 MyComponent를 생성, 반환하는 메소드를 작성해주었다.

❗️정적 팩토리 메소드를 이용한 인스턴스화 vs. 인스턴스 팩토리 메소드를 이용한 인스턴스화

인스턴스 팩토리 메소드는 객체 생성을 담당하는 메소드가 별도의 클래스에 정의되기 때문에 해당 클래스를 먼저 인스턴스화한 후에 해당 메소드가 호출된다. 반면에 정적 팩토리 메소드는 객체 생성을 담당하는 메소드가 해당 클래스 내에 정의되므로 클래스 이름을 이용하여 해당 메소드를 호출한다.

따라서, 인스턴스 팩토리 메소드는 객체 생성 과정에서 다양한 방식으로 객체를 생성할 수 있도록 유연성을 제공하며, 객체 생성 과정에서 복잡한 로직을 처리할 때 유용하다. 반면에 정적 팩토리 메소드는 객체 생성 과정에서 유연성은 제한되지만, 객체 생성에 필요한 정보를 정적 메소드 인자로 전달할 수 있어서 코드 가독성이 높아지는 장점이 있다.

4. XML-based 팩토리 메소드를 이용한 인스턴스화

위의 예제들을 보면, 팩토리 메소드를 사용해서 인스턴스화하는 경우에 Annotation 기반 혹은 Java 기반으로 코드를 작성할 경우 오히려 코드가 복잡해짐을 느낄 수 있다. 이때는 오히려 XML을 사용할 때 더 깔끔한데, factory-method라는 property key에 사용할 팩토리 메소드의 이름을 추가하기만 하면 된다.

정적 팩토리 메소드를 사용하는 경우

<bean id="clientService"
    class="examples.ClientService"
    factory-method="createInstance"/>
public class ClientService {
    private static ClientService clientService = new ClientService();
    private ClientService() {}

    public static ClientService createInstance() {
        return clientService;
    }
}

인스턴스 팩토리 메소드를 사용하는 경우

<bean id="serviceLocator" class="examples.DefaultServiceLocator">
</bean>

<bean id="clientService"
    factory-bean="serviceLocator"
    factory-method="createClientServiceInstance"/>
public class DefaultServiceLocator {
    private static ClientService clientService = new ClientServiceImpl();

    public ClientService createClientServiceInstance() {
        return clientService;
    }
}

맺음말


이번 포스팅에서는 Spring Core 기술 중 IoC Container와 Bean에 대해 알아보는 시간을 가졌다.

스프링에서는 IoC 원칙을 구현하기 위해 BeanFactory를 상속받는 ApplicationContext를 IoC 컨테이너로 정의한다. 사용자는 XML, Annotation, Java 기반 Configuration으로 원하는 빈 객체를 선언해야하며, 스프링은 해당 정보를 가지는 BeanDefinition 생성한다. 마지막으로 ApplicationContext는 이를 이용해 빈을 생성, 관리한다.

이어지는 포스팅에서는 IoC 패턴의 구현체이자, 빈 객체의 의존성을 해결하는 프로세스인 DI(Dependency Injection)를 해부하고 빈의 라이프 사이클에 대한 이해도를 높여보자.

Ref