개발/Spring

[Spring] 스프링 컨테이너의 빈 조회 방법

TutleKing 2022. 8. 25. 14:51

스프링 컨테이너에 등록된 빈들을 조회하게 될 경우 사용하는 방법을 기록하고자 한다. 

 

1. 모든 빈 조회

AnnotationConfigApplicationContext의 인스턴스인 ac로 부터 모든 스프링의 정보를 받기 위해서는 

ac.getBeanDefinitionNames()를 실행하면 빈 이름을 모두 받아와서 ac.getBean(빈 이름) 을 통하면 빈을 조회 할 수 있다.

public class ApplicationContextInfoTest {
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

    @Test
    @DisplayName("모든 빈 출력하기")
    void findAllBean(){
        String[] beanDefinitionNames = ac.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            Object bean = ac.getBean(beanDefinitionName);
            System.out.println("bean = " + bean);
        }
    }
}

실행 결과 : 

모든 빈 조회하기

 

2. 애플리케이션 빈 조회 

애플리케이션 빈이란 사용자가 직접 등록한 빈을 말한다. 

public class ApplicationContextInfoTest {
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
    
    @Test
    @DisplayName("어플리케이션 빈 출력하기")
    void findApplicationBean(){
        String[] beanDefinitionNames = ac.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            BeanDefinition beanDefinition = ac.getBeanDefinition(beanDefinitionName);

            if(beanDefinition.getRole() == BeanDefinition.ROLE_APPLICATION){
                Object bean = ac.getBean(beanDefinitionName);
                System.out.println("name = "+beanDefinitionName+", object = " + bean);
            }
        }
    }
}

ac를 통해 Bean의 definition 정보를 받는다. 

BeanDefinition beanDefinition = ac.getBeanDefinition(beanDefinitionName)

그 이후 defintion 정보를 통해 Role을 조회 한다. 

ROLE_APPLICATION : 직접 등록한 애플리케이션 빈 
ROLE_INFRASTRUCTURE : 스프링이 내부에서 사용하는 빈 

 

실행 결과 : 

 

3.스프링 빈 조회 : 가장 기본적인 조회 방법

ac.getBean(빈 이름, 타입) or ac.getBean(타입) 으로 가능 

public class ApplicationContextBasicFindTest {
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

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

    @Test
    @DisplayName("이름 없이 타입으로만 조회")
    void findBeanByType(){
        MemberService memberService = ac.getBean( MemberService.class);
        assertThat(memberService).isInstanceOf(MemberService.class);
    }
}

실행 결과 : 테스트 성공 

assertThat(인스턴스 명).isInstanceOf(타입) : 해당 인스턴스가 타입과 동일한지 확인 하는 메소드  "org.assertj.core.api.Assertions"의 메소드 

 

4. 동일한 타입이 둘 이상 있을때의 스프링 빈 조회

타입으로 조회시, 같은 타입의 스프링 빈이 둘 이상이면 오류가 발생한다. 이때는 빈 이름을 지정해야한다. 

테스트를 위해 SameBeanConfig 클래스를 만들어 MemberRepository 의 타입을 2개 생성하였다. 

public class ApplicationContextSameBeanFindTest {
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SameBeanConfig.class);

    @Test
    @DisplayName("타입으로 조회시 같은 타입이 둘 이상 있으면, 중복 오류 발생")
    void findBeanByTypeDuplicate(){
        Assertions.assertThrows(NoUniqueBeanDefinitionException.class,
                () -> ac.getBean(MemberRepository.class));

    }

    @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);
        org.assertj.core.api.Assertions.assertThat(beansOfType.size()).isEqualTo(2);
    }

    @Configuration
    static class SameBeanConfig {

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

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

동일한 타입의 빈이 있으면 "NoUniqueBeanDefinitionException"이 발생한다. 그렇기에 ac.getBeansOfType(타입)을 통해 해당 타입으로 구성된 빈의 정보를 Map 형식으로 받아 출력한다. 

 

실행결과 : 

동일한 타입의 빈이 2개 있을때 조회 한 결과

 

 

4. 상속관계에 있는 스프링 빈 조회 

위의 동일한 타입의 빈이 여러개 있을때와 동일하게 부모 타입으로 조회시 자식이 여러개 있다면 중복오류가 발생하게 된다. 그럴경우 빈의 이름을 지정하면 조회가 가능해진다. 

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


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

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

}

실행 결과 : 테스트 성공

 

5. 상속관계에 있는 모든 스프링 빈 조회

부모의 타입으로 조회 하거나 혹은 Object로 조회하여도 모두 조회 가능하다. 

Object를 조회할 경우, 스프링 내부의 빈까지 모두 조회 되어 버린다.

public class ApplicationContextExtendsFindTest {

    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.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();
        }
    }

}

실행 결과 : 

부모 타입으로 모든 빈 조회하기
Object를 통해 모든 빈 조회

 

반응형