public class ApplicationContextBasicFindTest { // 스프링 빈 조회 기능을 테스트하는 클래스 정의
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class); // 스프링 컨테이너를 초기화하고, AppConfig 클래스의 설정 정보를 로딩
@Test // JUnit 테스트 메소드임을 나타내는 어노테이션
@DisplayName("빈 이름으로 조회") // 테스트의 이름을 설정
void findBeanByName() { // 빈 이름으로 조회하는 테스트 메서드
MemberService memberService = ac.getBean("memberService", MemberService.class); // "memberService" 이름의 빈을 MemberService 타입으로 조회
assertThat(memberService).isInstanceOf(MemberService.class); // 조회된 빈이 MemberService 타입인지 검증
}
@Test
@DisplayName("이름 없이 타입으로만 조회")
void findBeanByType() { // 이름 없이 타입만으로 빈을 조회하는 테스트 메서드
MemberService memberService = ac.getBean(MemberService.class); // MemberService 타입의 빈을 조회
assertThat(memberService).isInstanceOf(MemberService.class); // 조회된 빈이 MemberService 타입인지 검증
}
@Test
@DisplayName("구체 타입으로 조회") // 구체적인 클래스 타입으로 빈을 조회하는 테스트
void findBeanByName2() {
MemberService memberService = ac.getBean("memberService", MemberServiceImpl.class); // "memberService" 이름의 빈을 MemberServiceImpl 타입으로 조회
assertThat(memberService).isInstanceOf(MemberServiceImpl.class); // 조회된 빈이 MemberServiceImpl 타입인지 검증
}
@Test
@DisplayName("빈 이름으로 조회")
void findBeanByNameX() { // 존재하지 않는 빈 이름으로 조회 시 예외를 테스트하는 메서드
//ac.getBean("xxxxx", MemberService.class);
assertThrows(NoSuchBeanDefinitionException.class,
() -> ac.getBean("xxxxx", MemberService.class)); // "xxxxx" 이름의 빈이 존재하지 않을 경우 예외가 발생하는지 검증
}
}
class ApplicationContextInfoTest { // ApplicationContextInfoTest 클래스 선언
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class); // Spring 컨테이너 초기화. AppConfig 클래스를 설정 정보로 사용
@Test // JUnit 테스트 케이스 선언
@DisplayName("모든 빈 출력하기") // 테스트 케이스의 이름을 지정
void findAllBean() { // findAllBean 테스트 메서드 시작
String[] beanDefinitionNames = ac.getBeanDefinitionNames(); // 컨테이너에 등록된 모든 빈의 이름을 가져옴
for (String beanDefinitionName : beanDefinitionNames) { // 빈 이름 목록을 순회
Object bean = ac.getBean(beanDefinitionName); // 빈 이름에 해당하는 빈 객체를 조회
System.out.println("name = " + beanDefinitionName + " object =" + bean); // 빈 이름과 빈 객체를 출력
}
}
@Test // 또 다른 JUnit 테스트 케이스 선언
@DisplayName("애플리케이션 빈 출력하기") // 테스트 케이스의 이름을 지정
void findApplicationBean() { // findApplicationBean 테스트 메서드 시작
String[] beanDefinitionNames = ac.getBeanDefinitionNames(); // 컨테이너에 등록된 모든 빈의 이름을 가져옴
for (String beanDefinitionName : beanDefinitionNames) { // 빈 이름 목록을 순회
BeanDefinition beanDefinition = // 빈의 정의를 조회
ac.getBeanDefinition(beanDefinitionName);
//Role ROLE_APPLICATION: 직접 등록한 애플리케이션 빈
//Role ROLE_INFRASTRUCTURE: 스프링이 내부에서 사용하는 빈
if (beanDefinition.getRole() == BeanDefinition.ROLE_APPLICATION) { // 빈의 역할이 애플리케이션 빈인지 확인
Object bean = ac.getBean(beanDefinitionName); // 빈 이름에 해당하는 빈 객체를 조회
System.out.println("name=" + beanDefinitionName + " object=" + bean); // 빈 이름과 빈 객체를 출력
}
}
}
}
getBean()
getBean() 메서드는 스프링의 ApplicationContext 인터페이스에서 제공하는 메서드로, 스프링 컨테이너에서 빈(bean)을 검색할 때 사용된다. 이 메서드는 스프링이 관리하는 빈의 인스턴스를 런타임 시에 가져올 수 있도록 해준다. 사용자는 getBean()을 호출하여 빈의 인스턴스를 직접 요청하고, 스프링 컨테이너는 해당 빈을 찾아 반환한다.
getBean() 메서드의 주요 형태와 사용법
- 이름으로 빈 조회: getBean(String name) 형태로 사용하며, 빈의 이름을 문자열로 지정하여 해당 빈을 검색한다. 반환 타입은 Object이므로, 적절한 타입으로 캐스팅이 필요하다.
- 이름과 타입으로 빈 조회: getBean(String name, Class<T> requiredType)을 사용하면, 이름과 함께 요구하는 타입을 명시할 수 있다. 이 메서드는 요청한 타입의 빈을 반환하므로, 타입 캐스팅이 필요 없다.
- 타입만으로 빈 조회: getBean(Class<T> requiredType)은 주어진 타입의 빈을 찾아 반환한다. 같은 타입의 빈이 여러 개 있을 경우, NoUniqueBeanDefinitionException이 발생할 수 있다.
예외 처리
- NoSuchBeanDefinitionException: 요청한 이름이나 타입의 빈이 컨테이너에 존재하지 않을 때 발생한다.
- NoUniqueBeanDefinitionException: 요청한 타입의 빈이 여러 개 존재하고, 구체적인 빈을 지정하지 않았을 때 발생한다.
- BeanNotOfRequiredTypeException: 요청한 빈이 존재하지만, 요청한 타입과 일치하지 않을 때 발생한다.
getBean() 사용 시 고려사항
getBean() 메서드는 유연성과 직접적인 제어를 제공하지만, 스프링의 의존성 주입(Dependency Injection) 기능을 통해 자동으로 빈을 주입받는 것이 권장된다. getBean()을 직접 사용하는 것은 코드의 결합도를 높일 수 있으며, 의존성 주입의 장점을 누리기 어렵게 만든다. 따라서 테스트 코드나 특정 상황에서 필수적인 경우에 한정하여 사용하는 것이 좋다.
public class ApplicationContextSameBeanFindTest { // 테스트 클래스 정의
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SameBeanConfig.class); // 스프링 컨테이너를 초기화하고 SameBeanConfig 설정 클래스를 사용
@Test // JUnit 테스트 메서드임을 나타내는 어노테이션
@DisplayName("타입으로 조회시 같은 타입이 둘 이상 있으면, 중복 오류가 발생한다")
void findBeanByTypeDuplicate() { // 동일한 타입의 빈이 여러 개 있을 때 발생하는 예외를 테스트하는 메서드
//MemberRepository bean = ac.getBean(MemberRepository.class);
assertThrows(NoUniqueBeanDefinitionException.class, () ->
ac.getBean(MemberRepository.class)); // 동일한 타입의 빈을 조회할 때 예외 발생을 확인
}
@Test
@DisplayName("타입으로 조회시 같은 타입이 둘 이상 있으면, 빈 이름을 지정하면 된다")
void findBeanByName() { // 동일한 타입의 빈 중 특정 이름으로 빈을 조회하는 메서드
MemberRepository memberRepository = ac.getBean("memberRepository1",
MemberRepository.class); // "memberRepository1" 이름의 빈을 조회
assertThat(memberRepository).isInstanceOf(MemberRepository.class); // 조회된 빈이 MemberRepository 타입인지 확인
}
@Test
@DisplayName("특정 타입을 모두 조회하기")
void findAllBeanByType() { // 동일한 타입의 모든 빈을 조회하는 메서드
Map<String, MemberRepository> beansOfType =
ac.getBeansOfType(MemberRepository.class); // MemberRepository 타입의 모든 빈을 Map 형태로 가져옴
for (String key : beansOfType.keySet()) { // Map의 모든 키(빈의 이름)를 순회
System.out.println("key = " + key + " value = " +
beansOfType.get(key)); // 각 키와 해당 키의 빈 객체를 출력
}
System.out.println("beansOfType = " + beansOfType); // 전체 Map 출력
assertThat(beansOfType.size()).isEqualTo(2); // Map의 크기가 2인지 확인 (두 개의 빈이 있는지)
}
@Configuration // 스프링 설정 클래스임을 나타내는 어노테이션
static class SameBeanConfig { // 테스트용 설정 클래스
@Bean // 스프링 빈으로 등록함을 나타내는 어노테이션
public MemberRepository memberRepository1() { // "memberRepository1" 이름의 빈 등록 메서드
return new MemoryMemberRepository(); // MemoryMemberRepository 인스턴스 반환
}
@Bean // 다른 빈을 등록
public MemberRepository memberRepository2() { // "memberRepository2" 이름의 빈 등록 메서드
return new MemoryMemberRepository(); // MemoryMemberRepository 인스턴스 반환
}
}
}
public class ApplicationContextExtendsFindTest { // 스프링 빈을 특정 타입으로 조회하는 테스트 클래스 정의
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class); // AppConfig 설정을 사용하여 스프링 컨텍스트를 초기화
@Test // JUnit 테스트 메소드 선언
@DisplayName("부모 타입으로 조회시, 자식이 둘 이상 있으면, 중복 오류가 발생한다")
void findBeanByParentTypeDuplicate() { // 부모 타입으로 빈을 조회할 때 같은 타입의 자식 빈이 두 개 이상 있으면 발생하는 오류를 테스트
assertThrows(NoUniqueBeanDefinitionException.class, () ->
ac.getBean(DiscountPolicy.class)); // DiscountPolicy 타입의 빈을 조회하려 할 때 예외 발생 여부 확인
}
@Test
@DisplayName("부모 타입으로 조회시, 자식이 둘 이상 있으면, 빈 이름을 지정하면 된다")
void findBeanByParentTypeBeanName() { // 부모 타입으로 조회시 빈 이름을 지정하여 정확한 빈을 조회
DiscountPolicy rateDiscountPolicy = ac.getBean("rateDiscountPolicy",
DiscountPolicy.class); // "rateDiscountPolicy" 이름으로 DiscountPolicy 타입의 빈을 조회
assertThat(rateDiscountPolicy).isInstanceOf(RateDiscountPolicy.class); // 조회된 빈이 RateDiscountPolicy의 인스턴스인지 확인
}
@Test
@DisplayName("특정 하위 타입으로 조회")
void findBeanBySubType() { // 특정 하위 타입으로 빈을 직접 조회
RateDiscountPolicy bean = ac.getBean(RateDiscountPolicy.class); // RateDiscountPolicy 타입의 빈을 직접 조회
assertThat(bean).isInstanceOf(RateDiscountPolicy.class); // 조회된 빈이 RateDiscountPolicy 타입인지 확인
}
@Test
@DisplayName("부모 타입으로 모두 조회하기")
void findAllBeanByParentType() { // 부모 타입의 모든 빈을 조회
Map<String, DiscountPolicy> beansOfType =
ac.getBeansOfType(DiscountPolicy.class); // DiscountPolicy 타입의 모든 빈을 Map으로 조회
assertThat(beansOfType.size()).isEqualTo(2); // 조회된 빈의 수가 2개인지 확인
for (String key : beansOfType.keySet()) { // 조회된 빈의 이름과 객체를 출력
System.out.println("key = " + key + " value=" +
beansOfType.get(key));
} }
@Test
@DisplayName("부모 타입으로 모두 조회하기 - Object")
void findAllBeanByObjectType() { // Object 타입으로 모든 빈을 조회
Map<String, Object> beansOfType = ac.getBeansOfType(Object.class); // Object 타입의 모든 빈을 Map으로 조회
for (String key : beansOfType.keySet()) { // 조회된 빈의 이름과 객체를 출력
System.out.println("key = " + key + " value=" +
beansOfType.get(key));
} }
@Configuration // 스프링 설정 클래스 선언
static class TestConfig { // 테스트용 설정 클래스
@Bean // 스프링 빈으로 등록
public DiscountPolicy rateDiscountPolicy() { // "rateDiscountPolicy" 이름의 DiscountPolicy 타입 빈을 등록
return new RateDiscountPolicy(); // RateDiscountPolicy 객체 반환
}
@Bean // 또 다른 스프링 빈으로 등록
public DiscountPolicy fixDiscountPolicy() { // "fixDiscountPolicy" 이름의 DiscountPolicy 타입 빈을 등록
return new FixDiscountPolicy(); // FixDiscountPolicy 객체 반환
}
}
}