스프링 컨테이너 == ApplicationContext (인터페이스)
XML 기반 or 에노테이션 기반으로 만들 수 있음
요즘에는 에노테이션으로 생성하는 경우가 많다. AppConfig를 사용했던 방식이 에노테이션 기반
에노테이션 기반 컨테이너 생성
ApplicationContext applicationContext = new AnnotationConfigApplication(Appconfig.class);
1) 스프링 컨테이너 생성
스프링 컨테이너를 생성할 때 구성 정보인 AppConfig.class를 넘겨준다

2) 스프링 빈 등록
이렇게 비어있는 컨테이너에서 AppConfig에서 Bean으로 등록한 것들을
빈 이름, 빈 객체로해서 저장함
Bean 등록시 Bean 이름을 직접 설정할 수도 있다.
⭐️Bean 이름은 중복 설정할 수 없다
@Bean(name="설정할 이름")

+) 스프링 빈 등록하는 방식
3) 스프링 빈 의존 관계 설정
스프링 컨테이너 생성시 넘겨줬던 설정 정보 (AppConfig)를 통해 스프링이 의존관계를 주입한다. (DI)

1) 스프링 빈 전체 조회
빈이 잘 등록되었는지 확인해보자
AnnotaitonConfigApplicationContext (스프링 컨테이너)를 만들어주고,
getBeanDefinitionNames(): 컨테이너에 있는 빈 이름 전체 조회
getBean(이름, 타입): 해당 이름을 가진 빈 반환, Object로 받아준다
두 메서드를 이용하여 빈 전체를 출력
⭐️ sout 단축키를 사용하면 System.out.println을 바로 만들어준다
public class ApplicationContextInfoTest {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
@Test
@DisplayName("모든 빈 출력하기")
void findAllBean(){
String beanDefinitionNames[] = ac.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames){
Object bean = ac.getBean(beanDefinitionName);
System.out.println("bean = " + bean);
}
}
}
2) 스프링 애플리케이션 빈 조회
전체 조회의 경우 스프링 내부의 빈이 같이 나오기 때문에, 내가 등록한 빈만 출력하고 싶을 때는 BeanDefinition의 getRole() 을 사용해 Application일 때만 출력하게 하면 된다.
@Test
@DisplayName("애플리케이션 빈 출력하기")
void findApplicationBean(){
String beanDefinitionNames[] = ac.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames){
BeanDefinition beanDefinition = ac.getBeanDefinition(beanDefinitionName);
if (beanDefinition.getRole() == BeanDefinition.ROLE_APPLICATION){
Object bean = ac.getBean(beanDefinitionName);
System.out.println("name = " + beanDefinitionName + " object = "+bean);
}
}
}
3) 스프링 빈 이름으로 조회
getBean("이름", 타입)
Assertions.assertThat(조회한 빈).isInstanceOf(구현체.class) 를 통해 검증한다.
4) 스프링 빈 타입으로 조회
getBean(타입)
같은 타입이 여러 개인 경우 오류가 발생한다
...
public class ApplicationContextBasicFindTest {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
@Test
@DisplayName("빈 이름으로 조회")
void findBeanByName(){
MemberService memberService = ac.getBean("memberService", MemberService.class);
assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}
@Test
@DisplayName("빈 타입으로 조회")
void findBeanByType(){
MemberService memberService = ac.getBean(MemberService.class);
assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}
3) 없는 빈 이름으로 조회
등록되어있지 않은 임의의 이름으로 조회하면 NoSuchBeanDefinitionException이 터진다
예외를 받아오기 위해서는 assertThrows 사용
@Test
@DisplayName("빈 이름으로 조회 X")
void findBeanByNameX(){
// MemberService memberService = ac.getBean("hongsi", MemberService.class);
Assertions.assertThrows(NoSuchBeanDefinitionException.class,
() -> ac.getBean("hongsi", MemberService.class));
}
테스트 내부에서만 사용하는 Config와 중복 빈을 생성하고 getBean을 사용해서 타입으로 빈을 조회
NoUniqueBeanDefinitionException 이 터진다
타입이 같은 빈이 있는 경우 이름과 함께 조회해야한다
특정 타입을 모두 조회하는 경우에는 getBeansOfType(타입) 으로 조회된 Map 뿌려서 확인한다.
key = 빈 이름, value = 빈 객체
@Test
@DisplayName("특정 타입을 모두 조회하기")
void findAllBeanByType(){
Map<String, MemberRepository> beansOfType = ac.getBeansOfType(MemberRepository.class);
for(String key: beansOfType.keySet()){
System.out.println("key = "+key+" value = "+beansOfType.get(key));
}
System.out.println("beansOfType = "+ beansOfType);
assertThat(beansOfType.size()).isEqualTo(2);
}
원칙) 부모 타입으로 조회하는 경우 자식도 함께 조회됨
모든 클래스의 최상위 부모인 Object로 조회하는 경우 모든 빈을 조회함

만약 부모 타입으로 조회하는 경우 자식의 타입이 중복된다면 오류가 발생함
NoUniqueBeanDefinitionException 발생
이런 경우 당연히 이름 지정해서 조회하면 문제가 생기지 않는다
구체적인 타입으로 지정해서 조회할 수도 있다.
그러나 이렇게 구체적인 것에 의존하는 것은 좋지 않음
RateDiscountPolicy bean = ac.getBean(RateDiscountPolicy.class);
부모 타입으로 전부 조회할 수도 있다.
Map<String, DiscountPolicy> beansOfType = ac.getBeansOfType(DiscountPolicy.class);
}
getBeansOfType(Object.class)로 하는 경우에는 전체가 조회된다
⭐️ 테스트에서 출력이 나오도록하면 안 된다. 실무에서는 시스템이 검수하게 해야함

BeanFactory
ApplicationContext
BeanFactory를 상속 받음, 주로 ApplicationContext 사용
BeanFactory가 제공하는 기능 + 부가 기능

메시지 소스를 활용한 국제화 기능
환경 변수: 로컬, 개발, 운영을 구분하여 처리
애플리케이션 이벤트: 이벤트 발생 및 구독 모델 지원
편리한 리소스 조회: 외부에서 리소스를 받아 추상화
지금까지는 AppConfig를 이용해서 설정을 했으나, XML을 이용해서도 가능하다.
최근에는 스프링부트를 주로 사용하나, XML의 경우에는 컴파일이 필요 없다는 장점이 있다.
resources/appConfig.xml 파일 생성 (우클릭하면 spring config 파일로 자동 생성해주는 기능이 있다)
bean으로 등록하는 기능과 동일하다
코드는 생략
스프링이 다양한 설정 형식을 지원할 수 있는 이유
→ BeanDefinition이라는 추상화 덕분 (역할과 구현을 개념적으로 나눔)
설정 형식에 관계없이 스프링 컨테이너는 BeanDefinition만 알면 된다.
BeanDefinition을 직접 정의하거나 사용하는 일은 실무에서 거의 없다. (가끔 스프링 오픈 소스를 볼 때 참고용으로 이해)