[Spring] 스프링 빈 조회하기

imcool2551·2022년 1월 26일
0

Spring

목록 보기
3/15
post-thumbnail

본 글은 인프런 김영한님의 스프링 완전 정복 로드맵을 기반으로 정리했습니다.

빈 조회


AnnotationConfigApplicationContext@Configuration이 붙은 설정 클래스를 통해 객체를 스프링 컨테이너의 관리를 받는 빈으로 만들 수 있다.

이 글에서는 컨테이너에 등록된 빈들을 가져오는 방법을 알아보도록 하겠다.

기본 조회


class BasicFindTest {

    ApplicationContext ac =
            new AnnotationConfigApplicationContext(AppConfig.class);

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

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

    @Test
    @DisplayName("구체타입으로 조회 (유연성 떨어짐)")
    void findBeanByName2() {
        CreateOrderService createOrderService =
                ac.getBean("createOrderService", CreateOrderServiceImpl.class);
        assertThat(createOrderService).isInstanceOf(CreateOrderServiceImpl.class);
    }

    @Test
    @DisplayName("조회 실패")
    void findBeanFail() {
        assertThrows(NoSuchBeanDefinitionException.class,
                () -> ac.getBean("xxxxx", MemberService.class));
    }

    @Configuration
    static class AppConfig {
        @Bean
        public CreateOrderService createOrderService() {
            return new CreateOrderServiceImpl();
        }
    }
}

T getBean(Class<T> requiredType) 혹은 T getBean(String name, Class<T> requiredType) 으로 컨테이너에 등록된 빈을 가져올 수 있다. 타입 파라미터로 구체클래스를 받을 수 있긴 하지만 구체 타입은 유연성이 떨어지기 때문에 되도록 추상 타입(인터페이스)로 조회하도록 하자. 빈 조회에 실패하면 NoSuchBeanDefinition 익셉션이 발생한다.

동일한 타입의 빈 조회


class SameBeanTest {

    ApplicationContext ac =
            new AnnotationConfigApplicationContext(SameBeanConfig.class);

    @Test
    @DisplayName("타입이 둘 이상이면 중복 오류 발생")
    void findBeanByTypeNoUnique() {
        assertThrows(NoUniqueBeanDefinition.class,
                () -> ac.getBean(CreateOrderService.class));
    }

    @Test
    @DisplayName("타입과 이름으로 조회하면 중복 오류 발생X")
    void findBeanByTypeAndName() {
        CreateOrderService createOrderService =
                ac.getBean("createOrderService1", CreateOrderService.class);
        assertThat(createOrderService).isInstanceOf(CreateOrderServiceImpl.class);
    }

    @Test
    @DisplayName("특정 타입의 빈 모두 조회")
    void findAllBeansByType() {
        Map<String, CreateOrderService> beans =
                ac.getBeansOfType(CreateOrderService.class);

        assertThat(beans.size()).isEqualTo(2);
    }

    @Configuration
    static class SameBeanConfig {
        @Bean
        public CreateOrderService createOrderService1() {
            return new CreateOrderServiceImpl();
        }

        @Bean
        public CreateOrderService createOrderService2() {
            return new CreateOrderServiceImpl();
        }
    }
}

CreateOrderService는 인터페이스, CreateOrderServiceImpl은 구체 클래스다. 컨테이너가 같은 타입(인터페이스)의 빈 객체를 2개 이상 가지고 있을 때 ac.getBean(객체 타입)으로 빈을 조회하면 NoUniqueBeanDefinition 익셉션이 발생한다. 논리적으로 이는 당연한 결과다. 컨테이너에게 특정 타입의 빈을 요구하는데 ac.getBean()은 한 개의 객체만 반환해야하므로 2개 이상의 빈중 어떤 빈을 반환해주어야 할지 모르기 때문에 예외가 발생한다.

부모 클래스 타입으로 빈을 조회해도 자식 타입이 모두 조회된다. 따라서, 부모 타입으로 등록된 빈이 2개이상일때 빈 이름 없이 부모 타입으로만 조회해도 NoUniqueBeanDefinition 익셉션이 발생한다. 객체지향 관점에서 child class is a kind of parent class 관계가 성립하기 때문에 이 또한 합리적인 동작이다.

동일한 타입의 빈이 둘 이상일 경우 ac.getBean(빈 이름, 객체 타입) 처럼 타입과 이름으로 조회하면 NoUniqueBeanDefinition 익셉션 없이 빈을 가져올 수 있다.

특정 타입의 빈을 모두 가져오고 싶으면 Map<String, T> getBeansOfType(@Nullable Class<T> type) 을 사용하면 된다. 빈 이름, 빈 객체를 각각 키, 값으로 하는 맵을 반환해준다.

마치며


참고로 이 글에서 처럼 스프링 컨테이너에서 직접 빈을 조회할 일은 거의 없지만 스프링 컨테이너가 빈을 어떻게 관리하는지 이해하기 위해서 알아보았다.

profile
아임쿨

0개의 댓글