
의존관계 주입을 하여 스프링 컨테이너에 등록이 된 스프링 빈을 조회하는 방법에 대해 정리하겠다.
기술적인 내용들이라 이런 것이 있구나, 이렇게 활용하면 되는구나 정도 짚고 넘어가겠다.
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("name = " + beanDefinitionName + "object = " + bean );
}
}
@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 );
}
}
}
}
별 다른 조건없이 출력을 하게 될 경우, 스프링 내부에서 사용하는 빈이 모두 함께 출력이 된다.
BeanDefinition 조건을 걸어 직접 등록한 애플리케이션 빈, 스프링이 내부에서 사용하는 빈을 구분하여 출력할 수 있다.
참고
ROLE_APPLICATION: 직접 등록한 애플리케이션 빈
ROLE_INFRASTRUCTURE: 스프링이 내부에서 사용하는 빈
그런데, 이렇게 실제로 다 출력해서 확인할 일은 별로 없다.
한번에 다 출력하고, 일일이 눈으로 확인해보는 것은 비효율적이다.
원하는 빈을 선택하여 조회하는 방법이 있다.
.getBean(빈이름, 타입)
.getBean(타입)
해당 메서드로 스프링 컨테이너에 등록된 빈을 꺼내올 수 있다.
조회 대상 빈이 없으면 예외 발생한다.
NoSuchBeanDefinitionException: No bean named 'xxxxx' available
오류명을 알아야 테스트 코드를 작성할 때, 실패 케이스에 대한 테스트가 가능하다.
public class ApplicationContextBasicFindTest {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
@Test
@DisplayName("빈 이름으로 조회")
void findBeanByName() {
MemberService memberService = ac.getBean("memberService", MemberService.class);
assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}
@Test
@DisplayName("이름 없이 타입으로만 조회")
void findBeanByType() {
MemberService memberService = ac.getBean(MemberService.class);
assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}
@Test
@DisplayName("구체 타입으로 조회")
void findBeanByName2() {
MemberService memberService = ac.getBean("memberService", MemberServiceImpl.class);
assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}
@Test
@DisplayName("빈 이름으로 조회X")
void findBeanByNameX() {
// ac.getBean("xxxxx", MemberService.class);
assertThrows(NoSuchBeanDefinitionException.class,
() -> ac.getBean("xxxxx", MemberService.class));
}
}
빈 이름으로 조회해서 원하는 구현 객체로 빈이 생성되었는 지 확인할 수 있다.
즉, 인터페이스를 조회를 하면 검증의 대상으로 인터페이스의 구현체가 대상이 된다.
직접 구현체를 조회해도 된다.
다만, 구현체를 직접 등록하면 역할과 구현을 구분하고 역할에 의존하지 않기 때문에 유연성이 떨어진다.
가끔 필요할 때가 있으니 가능하다는 정도로만 알고 있자.
실패 케이스는 junit Assertions의 assertThrow를 활용, 람다를 활용하여 예외가 터지는 케이스를 정의한다.
타입으로 조회시 같은 타입의 빈이 둘이상이면 오류가 발생한다. 이 때는 빈 이름을 지정한다.
.getBeanOfType()을 사용하면 해당 타입의 모든 빈을 조회할 수 있다.
만약 타입으로만 빈을 조회했으나, 같은 타입의 빈이 둘 이상이면 NoUniqueBeanDefinitionException 에러가 발생한다.
public class ApplicationContextSameBeanFindTest {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SameBeanConfig.class);
@Test
@DisplayName("타입으로 조회시 같은 타입이 둘 이상 있으면, 중복 오류가 발생한다")
void findBeanByTypeDuplicate() {
assertThrows(NoUniqueBeanDefinitionException.class,
() -> ac.getBean(MemberRepository.class));
}
@Test
@DisplayName("타입으로 조회시 같은 타입이 둘 이상 있으면, 빈 이름을 지정하면 된다")
void findBeanByType() {
MemberRepository memberRepository = ac.getBean("memberRepository1", MemberRepository.class);
assertThat(memberRepository).isInstanceOf(MemoryMemberRepository.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);
assertThat(beansOfType).hasSize(2);
}
@Configuration
static class SameBeanConfig {
@Bean
public MemberRepository memberRepository1() {
return new MemoryMemberRepository();
}
@Bean
public MemberRepository memberRepository2() {
return new MemoryMemberRepository();
}
}
}
특정 타입의 빈을 모두 조회하는 경우가 있는데, 이는 @Autowired에서 자동으로 의존관계 주입과 관련이 있다.