클래스들 간에 상속관계가 있을 때, 빈을 조회하는 방법은 무엇일까?

 

 

 

 

부모 타입으로 조회하면, 자식타입도 함께 조회한다

    -  부모 타입을 조회하면 자식 타입으로 등록된 빈들도 같이 조회하여 우리에게 알려준다. 

 

자바의 최상위 부모는 object 이다. ( 우리 눈에는 코드로 보이지 않지만 모든 클래스에는 extends Object 가 생략되어있다) 

따라서 모든 자바 객체의 최고 부모인 Object 타입으로 조회하면 모든 스프링 빈을 조회한다.

 

 

ApplicationContextExtendFindTest

  • 상속관계가 있는 타입의 빈들을 조회하는 방법에 대해서 알아보자
public class ApplicationContextExtendsFindTest {

    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);

    @Test
    @DisplayName("부모타입으로 조회시 자식이 둘 이상있으면 중복오류가 발생한다")
    public void findBeanByParentTypeDuplicate(){

        DiscountPolicy bean = ac.getBean(DiscountPolicy.class);
        assertThat(bean).isInstanceOf(DiscountPolicy.class);
    }

    @Configuration
    static class TestConfig{
        @Bean
        public DiscountPolicy rateDiscountPolicy(){
            return new RateDiscountPolicy();
        }

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



}

 

// 실행결과
NoUniqueBeanDefinitionException: 
No qualifying bean of type 'hello.core.discount.DiscountPolicy' available: 
expected single matching bean but found 2: rateDiscountPolicy,fixDiscountPolicy

 

부모 타입인 DiscountPolicy.class 을 조회하면

자식 타입인 RateDiscountPolicy.class와 FixDiscountPoolicy.class 로 등록되어있는 빈들을 모두 조회하기 때문에 

반환값이 rateDiscountPolicy, fixDiscountPolicy 로 두개의 빈이 조회되어 중복오류가 발생하는 것이다.

 

 

 

  • 중복예외가 발생하는 상황에 대한 테스트 코드를 작성해보자
    @Test
    @DisplayName("부모타입으로 조회시 자식이 둘 이상있으면 중복오류가 발생한다")
    public void findBeanByParentTypeDuplicate(){

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

 

 

중복오류를 없애는 방법으로  빈 이름도 함께 지정해주면 중복오류를 해결 할 수 있다고 배웠다. 

또 다른 방법으로는  특정 하위 타입으로 조회하는 것이 있다.  

 

1. 빈 이름 지정해주기

@Test
@DisplayName("부모타입으로 조회시 자식이 둘 이상있으면 빈 이름을 지정하면 된다")
public void findBeanByParentTypeBeanName(){

    DiscountPolicy rateDiscountPolicy = ac.getBean("rateDiscountPolicy", DiscountPolicy.class);
    assertThat(rateDiscountPolicy).isInstanceOf(DiscountPolicy.class);
}

 

 

2. 특정 하위 타입(자식 타입) 으로 조회하기

@Test
@DisplayName("특정 하위 타입으로 조회하기")
public void findBeanBySubType(){

    DiscountPolicy rateDiscountPolicy = ac.getBean(RateDiscountPolicy.class);
    assertThat(rateDiscountPolicy).isInstanceOf(RateDiscountPolicy.class);
}

 

 

 

  • 부모 타입으로 모든 빈들을 조회하기
@Test
@DisplayName("부모 타입으로 모두 조회하기")
public void findAllBeanByParentType(){

    Map<String, DiscountPolicy> beansOfType = ac.getBeansOfType(DiscountPolicy.class);
    for (String beanName : beansOfType.keySet()) {
        System.out.println("빈 이름 = " + beanName+","+ "빈 타입 = "+beansOfType.get(beanName));
    }
    System.out.println("beansOfType = "+beansOfType);
    assertThat(beansOfType.size()).isEqualTo(2);

}

 

 

// 실행결과

빈 이름 = rateDiscountPolicy,빈 타입 = hello.core.discount.RateDiscountPolicy@75459c75
빈 이름 = fixDiscountPolicy,빈 타입 = hello.core.discount.FixDiscountPolicy@183e8023
beansOfType = {rateDiscountPolicy=hello.core.discount.RateDiscountPolicy@75459c75, 
               fixDiscountPolicy=hello.core.discount.FixDiscountPolicy@183e8023}

 

 

 

  • Object 타입으로 모든 빈 조회하기
@Test
@DisplayName("Object 타입으로 모두 조회하기")
public void findAllBeansByObjectType(){

    Map<String, Object> beansOfType = ac.getBeansOfType(Object.class);
    for(String beanName: beansOfType.keySet()){
        Object bean= ac.getBean(beanName);
        System.out.println("빈 이름="+beanName+","+"빈 타입= "+beansOfType.get(beanName));
    }

}

 

         실행결과

Object 타입으로 빈 조회하기

 

Object 타입으로 빈을 조회하면 우리가 등록한 빈들 뿐만 아니라 스프링에서 내부적으로 등록한 빈들도 모두 조회한다.

왜냐하면 Object 타입은 모든 객체의 최상위 부모이기 때문이다.

따라서 Object를 상속받고 있는 모든 빈들이 출력된다. 

 

 


☑️ ApplicationContextExtendFindTest 전체코드

package hello.core.beanfind;

import hello.core.discount.DiscountPolicy;
import hello.core.discount.FixDiscountPolicy;
import hello.core.discount.RateDiscountPolicy;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition;
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;

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

    @Test
    @DisplayName("부모타입으로 조회시 자식이 둘 이상있으면 중복오류가 발생한다")
    public void findBeanByParentTypeDuplicate(){

//        DiscountPolicy bean = ac.getBean(DiscountPolicy.class); - 중복오류 발생

        assertThrows(NoUniqueBeanDefinitionException.class,()->
                ac.getBean(DiscountPolicy.class));
    }
    
    @Test
    @DisplayName("부모타입으로 조회시 자식이 둘 이상있으면 빈 이름을 지정하면 된다")
    public void findBeanByParentTypeBeanName(){
        
        DiscountPolicy rateDiscountPolicy = ac.getBean("rateDiscountPolicy", DiscountPolicy.class);
        assertThat(rateDiscountPolicy).isInstanceOf(DiscountPolicy.class);
    }

    @Test
    @DisplayName("특정 하위 타입으로 조회하기")
    public void findBeanBySubType(){

        DiscountPolicy rateDiscountPolicy = ac.getBean(RateDiscountPolicy.class);
        assertThat(rateDiscountPolicy).isInstanceOf(RateDiscountPolicy.class);
    }

    @Test
    @DisplayName("부모 타입으로 모두 조회하기")
    public void findAllBeanByParentType(){

        Map<String, DiscountPolicy> beansOfType = ac.getBeansOfType(DiscountPolicy.class);
        for (String beanName : beansOfType.keySet()) {
            System.out.println("빈 이름 = " + beanName+","+ "빈 타입 = "+beansOfType.get(beanName));
        }
        System.out.println("beansOfType = "+beansOfType);
        assertThat(beansOfType.size()).isEqualTo(2);

    }

    @Test
    @DisplayName("Object 타입으로 모두 조회하기")
    public void findAllBeansByObjectType(){

        Map<String, Object> beansOfType = ac.getBeansOfType(Object.class);
        for(String beanName: beansOfType.keySet()){
            Object bean= ac.getBean(beanName);
            System.out.println("빈 이름="+beanName+","+"빈 타입= "+beansOfType.get(beanName));
        }

    }


    @Configuration
    static class TestConfig{
        @Bean
        public DiscountPolicy rateDiscountPolicy(){
            return new RateDiscountPolicy();
        }

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



}

ApplicationcontextExtendFindTest 실행결과

+ Recent posts