4장. 스프링 컨테이너와 스프링 빈

1. 스프링 컨테이너란?

스프링 컨테이너는 ApplicationContext라는 이름으로 존재한다.

ApplicationContext applicationContext =
        new AnnotationConfigApplicationContext(AppConfig.class);

이 한 줄이 정말 강력하다.

이 말의 의미는?

  1. AppConfig를 설정 정보로 사용
  2. AppConfig의 @Bean 메서드들을 읽음
  3. 그 메서드들이 생성하는 객체를 스프링 빈으로 등록
  4. 모든 빈을 스프링이 대신 생성하고 관리(싱글톤 보장)
  5. 필요한 빈들을 서로 연결하여 의존관계 자동 주입

즉, AppConfig에서 하던 역할을 스프링이 자동으로 해주는 것이다.


2. 스프링 컨테이너 생성 과정

스프링 컨테이너 생성 과정은 크게 3단계다.

2-1. 설정 정보 읽기

스프링은 AppConfig.class를 읽고 내부에 있는 @Bean 메서드를 모두 찾는다.

2-2. 스프링 빈 등록

@Bean 있는 메서드들을 분석하고, 리턴 객체를 모두 스프링 빈으로 등록한다.

이때 스프링 빈 이름은 기본적으로 @Bean 메서드 이름이다.

예:

@Bean
public MemberService memberService()

→ 빈 이름: “memberService”

→ 빈 객체: new MemberServiceImpl(…)

2-3. 스프링 빈 의존관계 설정

스프링은 @Bean 메서드 안의 파라미터 혹은 내부 new 코드들을 분석하여

필요한 빈을 찾아 자동으로 주입해준다.

이 과정은 “스프링 DI(의존관계 주입)”이라고 부른다.


3. 스프링 빈 조회하기 (중요)

우리는 스프링 컨테이너에 어떤 빈이 등록되었는지 조회해볼 수 있다.

모든 빈 조회 예시

String[] beanNames = applicationContext.getBeanDefinitionNames();

실행하면 스프링이 생성한 모든 빈이 출력된다.

여기서 두 종류로 나뉜다:

  • ROLE_APPLICATION → 개발자가 등록한 빈 (@Bean, @Component 등)
  • ROLE_INFRASTRUCTURE → 스프링 내부에서 사용하는 빈 (예: 메시지 처리, AOP 설정 등)

원하는 빈을 조회하기

MemberService ms = applicationContext.getBean("memberService", MemberService.class);

타입만으로 조회도 가능:

MemberService instance = applicationContext.getBean(MemberService.class);

빈 이름 또는 타입이 잘못되면?

스프링은 다음 오류를 발생시킨다.

NoSuchBeanDefinitionException: No bean named 'xxx' available


4. 동일한 타입의 빈이 둘 이상일 때

예를 들어 DiscountPolicy의 두 구현체를 스프링에 등록하면?

  • FixDiscountPolicy
  • RateDiscountPolicy

이 둘은 타입이 같기 때문에 다음 코드가 오류를 발생시킨다:

DiscountPolicy dp = ac.getBean(DiscountPolicy.class);

왜?

DiscountPolicy 타입의 빈이 2개라 어떤 걸 줘야 할지 모르기 때문.

이때 해결하는 방법:

1) 빈 이름으로 찾기

ac.getBean("fixDiscountPolicy", DiscountPolicy.class);

2) @Primary 사용

기본 우선순위를 정하는 방법

3) @Qualifier 사용

특정 빈을 지정할 때 사용


5. 스프링 빈의 상속 관계

스프링 빈 조회는 부모 타입으로 조회해도 된다.

예를 들어,

  • MemoryMemberRepository implements MemberRepository

이 구조라면:

MemberRepository repo = ac.getBean(MemberRepository.class);

이렇게 조회하면 구현체가 대신 반환된다.

하지만 구현체가 여러 개면 오류가 생기므로

이 경우 빈 이름이나 Qualifier로 지정해야 한다.


6. BeanFactory vs ApplicationContext

스프링 컨테이너는 사실 두 개의 계층 구조를 가진다.

BeanFactory

  • 스프링 컨테이너의 가장 기본 기능
  • 스프링 빈을 관리하고 조회하는 기능만 담당
  • 지금까지 말해온 “DI 컨테이너”의 API

ApplicationContext

BeanFactory를 상속하며, 다음 기능들을 포함한다:

  • 국제화 메시지 관리
  • 환경 변수
  • 리소스(파일 등) 쉽게 읽기
  • 이벤트 발행 기능
  • AOP

요약하면,

BeanFactory = DI 기능

ApplicationContext = DI + 부가 기능(스프링을 완전한 애플리케이션 프레임워크로 만들어줌)

일반적으로 우리는 ApplicationContext만 사용한다.


7. 다양한 설정 형식 지원

스프링 컨테이너는 설정 정보 형식을 다양하게 지원한다:

  1. 자바 코드 기반 설정 (이 장에서 사용)
  2. XML 설정
  3. 컴포넌트 스캔 (@ComponentScan)
  4. 자바 Config 클래스 + @Bean 조합
  5. 최근에는 코틀린 DSL도 가능

하지만 현재 Spring Boot 시대에서는

자바 기반 설정 + 컴포넌트 스캔 조합을 가장 많이 사용한다.


8. 스프링 빈의 메타 정보: BeanDefinition

BeanDefinition은 스프링이 내부적으로 사용하는 “설계도”다.

  • 어떤 클래스를 기반으로 빈을 만들지
  • 스코프(Scope)는 무엇인지
  • 라이프사이클 콜백은 있는지
  • 어떤 방식으로 빈을 생성하는 설정인지

스프링은 AppConfig, XML, 컴포넌트 스캔 등 다양한 설정을 읽어

모두 BeanDefinition이라는 통일된 메타 정보로 변환한다.

즉,

설정 방법은 달라도 스프링 컨테이너 입장에서는 전부 BeanDefinition으로 통합된다.

그래서 스프링은 설정 방법과 무관하게 동일하게 동작할 수 있다.


4장 핵심 정리

4장은 한 문장으로 요약하면:

우리가 직접 만들던 AppConfig를 스프링에게 맡기고, 빈을 자동 생성·관리하는 환경을 구축하는 단계.

핵심 포인트는 다음과 같다.

  1. ApplicationContext = 스프링 컨테이너
  2. @Bean 메서드를 기반으로 객체를 생성하고 스프링 빈으로 등록
  3. DI(의존관계 주입)를 자동으로 처리
  4. 부모-자식 타입 조회, 이름 기반 조회 등 빈 조회 방법 학습
  5. BeanFactory vs ApplicationContext 차이
  6. 다양한 설정 방법 지원
  7. BeanDefinition으로 내부적으로 통합 관리