스프링 핵심 원리 (4) - 스프링 컨테이너
1. 개요
앞선 글들에서는 순수한 자바 코드로 만든 AppConfig가 DI 컨테이너 역할을 했다. 이제부터는 스프링이 직접 제공하는 컨테이너인 ApplicationContext를 써볼 차례다. 이번 글에서는 이 컨테이너를 어떻게 만드는지, 그리고 만들어지는 동안 내부에서 어떤 순서로 일이 벌어지는지를 정리한다.
2. 스프링 컨테이너 생성
1
2
3
// 스프링 컨테이너 생성
ApplicationContext applicationContext =
new AnnotationConfigApplicationContext(AppConfig.class);
- 스프링에서는
ApplicationContext를 컨테이너라고 부른다. ApplicationContext는 실제 동작하는 클래스가 아니라 인터페이스일 뿐이다.- 컨테이너를 만드는 방법은 한 가지만 있는 게 아니라서, XML 설정으로 만들 수도 있고 애노테이션을 붙인 자바 설정 클래스로 만들 수도 있다.
- 지금까지 써온
AppConfig방식이 바로 후자, 즉 애노테이션 기반 자바 설정으로 컨테이너를 만든 예다.
여기서 AnnotationConfigApplicationContext는 이름 그대로 ApplicationContext를 실제로 구현한 클래스 중 하나다.
참고: 좀 더 정확히 말하면 스프링 컨테이너는
BeanFactory와ApplicationContext로 나눌 수 있는데, 실무에서BeanFactory를 직접 쓸 일은 거의 없어서 보통ApplicationContext를 스프링 컨테이너라고 부른다.
3. 스프링 컨테이너의 생성 과정
3.1 컨테이너를 만들 때 구성 정보 알려주기
컨테이너를 만들 때는 어떤 구성 정보를 쓸지 꼭 함께 알려줘야 한다. new AnnotationConfigApplicationContext(AppConfig.class)에서는 AppConfig.class가 바로 그 구성 정보다.
3.2 스프링 빈 등록
컨테이너는 넘겨받은 이 설정 클래스를 하나씩 살펴보면서, 그 안에 담긴 빈들을 차례로 등록한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@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 MemoryMemberRepository();
}
@Bean
public DiscountPolicy discountPolicy() {
return new RateDiscountPolicy();
}
}
쉽게 말해 @Bean이 붙은 메서드를 하나씩 실행하고, 그 결과로 나온 객체를 컨테이너 안의 빈 저장소에 담아두는 것이다.
| 빈 이름 | 빈 객체 |
|---|---|
| memberService | MemberServiceImpl@x01 |
| orderService | OrderServiceImpl@x02 |
| memberRepository | MemoryMemberRepository@x03 |
| discountPolicy | RateDiscountPolicy@x04 |
빈 이름은 어떻게 정해질까
- 따로 지정하지 않으면 메서드 이름이 그대로 빈 이름이 된다.
@Bean(name="memberService2")처럼 이름을 직접 정해줄 수도 있다.
주의: 빈 이름은 서로 겹치면 안 된다. 이름이 같으면 나중에 등록된 빈이 앞의 빈을 덮어쓰거나, 반대로 무시되거나, 설정 방식에 따라 오류가 날 수 있다.
3.3 빈 사이의 의존관계 연결
빈 등록이 끝나면, 컨테이너는 다시 설정 클래스를 보면서 각 빈이 필요로 하는 다른 빈을 연결해준다. 예를 들어 orderService 빈에는 memberRepository, discountPolicy 빈이 함께 이어져 들어간다.
겉으로 보면 그냥 자바 메서드를 호출한 것처럼 보이지만 실제로는 조금 다른 점이 있는데, 이 부분은 싱글톤 컨테이너에서 자세히 알아본다.
참고: 원래 스프링에서는 빈을 만드는 단계와 의존관계를 연결하는 단계가 개념적으로 따로 나뉘어 있다. 다만 자바 설정 코드에서는 생성자를 호출하는 순간 의존관계 연결까지 함께 처리되다 보니, 두 단계가 한 번에 끝나는 것처럼 보일 뿐이다.
4. 정리
스프링 컨테이너는 구성 정보(AppConfig)를 넣어서 만들며, 그 안에서는 크게 두 단계가 차례로 일어난다. 먼저 설정 클래스에 있는 @Bean 메서드들을 실행해 빈을 저장소에 등록하고, 그다음 각 빈이 필요로 하는 다른 빈을 서로 연결해준다.
빈 이름은 따로 지정하지 않으면 메서드 이름을 따르며, 서로 겹치지 않게 관리해야 한다는 점이 중요하다.