슬픈 야옹이 2023. 11. 18. 12:56

스프링 빈은 기본적으로 getBean()을 이용해 조회할 수 있으며,

 

빈 이름 뿐 아니라 빈 타입으로도 조회가 가능했다.

 

예컨데, MemberRepository 타입의 빈을 조회하고 싶으면 ac.getBean(MemberRepository.class)의 형태로 빈 조회가 가능했던 것이다.

 

하지만, 어플리케이션 환경이나 빈의 사용 목적에 따라 같은 타입의 빈은 얼마든지 여러개 존재할 수 있다.

 

타입으로 조회 시 같은 타입의 빈이 여러개 있을 경우 예외가 발생하므로 주의해야 한다.

 

그러한 경우 빈 이름을 지정해서 조회하거나, getBeansOfType()을 활용한다. 하나씩 알아보자.

 

테스트 클래스는 강의에서는 테스트 폴더 beanfind 패키지 하위에 만들었지만, 패키지 경로가 그리 중요하지는 않다.

 

메서드 사용법이 중요하다.

 

 

 

Config 클래스

원활한 테스트를 위해 기존에 사용하던 AppConfig가 아닌 SameBeanConfig 클래스를 임의로 생성하여 사용한다.

 

클래스는 MemberRepository 타입의 빈 2개를 생성하도록 구성하였다.

 

static class SameBeanConfig

 

static class SameBeanConfig

@Configuration
static class SameBeanConfig {

@Bean
public MemberRepository memberRepository1() {
        return new MemoryMemberRepository();
    } // 빈 이름은 memberRepository1

    @Bean
    public MemberRepository memberRepository2() {
        return new MemoryMemberRepository();
    } // 빈 이름은 memberRepository2

}

 

임의의 설정 클래스를 사용하므로 ApplicationContext 클래스 생성자에 전달하는 정보로 변경된다.

 

ApplicaionContext 생성자

 

AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SameBeanConfig.class);

 

 

 

 

 

 

 

동일한 타입의 빈이 둘 이상인 경우

동일한 타입의 빈이 둘 이상인 경우, getBean() 메서드에 빈 이름을 전달하거나 getBeansOfType()을 이용한다.

 

 

중복 타입 빈 조회 시 예외 발생

먼저, 동일 타입 빈을 조회할 경우 발생하는 예외를 테스트해본다.

동일 타입 조회 시 예외 발생

 

findBeanByTypeDuplicate()

@Test
@DisplayName("타입으로 조회 시 같은 타입의 빈이 여러 개 있으면 예외가 발생한다.")
void findBeanByTypeDuplicate() {
    // ac.getBean(MemberRepository.class);
    assertThrows(NoUniqueBeanDefinitionException.class,
            () -> ac.getBean(MemberRepository.class));
}

 

같은 타입의 빈을 타입으로 조회하면 NoUniqueBeanDefinitionException 예외가 발생한다.

findBeanByTypeDuplicate() 메서드는 해당 예외가 발생하는지를 테스트한다.

 

+) assertThrows()는 jupyter의 Assertions를 static import했다. 즉 원형은 Assertions.assertThrows()

 

 

 

빈 이름을 지정해서 조회

같은 타입의 빈이 여러개 있을 경우, 빈 이름을 지정해서 조회하면 된다.

빈 이름은 기본적으로 빈을 구분하는 key이므로 정상적인 조회가 가능하다.

빈 이름을 지정해서 조회

 

findBeanByName()

@Test
@DisplayName("같은 타입의 빈이 여러개 있을 경우, 빈 이름을 지정해서 조회한다.")
void findBeanByName() {
    MemberRepository memberRepository = ac.getBean("memberRepository1", MemberRepository.class);
    assertThat(memberRepository).isInstanceOf(MemberRepository.class);
}

 

 

 

 

특정 타입의 빈을 모두 조회

혹은, getBeansOfType()을 이용하면 특정 타입의 모든 빈을 조회할 수 있다.

 

인자로 빈의 타입 (.class)을 전달하며, 전달한 타입의 빈들을 Map<이름, 타입>의 형태로 리턴한다.

 

특정 타입의 빈 모두 조회

 

findAllBeanByType()

@Test
@DisplayName("특정 타입을 모두 조회하기")
void findAllBeanByType() {
    // 같은 타입들을 모두 조회하면 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); // 찾아낸게 2개인지 확인

}

 

getBeansOfType()은 해당 타입의 빈들을 Map<> 형태로 리턴하므로 key들을 이용해 모두 조회할 수 있다.

 

테스트는 단순히 조회된 빈의 개수가 2개가 맞는지 확인한다.

 

 

 

전체 테스트 클래스 코드

 

ApplicaionSameBeanFindTest.java

package hdxian.hdxianspringcore.beanfind;

import hdxian.hdxianspringcore.AppConfig;
import hdxian.hdxianspringcore.discount.DiscountPolicy;
import hdxian.hdxianspringcore.member.MemberRepository;
import hdxian.hdxianspringcore.member.MemoryMemberRepository;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Map;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;

class ApplicationSameBeanFindTest {

    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SameBeanConfig.class);

    @Test
    @DisplayName("타입으로 조회 시 같은 타입의 빈이 여러 개 있으면 예외가 발생한다.")
    void findBeanByTypeDuplicate() {
        // 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 형태로 찾아서 리턴한다.
        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); // 찾아낸게 2개인지 확인

    }


    @Configuration
    static class SameBeanConfig {

        @Bean
        public MemberRepository memberRepository1() {
            return new MemoryMemberRepository();
        } // 빈 이름은 memberRepository1

        @Bean
        public MemberRepository memberRepository2() {
            return new MemoryMemberRepository();
        } // 빈 이름은 memberRepository2

    }



}