Notice
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
01-24 00:19
Today
Total
관리 메뉴

그날그날 공부기록

스프링 빈 조회 본문

Spring 공부

스프링 빈 조회

given_dragon 2022. 7. 11. 16:05

생성된 스프링 빈을 여러가지 방법으로 조회할 수 있다.

기본적으로 getBean(”빈 이름", “타입”)으로 조회하면 된다.

 

1. 빈 이름과 타입으로 조회

AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

@Test
@DisplayName("빈 조회 - 이름, 타입")
void findBeanByName(){
    MemberService memberService = ac.getBean("memberService", MemberService.class);
    assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}

“memberService”라는 빈이 잘 등록이 되었다면 테스트 코드가 통과된다. assertThat~~부분은 조회된 빈이 MemberServiceImpl의 인스턴스가 맞는지 확인해준다.

assertj의 Assertions를 import하여 테스트코드 작성하는데 사용하면 편리하다! 나중에 사용하는걸 자세히 해볼 예정

 

2. 타입으로 조회

@Test
@DisplayName("빈 조회 - 타입")
void findBeanByType(){
    MemberService memberService = ac.getBean(MemberService.class);
    assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}

빈 이름을 작성해주지 않아도 조회가 가능하다.

하지만 같은 타입의 빈이 여러개일 경우 오류가 발생한다.

 

2-1. 구체 타입으로 조회

@Test
@DisplayName("빈 조회 - 구체 타입")
void findBeanByType2(){
    MemberService memberService = ac.getBean("memberService", MemberServiceImpl.class);
    assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}

타입을 인터페이스로 적지 않고 구현체로 적어도 조회가 가능하다. 컨테이너에 빈을 등록할 때 반환 타입이 아닌 인스턴스 타입으로 결정되기 때문이다.

하지만 구체적인 타입으로 적는 것은 꼭 필요하지 않다면 사용하지 않는 것이 역할과 구현을 분리하는데 좋다.

 

3. 동일한 타입의 빈 조회

앞서 오류가 발생했던 동일한 타입의 빈을 조회하고 싶다면 이름을 포함하여 검색해주면 된다.

만약 모두 검색을 하고싶다면 getBeansOfType을 사용한다.

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

getBeansOfType을 사용하면 Map타입으로 반환이 된다.

출력 결과는 다음과 같다.

key = memberRepository1 value = hello.core.member.MemoryMemberRepository@6d026701
key = memberRepository2 value = hello.core.member.MemoryMemberRepository@78aa1f72
beansOfType = {memberRepository1=hello.core.member.MemoryMemberRepository@6d026701, memberRepository2=hello.core.member.MemoryMemberRepository@78aa1f72}

 

4. 상속관계

부모 타입으로 조회할 경우 자식 타입도 모두 조회된다.

1~3과 겹쳐서 코드만 첨부

public class ApplicationContextExtendsFindTest {
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);

    @Test
    @DisplayName("부모 타입으로 조회시, 자식이 둘 이상 있으면 중복오류가 발생")
    void findBeanByParentTypeDuplicate(){
        assertThrows(NoUniqueBeanDefinitionException.class,
                () -> ac.getBean(DiscountPolicy.class));
    }

    @Test
    @DisplayName("부모 타입으로 조회시, 자식이 둘 이상 있으면 빈 이름 지정.")
    void findBeanByParentTypeBeanName(){
        DiscountPolicy rateDiscountPolicy = ac.getBean("rateDiscountPolicy", DiscountPolicy.class);
        assertThat(rateDiscountPolicy).isInstanceOf(RateDiscountPolicy.class);
        //해당 이름의 빈만 조회된다.
    }

    @Test
    @DisplayName("특정 하위 타입으로 조회")
    void findBeanBySubType(){
        DiscountPolicy rateDiscountPolicy = ac.getBean(RateDiscountPolicy.class);
        assertThat(rateDiscountPolicy).isInstanceOf(RateDiscountPolicy.class);
        //구체 타입으로 조회해도 가능하지만 좋지 않은 방법
    }

    @Test
    @DisplayName("부모 타입으로 모두 조회")
    void findAllBeanByParentType(){
        Map<String, DiscountPolicy> beansOfType = ac.getBeansOfType(DiscountPolicy.class);
        assertThat(beansOfType.size()).isEqualTo(2);
        for (String key : beansOfType.keySet()) {
            System.out.println("key = " + key + " value = " + beansOfType.get(key));
        }
        //같은 타입인 빈 2개가 조회된다.
    }

    @Test
    @DisplayName("Object 타입으로 모두 조회")
    void findAllBeanByObjectType(){
        Map<String, Object> beansOfType = ac.getBeansOfType(Object.class);
        for (String key : beansOfType.keySet()) {
            System.out.println("key = " + key + " value = " + beansOfType.get(key));
        }
        //최상위 클래스인 Object로 조회했기 때문에 스프링 내부적으로 사용되는 빈 까지 모두 조회된다.
    }

    @Configuration
    static class TestConfig{    //임시 AppConfig
        @Bean
        public DiscountPolicy rateDiscountPolicy(){
            return new RateDiscountPolicy();
        }
        @Bean
        public DiscountPolicy fixDiscountPolicy(){
            return new FixDiscountPolicy();
        }
    }
}

테스트 코드 작성 시 실패 테스트도 필요하다.

@Test
@DisplayName("빈 조회 - 실패")
void findBeanByNameFail(){
    org.junit.jupiter.api.Assertions.assertThrows(
			NoSuchBeanDefinitionException.class,
            () -> ac.getBean("xxxx", MemberService.class));
}

조회 테스트 코드처럼 getBean을 사용하여 “xxxx”라는 이름의 빈을 조회한다면 NoSuchBeanDefinitionException이 발생하며 테스트가 실패한다.

당연하지만 해당 이름을 가진 빈을 생성하지 않았기 때문이다.

테스트 코드가 실패했다는걸 검증하기 위해 junit의 assertThrows를 사용한다.

람다식이 실행될 경우 NoSuchBeanDefinitionException이 발생되므로 검증에 성공한다. (예외 발생 → 성공, 예외 발생X → 실패)

'Spring 공부' 카테고리의 다른 글

BeanDefinition  (0) 2022.07.18
BeanFactory & ApplicationContext  (0) 2022.07.15
스프링 컨테이너 생성 과정  (0) 2022.07.07
IoC, DI, 컨테이너  (0) 2022.07.06
스프링 DI 이해하기  (0) 2022.07.05
Comments