1. 스프링 컨테이너란?
스프링 컨테이너는 ApplicationContext라는 이름으로 존재한다.
ApplicationContext applicationContext =
new AnnotationConfigApplicationContext(AppConfig.class);
이 한 줄이 정말 강력하다.
이 말의 의미는?
- AppConfig를 설정 정보로 사용
- AppConfig의 @Bean 메서드들을 읽음
- 그 메서드들이 생성하는 객체를 스프링 빈으로 등록
- 모든 빈을 스프링이 대신 생성하고 관리(싱글톤 보장)
- 필요한 빈들을 서로 연결하여 의존관계 자동 주입
즉, 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. 다양한 설정 형식 지원
스프링 컨테이너는 설정 정보 형식을 다양하게 지원한다:
- 자바 코드 기반 설정 (이 장에서 사용)
- XML 설정
- 컴포넌트 스캔 (@ComponentScan)
- 자바 Config 클래스 + @Bean 조합
- 최근에는 코틀린 DSL도 가능
하지만 현재 Spring Boot 시대에서는
자바 기반 설정 + 컴포넌트 스캔 조합을 가장 많이 사용한다.
8. 스프링 빈의 메타 정보: BeanDefinition
BeanDefinition은 스프링이 내부적으로 사용하는 “설계도”다.
- 어떤 클래스를 기반으로 빈을 만들지
- 스코프(Scope)는 무엇인지
- 라이프사이클 콜백은 있는지
- 어떤 방식으로 빈을 생성하는 설정인지
스프링은 AppConfig, XML, 컴포넌트 스캔 등 다양한 설정을 읽어
모두 BeanDefinition이라는 통일된 메타 정보로 변환한다.
즉,
설정 방법은 달라도 스프링 컨테이너 입장에서는 전부 BeanDefinition으로 통합된다.
그래서 스프링은 설정 방법과 무관하게 동일하게 동작할 수 있다.
4장 핵심 정리
4장은 한 문장으로 요약하면:
우리가 직접 만들던 AppConfig를 스프링에게 맡기고, 빈을 자동 생성·관리하는 환경을 구축하는 단계.
핵심 포인트는 다음과 같다.
- ApplicationContext = 스프링 컨테이너
- @Bean 메서드를 기반으로 객체를 생성하고 스프링 빈으로 등록
- DI(의존관계 주입)를 자동으로 처리
- 부모-자식 타입 조회, 이름 기반 조회 등 빈 조회 방법 학습
- BeanFactory vs ApplicationContext 차이
- 다양한 설정 방법 지원
- BeanDefinition으로 내부적으로 통합 관리