Study/Java&Spring

Spring Container/Bean

kdhoooon 2021. 7. 19. 15:49

스프링 컨테이너

  • ApplicationContext를 스프링 컨테이너라 한다.
  • ApplicationContext는 인터페이스다.
  • 스프링 컨테이너는 XML을 기반으로 만들 수 있고, 애노테이션 기반의 자바 설정 클래스로 만들 수 있다.
  • 기존에 개발자가 AppConfig를 사용해서 직접 객체를 생성하고 DI를 했지만, 이제부터는 스플링 컨테이너를 통해서 사용한다.
  • 스프링 컨테이너는 @Configuration 이 붙은 AppConfig를 설정(구성)정보로 사용한다. 여기서 @Bean 이라 적힌 메서드를 모두 호출해서 반환된 객체를 스프링 컨테이너에 등록한다.

 

스프링 빈 

  • 스프링 컨테이너에 등록된 객체를 스프링 빈이라 한다.
  • 스프링 빈은 @Bean이 붙은 메서드의 명을 스프링 빈의 이름으로 사용한다.
  • 이전에는 개발자가 필요한 객체를 AppConfig를 사용해서 직접 조회했지만, 이제부터는 스프링 컨테이너를 통해서 필요한 스프링 빈(객체)를 찾아야 한다. 스프링 빈은 applicationContext.getBean() 메서드를 사용해서 찾을 수 있다.
  • 빈 이름은 항상 다름이름을 부여해야한다. 같은 이름을 부여하면, 다름 빈이 무시되거나, 기존 빈을 덮어버리거나 설정에 따라 오류를 발생시킨다.

빈 조회하기

<모든 빈 조회하기>

@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 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);
        }
    }
}

 

<그외 다른 것들>

@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 findBeanByName2(){
    MemberService memberService = ac.getBean("memberService", MemberServiceImpl.class);
    assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}

 

<없는 빈을 조회하는 실패 테스트>

@Test
@DisplayName("빈 이름으로 조회X")
void findBeanByNamex(){
    //ac.getBean("xxxx", MemberService.class);
    assertThrows(NoSuchBeanDefinitionException.class,
            () -> ac.getBean("xxxx", MemberService.class));
}

 

스프링빈 조회의 상속관계

  • 부모 타입으로 조회하면, 자식 타입도 함께 조회한다.
  • 그래서 모든 자바 객체의 최고 부모인 Object 타입으로 조회하면, 모든 스프링 빈을 조회한다.

<상속관계를 보기 위한 예시 코드>

@Test
@DisplayName("타입으로 조회시 같은 타입이 둘 이상 있으면, 중복 오류가 발생한다")
void findBeanByTypeDuplicate(){
    //MemberRepository bean = ac.getBean(MemberRepository.class);

    assertThrows(NoUniqueBeanDefinitionException.class,
            () -> ac.getBean(MemberRepository.class));
}

@Test
@DisplayName("타입으로 조회시 같은 타입이 둘 이상 있으면, 빈 이름을 지정하면 된다")
void findBeanByName(){
    MemberRepository memberRepository = ac.getBean("memberRepository1", MemberRepository.class);
    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("key = " + key + " value = " + beansOfType.get(key));
    }
    System.out.println("beansOfType = " + beansOfType);
    assertThat(beansOfType.size()).isEqualTo(2);

}


@Configuration
static class SameBeanConfig {

    @Bean
    public MemberRepository memberRepository1() {
        return new MemoryMemberRepository();
    }

    @Bean
    public MemberRepository memberRepository2() {
        return new MemoryMemberRepository();
    }

}