본 글은 인프런 김영한님의 스프링 완전 정복 로드맵을 기반으로 정리했습니다.
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)
을 사용하면 된다. 빈 이름, 빈 객체를 각각 키, 값으로 하는 맵을 반환해준다.
참고로 이 글에서 처럼 스프링 컨테이너에서 직접 빈을 조회할 일은 거의 없지만 스프링 컨테이너가 빈을 어떻게 관리하는지 이해하기 위해서 알아보았다.