1. 스프링 컨테이너 생성 과정
1-1. 스프링 컨테이너 생성
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
스프링 컨테이너
= ApplicationContext
ApplicationContext
는 인터페이스
- 스프링 컨테이너는 XML기반으로도 만들 수 있고, 어노테이션 기반의 자바 설정 클래스로 만들 수 있음
- 현재, AppConfig.class를 구성 정보로 지정
1-2. 스프링 빈 등록
- 빈 이름 = 메서드 이름
- 빈 이름 직접 부여 가능 : @Bean(name="memberService2")
1-3. 스프링 빈 의존관계 설정
- 설정 정보를 참고해서 의존 관계를 주입(DI)
- 싱글톤 컨테이너로, 단순히 자바 코드를 호출하는 것과 차이가 있음
- 동적인 객체 인스턴스 의존관계를 스프링이 연결해줌 -> 참조값 연결해줌 = 의존관계 단순히 주입!
2. 컨테이너에 등록된 빈 조회
2-1. 기본
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
@Test
@DisplayName("빈 이름으로 조회")
void findBeanByName() {
MemberSerivce memberService = ac.getBean("memberService", MemberSerivce.class);
Assertions.assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}
@Test
@DisplayName("모든 빈 조회")
void findAllBean() {
String[] beanDefinitionNames = ac.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
Object bean = ac.getBean(beanDefinitionName);
System.out.println("name=" + beanDefinitionName + " object=" + bean);
}
@Test
@DisplayName("빈 이름 없이 타입으로만 조회")
void findBeanByType() {
MemberSerivce memberService = ac.getBean(MemberSerivce.class);
Assertions.assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}
@Test
@DisplayName("구체 타입으로 조회")
void findBeanByName2() {
MemberSerivce memberService = ac.getBean("memberService", MemberServiceImpl.class);
Assertions.assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}
@Test
@DisplayName("빈 이름으로 조회X")
void findBeanByNameX() {
org.junit.jupiter.api.Assertions.assertThrows(NoSuchBeanDefinitionException.class,
() -> ac.getBean("xxxx", MemberSerivce.class));
}
ac.getBeanDefinitionNames()
: 스프링에 등록된 모든 빈 이름을 조회
ac.getBean((빈이름), 타입)
: 빈 이름으로 빈 객체(인스턴스)를 조회
- 조회 대상 스프링 빈이 없으면 예외 발생
NoSuchBeanDefinitionException: No bean named 'xxxxx' available
2-2. 동일한 타입 둘 이상
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(ApplicationContextSameBeanFindTest.SameBeanConfig.class);
@Test
@DisplayName("타입으로 조회 시 같은 타입이 둘 이상있으면, 중복오류 발생")
void findBeanByTypeDuplicate() {
Assertions.assertThrows(NoUniqueBeanDefinitionException.class,
() -> ac.getBean(MemberRepository.class));
}
@Test
@DisplayName("타입으로 조회 시 같은 타입이 둘 이상있으면, 빈 이름을 지정하면 됨")
void findBeanByName() {
MemberRepository memberRepository = ac.getBean("memberRepository1", MemberRepository.class);
org.assertj.core.api.Assertions.assertThat(memberRepository).isInstanceOf(MemberRepository.class);
}
@Test
@DisplayName("특정 타입을 모두 조회하기")
void findAllBeanByType() {
Map<String, MemberRepository> beansOfType = ac.getBeansOfType(MemberRepository.class);
for (String key : beansOfType.keySet()) {
System.out.println("beansOfType = " + beansOfType);
}
System.out.println("beansOfType = " + beansOfType);
org.assertj.core.api.Assertions.assertThat(beansOfType.size()).isEqualTo(2);
}
@Configuration
static class SameBeanConfig {
@Bean
public MemberRepository memberRepository1() {
return new MemoryMemberRepository();
}
@Bean
public MemberRepository memberRepository2() {
return new MemoryMemberRepository();
}
}
ac.getBeansOfType()
: 해당 타입의 모든 빈을 조회
- 동일한 타입이 둘 이상이면 에러 발생 -> 빈 이름 지정!
2-3. 상속관계
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
@Test
@DisplayName("부모 타입으로 조회 시, 자식이 둘 이상 있으면, 중복오류 발생")
void findBeanByParentTypeDuplicate() {
assertThrows(NoSuchBeanDefinitionException.class,
() -> ac.getBean(DiscountPolicy.class));
}
@Test
@DisplayName("부모 타입으로 조회 시, 자식이 둘 이상 있으면, 빈 이름 지정하면 됨")
void findBeanByParentTypeBeanName() {
DiscountPolicy rateDiscountPolicy = ac.getBean("rateDiscountPolicy",DiscountPolicy.class);
Assertions.assertThat(rateDiscountPolicy).isInstanceOf(RateDiscountPolicy.class);
}
@Test
@DisplayName("특정 하위 타입으로 조회")
void findBeanBySubType() {
DiscountPolicy rateDiscountPolicy = ac.getBean(RateDiscountPolicy.class);
Assertions.assertThat(rateDiscountPolicy).isInstanceOf(RateDiscountPolicy.class);
}
@Test
@DisplayName("부모 타입으로 모두 조회")
void findAllBeanByParentType() {
Map<String, DiscountPolicy> beansOfType = ac.getBeansOfType(DiscountPolicy.class);
Assertions.assertThat(beansOfType.size()).isEqualTo(2);
for (String key : beansOfType.keySet()) {
System.out.println("key = " + key + " value = " + beansOfType.get(key));
}
}
@Test
@DisplayName("부모 타입으로 모두 조회 - Object")
void findAllBeanByObjectType() {
Map<String, Object> beansOfType = ac.getBeansOfType(Object.class);
Assertions.assertThat(beansOfType.size()).isEqualTo(2);
for (String key : beansOfType.keySet()) {
System.out.println("key = " + key + " value = " + beansOfType.get(key));
}
}
@Configuration
static class TestConfig {
@Bean
public DiscountPolicy rateDiscountPolicy() {
return new RateDiscountPolicy();
}
@Bean
public DiscountPolicy fixDiscountPolicy() {
return new FixDiscountPolicy();
}
}
- 부모 타입으로 조회하면 자식 타입도 함께 조회됨
Object
타입으로 조회하면, 모든 스프링 빈 조회됨
3. BeanFactory와 ApplicationContext
3-1. BeanFactory
- 스프링 컨테이너의 최상위 인터페이스
- 스프링 빈을 관리하고 조회하는 역할
- getBean() 제공
- 지금까지 우리가 사용했던 대부분의 기능은 BeanFactory가 제공하는 기능
3-2. ApplicationContext
- BeanFactory 기능을 모두 상속받아서 제공
- 부가기능 제공(메세지소스를 활용한 국제화 기능, 환경변수, 어플리케이션 이벤트 등)
- BeanFactory를 직접 사용할 일은 거의 없음. 부가기능이 포함된 ApplicationContext를 사용
스프링 컨테이너
= BeanFactory나 ApplicationContext
4. 다양한 설정 형식 지원 - 자바 코드, XML
- AppConfig.class 자리에 XML과 같은 다른 설정 형식 입력
5. 스프링 빈 설정 메타 정보 - BeanDefinition
- 역할과 구현을 개념적으로 나눈 것
- BeanDefinition = 빈 설정 메타정보
@Bean(java)
, <bean>(xml)
당 각각 하나씩 메타 정보가 생성됨
- 스프링 컨테이너는 이 메타정보를 기반으로 스프링 빈 생성
- beanDefinition은 interface (추상) -> 스프링컨테이너는 추상화에만 의존
- AnnotatedBeanDefinitionReader는 AppConfig.class파일 읽어서 beanDefinition 생성하는 것