# 스프링 컨테이너
ApplicationContext AC=new AnnotationConfigApplicationContext(AppConfig.class);
//자동으로 AppConfig의 bean들을 spring container에 넣어서 관리해줌
//스프링 ApplicationContext를 통해 찾아와야함
- ApplicationContext를 스프링 컨테이너라고 하며, 이는 인터페이스이다.
: 스프링 컨테이너는 XML로도, 애노테이션 기반의 자바 설정 클래스로도 만들 수 있으며 현재는 후자가 더 많이 사용된다.
- new AnnotationConfigApplicationContext(AppConfig.class) 은 위의 ApplicationContext 인터페이스의 구현체이다.
# 스프링 컨테이너의 생성 과정
1. 스프링 컨테이너 생성
: 스프링 컨테이너를 생성할 때는 구성 정보를 지정해야 하며, 이는 AppConfig.class로 지정되었다.
-> AppConfig 내에서 @Configuration 애노테이션을 통해 스프링 컨테이너에 자동 등록될 구성 정보가 만들어진 바 있다.
2. 스프링 빈 등록
: 스프링 컨테이너는 파라미터로 넘어온 설정 클래스 정보를 사용해 스프링 빈을 컨테이너에 등록한다.
- 빈 이름은 기본적으로 메소드 이름을 사용하나, @Bean(name="memberSerivce2")와 같이 직접 부여 가능
- 빈 이름은 항상 다른 이름을 부여해야 한다. 아니면 오류 또는 다른 빈을 무시하는 일이 생긴다.
3. 스프링 빈 의존 관계 설정
: 구성 정보를 참고하여 의존관계를 주입하며, 어떤 구현체가 들어가는지는 getter를 통해 결정해주면 된다.
1) memberService에 사용될 repository는 아래의 memberRepository 객체를 참조한다.
2) orderService에 사용될 Policy와 Repository는 아래의 memberRepository와 discountPolicy 객체를 참조한다.
-> 각각의 인터페이스에 어떤 구현체가 들어올지는 런타임에 결정되며, 이때 외부에서 실제 구현 객체가 생성되어 클라이언트에 주입된다.
# 스프링 빈 등록 확인
1. 컨테이너에 등록된 모든 빈 조회
- ac.getBeanDefinitionNames() : 스프링에 등록된 모든 빈 이름을 조회한다.
- ac.getBean() : 빈 이름을 통해 bean 객체를 조회한다.
//모든 빈을 출력
void findAllBean(){
String[] beanDefinitionNames=ac.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
Object bean=ac.getBean(beanDefinitionName);
System.out.println( "name = " + beanDefinitionName+"object="+bean);
}
}
-> 현재 컨테이너에 등록된 모든 빈이 출력된다.
+) 여기서 만약 자신이 직접 등록한 애플리케이션 빈만 출력하고 싶다면 if 조건문을 더해줘야 한다.
if(beanDefinition.getRole()==BeanDefinition.ROLE_APPLICATION){
Object bean = ac.getBean(beanDefinitionName);
System.out.println( "name = " + beanDefinitionName+"object="+bean);
}
1) ROLE_APPLICATION : 직접 등록한 애플리케이션 빈만 출력
2) ROLE_INFRASTRUCTURE : 스프링이 내부에서 사용하는 빈만 출력한다.
2. 특정 빈 찾기
- ac.getBean(빈 이름, 타입) or ac.getBean(타입)
@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);
}
@Test
@DisplayName("구체 타입으로 조회")
void findBeanByType2(){
MemberService memberService = ac.getBean("memberService",MemberServiceImpl.class);
assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
//구현체를 건드리면 추후 변경시 유연성이 떨어지므로 좋은 방법아님. 웬만하면 추상화로 찾을것.
}
@Test
@DisplayName("없는 빈 이름으로 조회할때 오류 처리")
void findBeanByTypeX(){
assertThrows(NoSuchBeanDefinitionException.class,()->
ac.getBean("XXXX",MemberServiceImpl.class));
}
3. 동일한 타입이 둘 이상
- 위에서처럼 타입으로 조회 시 스프링 빈이 둘 이상이면 오류가 발생할 수 있다.
- ac.getBeansOfType() 를 사용하면 해당 타입의 모든 빈을 조회할 수 있다.
@Test
@DisplayName("타입으로 조회시 같은 타입이 둘 이상 있으면, 중복 오류가 발생한다.")
void findBeanByTypeDuplicate(){
//MemberRepository bean = ac.getBean(MemberRepository.class);
//이때 클래스 두개가 튀어나옴->noUniqueBeanExceptionError 발생
Assertions.assertThrows(NoUniqueBeanDefinitionException.class,
()->ac.getBean(MemberRepository.class));
}//따라서 타입 앞에 빈 이름을 지정해줘야 한다.
@Test
@DisplayName("특정 타입을 모두 조회하기")
void findAllBeanByType(){
//해당 타입을 가진 beans들을 조회할수 있게 된다. Map으로 리턴됨.
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);
}
@Configuration
static class SameBeanConfig{ //static의 장점- scope을 해당 클래스 안에서만 쓰겠다는뜻
@Bean
public MemberRepository memberRepository1(){
return new MemoryMemberRepository();
}
@Bean
public MemberRepository memberRepository2(){
return new MemoryMemberRepository();
}
}
4. 상속 관계로 조회
- 부모 타입 빈을 조회하면 자식 타입도 함께 조회한다는 특징이 존재한다.
- 그렇기 때문에 모든 자바 객체의 부모인 Object로 조회하면, 모든 스프링 빈을 조회할 수 있게 된다.
- ac.getBeansOfType(부모클래스.class); 로 상속관계에 가진 빈의 정보를 담은 Map을 리턴한다.
@Test
@DisplayName("부모 타입으로 조회 시 자식이 둘 이상 있으면 빈 이름을 지정하면 된다.")
void findBeanByParentTypeBeanName(){
DiscountPolicy rateDiscountPolicy= ac.getBean("rateDiscountPolicy",DiscountPolicy.class);
assertThat(rateDiscountPolicy).isInstanceOf(RateDiscountPolicy.class);
}
@Test
@DisplayName("부모 타입으로 모두 조회하기")
void findBeanByParentType(){
Map<String,DiscountPolicy> beansOfType=ac.getBeansOfType(DiscountPolicy.class);
//DiscountPolicy의 구현체인 아래 두개의 빈이 모두 출력된다.
assertThat(beansOfType.size()).isEqualTo(2);
for(String key:beansOfType.keySet()){
System.out.println("key="+key+"value="+beansOfType.get(key));
}
}
@Test
@DisplayName("부모 타입으로 모두 조회하기-Object")
void findBeanByObjectType(){
Map<String,Object> beansOfType=ac.getBeansOfType(Object.class);
//모든 빈은 object이기 때문에 스프링 자체의 빈도 모두 출력된다.
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();
}
}
'Web > Java (Spring+JSP)' 카테고리의 다른 글
Spring 핵심 원리 #8- 싱글톤 컨테이너 (0) | 2022.01.12 |
---|---|
Spring 핵심 원리 #7- 스프링 컨테이너 관련 인터페이스 (0) | 2022.01.11 |
Spring 핵심 원리 #5- IOC,DI, 그리고 컨테이너 (0) | 2021.11.15 |
Spring 핵심 원리 #4- AppConfig 리팩토링/총정리 (0) | 2021.11.12 |
Spring 핵심 원리 #3- 새로운 할인 정책/AppConfig 적용 (0) | 2021.11.12 |