728x90

🎈 스프링 빈 조회 - 동일한 타입이 둘 이상

타입으로 조회 시 같은 타입의 스프링 빈이 둘 이상이면 오류가 발생합니다.빈 이름을 지정해줌으로써 오류를 방지할 수 있습니다. 스프링 빈의 타입을 조회하는 문법은 ac.getBeansOfType();을 사용하면 해당 타입의 모든 빈을 조회할 수 있습니다.

👉 동일한 타입의 스프링 Bean의 DI 컨테이너에서 Bean을 조회했을 때의 예시

        public class ApplicationContextSameBeanFindTest {

            AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SameBeanConfig.class);

            @Test
            @DisplayName("타입으로 조회 시 같은 타입이 둘 이상 있으면, 중복 오류가 발생합니다.")
            void findBeanByTypeDuplicate() {

                //DiscountPolicy bean = ac.getBean(DiscountPolicy.class);
                //아래와 같이 코드를 작성한다면 예외가 발생하게 되는데, 지금 @Bean(하단에 작성한) 조회는 하단의 Bean을 조회
                /*MemberRepository bean = ac.getBean(MemberRepository.class);*/
                assertThrows(NoUniqueBeanDefinitionException.class, () -> ac.getBean(MemberRepository.class));

            }

            @Configuration
            //static를 사용 시 지역(scope)설정을 하기 위해 해당 클래스 안에서만 쓰겠다는 의미로 작성
            static class SameBeanConfig {

                //Bean의 이름은 다르나 반환되는 리턴값의 객체는 동일할 수 있음
                @Bean
                public MemberRepository memberRepository1() {
                    return new MemoryMemberRepository();
                }

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

            }

        }

해당 TEST 코드를 실행하게 되면 NoUniqueBeanDefinitionException 예외가 발생하게 됩니다.

 

먼저 두 개의 @Bean 메서드가 동일한 타입인 경우는 메서드의 반환 타입을 기반으로 알 수 있습니다. 위의 코드에서 memberRepository1()과 memberRepository2() 메서드는 모두 MemoryMemberRepository 타입의 객체를 반환하고 있으므로, 이 두 @Bean 메서드의 타입은 MemoryMemberRepository입니다.

 

스프링 프레임워크는 @Bean 메서드의 반환 타입을 기준으로 해당 메서드가 생성하는 빈(Bean)의 타입을 결정합니다. 따라서 같은 타입의 빈을 여러 개 정의하려면, 메서드의 반환 타입을 동일한 타입으로 지정해야 합니다.

이러한 상황에서 스프링은 빈의 식별을 위해 메서드 이름을 사용합니다. 즉, memberRepository1()과 memberRepository2() 메서드는 동일한 타입의 빈을 생성하지만, 스프링은 메서드 이름으로 구분하여 각각의 빈을 생성합니다. 따라서, 위의 코드에서 memberRepository1()과 memberRepository2() 메서드는 동일한 타입인 MemoryMemberRepository를 반환하지만, 스프링은 메서드 이름에 따라 각각의 빈을 구분합니다.


 

💡 스프링 빈 조회 - 상속관계

스프링 빈 상속 관계스프링 프레임워크에서 빈(Bean) 설정 시 사용되는 개념입니다.

부모타입으로 조회하면 본인을 포함하여 자식 타입도 함께 조회됩니다. 그래서 모든 자바 객체의 최상위 Object 타입으로 조회하면 모든 스프링 빈을 조회합니다. 스프링에서는 빈을 정의하고 구성하기 위해 XML 또는 어노테이션 기반의 설정을 사용합니다.

빈 상속은 한 빈을 기반으로 다른 빈을 정의하는 것을 의미합니다. 기본 빈(부모 빈)을 정의하고, 이를 상속하는 빈(자식 빈)을 생성할 수 있습니다. 자식 빈은 부모 빈의 설정을 상속하면서 추가적인 설정이나 오버라이딩을 할 수 있습니다.

빈 상속을 사용하면 다음과 같은 장점을 얻을 수 있습니다:

  1. 코드 중복 감소: 공통적인 설정을 부모 빈에서 정의하고, 자식 빈에서 추가적인 설정만 정의하여 중복을 줄일 수 있습니다.
  2. 유지보수성 향상: 공통된 설정을 부모 빈에서 관리하므로, 변경이 필요한 경우 부모 빈만 수정하면 모든 자식 빈에 반영됩니다.
  3. 가독성과 일관성: 빈 상속을 통해 관련 빈들을 계층 구조로 표현할 수 있으며, 코드의 가독성과 일관성을 높일 수 있습니다.

빈 상속은 스프링의 설정 파일(XML 또는 어노테이션)에서 parent 속성을 사용하여 부모 빈을 지정하는 방식으로 사용됩니다. 자식 빈은 부모 빈의 설정을 상속하며, 필요한 경우 추가적인 설정을 오버라이딩하여 사용할 수 있습니다.

 



        public class ApplicationContextExtendsFindTest {

            AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);

            @Test
            @DisplayName("부모 타입으로 조회 시 자식이 둘 이상 있으면 중복 오류가 발생합니다.")
            void findBeanByParentTypeDuplicate() {
            
                //아래와 같이 작성한 후 bean을 조회한다면 현재 2개의 @Bean이 생성되어 있어 두개 다 조회되며
                //NoUniqueBeanDefinitionException 예외가 발생
                //DiscountPolicy bean = ac.getBean(DiscountPolicy.class);
                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() {
                RateDiscountPolicy bean = ac.getBean(RateDiscountPolicy.class);
                assertThat(bean).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)  );

                }
            }

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

                }

            }


            @Configuration
            static class TestConfig {

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

                @Bean
                public DiscountPolicy fixDiscountPolicy() {
                    return new FixDiscountPolicy();
                }
            }
        }
728x90
반응형

+ Recent posts