<aside> ❗ 중요한 내용

</aside>

상속 관계

예제 코드

ApplicationContextExtendsFindTest를 만듭니다.

Untitled

package hello.core.beanfind;

import hello.core.discount.DiscountPolicy;
import hello.core.discount.FixDiscountPolicy;
import hello.core.discount.RateDiscountPolicy;
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 static org.junit.jupiter.api.Assertions.assertThrows;

public class ApplicationContextExtendsFindTest {

    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);

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

    @Configuration //...1
    static class TestConfig { //...3

        @Bean
        public DiscountPolicy rateDiscountPolicy() {
            return new RateDiscountPolicy();
        }

        @Bean
        public DiscountPolicy fixDiscountPolicy() {
            return new FixDiscountPolicy();
        }
    }

}
  1. @Configuration

  2. 부모 타입으로 조회 했는데 자식 인스턴스 2개가 다 걸려듭니다. 그래서 둘다 조회가 됩니다.

    그래서 NoUniqueBeanDefinitionException이 터집니다.

    부모를 찾으면 자식이 끌려옵니다! 타입을 DiscountPolicy에서 RateDiscountPolicy 나 FixDiscountPolicy로 변경해도NoUniqueBeanDefinitionException 이 터집니다.

  3. static을 써야하는 이유?

    static을 뺀 inner class는 inner class를 감싸고 있는 outer class에 종속됩니다.

    즉, outer class의 객체를 통해서만 inner class에 접근할 수 있습니다.

    static inner class는 outer class 내부에 선언되었지만 outer class의 객체 생성 유무와 별개로 만들어집니다.

    독립적으로 사용되어지는 것이죠.

    Test 클래스가 outer 클래스이고, TestConfig 클래스가 inner 클래스입니다.

    Test 클래스 내 TestConfig 클래스에 static 키워드를 뺀다면 Test 클래스가 생성되어야 TestConfig를 사용할 수 있습니다.

    그러나 Test 클래스 내에서는 이미 TestConfig가 생성되기도 전에 스프링 컨테이너에서 TestConfig 빈이 있는지 찾아오려고 합니다.

    당연히 스프링에서는 그런 빈이 없다고 합니다.

    static 키워드를 붙이면 Test 클래스와는 별개로 TestConfig 클래스가 생성되고 빈으로 등록됩니다.

    당연히 Test 클래스에서는 TestConfig를 불러와서 사용할 수 있게 됩니다.

    자세한 내용

그럼 어떻게 하면 될까요?

당연히 빈 이름을 지정하면 됩니다.

@Test
    @DisplayName("부모 타입으로 조회시, 자식이 둘 이상 있으면, 빈 이름을 지정하면 된다.")
    void findBeanByParentTypeBeanName() {
				//...1
        DiscountPolicy rateDiscountPolicy = ac.getBean("rateDiscountPolicy", DiscountPolicy.class);
				//...2
        assertThat(rateDiscountPolicy).isInstanceOf(RateDiscountPolicy.class);
    }
  1. 타입은 DiscountPolicy이지만 실제 구현 객체는 RateDiscountPolicy가 나옵니다.

    <aside> ❗ DiscountPolicy는 인터페이스이므로 부모 타입으로 쓸 수 있습니다.

    예를 들어 HashMap<String, String> map = new HashMap<>();으로 할 수도 있지만 Map<String, String> map = new HashMap<>();으로 할 수도 있습니다.

    이렇게 사용하는 이유?

    </aside>

  2. rateDiscountPolicy가 RateDiscountPolicy의 인스턴스였으니 검증을 성공합니다.

타입으로 호출하는 방법이 있습니다.

특정 하위 타입으로 조회하면 됩니다. 물론 안좋은 방법 입니다.

	  @Test
    @DisplayName("특정 하위 타입으로 조회")
    void findBeanBySubType() {
        RateDiscountPolicy bean = ac.getBean(RateDiscountPolicy.class);
        assertThat(bean).isInstanceOf(RateDiscountPolicy.class);
    }

구체적인 타입으로 지정하면 RateDiscountPolicy는 하나밖에 없으니까 가능합니다.