스프링 컨테이너에서 스프링 빈을 찾는 가장 기본적인 방법은 getBean() 메서드를 사용하는 것이다.
ac.getBean(빈이름, 타입);
// ex
ac.getBean(memberService, MemberService.class);
getBean() 메서드를 다양하게 사용해보는 테스트 코드를 작성해본다.
기본적인 빈 조회
beanfind 패키지에 ApplicationContextBasicFindTest 클래스를 새로 생성하고, 스프링 컨테이너를 하나 생성한다.
class ApplicationContextBasicFindTest {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
}
빈 이름과 타입으로 조회
getBean(빈이름, 빈 타입)의 형태로 호출한다.
이에 대한 테스트 메서드를 다음과 같이 작성할 수 있다.
findBeanByName()
@Test
@DisplayName("빈 이름으로 조회")
void findBeanByName() {
MemberService memberService = ac.getBean("memberService", MemberService.class);
System.out.println("memberService = " + memberService);
System.out.println("memberService.getClass() = " + memberService.getClass());
assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}
빈 이름과 빈 타입(.class)를 전달하면 해당하는 빈을 리턴한다.
테스트로는 getBean()이 리턴한 MemberService 구현체가 MemberServiceImpl 타입의 인스턴스인지 확인한다.
만일 해당하는 빈이 없을 경우, NoSuchBeanDefinitionException 예외를 발생시킨다.
+) getClass()
해당 참조 변수가 가리키는 인스턴스의 실제 타입을 리턴한다.
+) assertThat()
assertj의 Assertions에 정의된 메서드이며, 해당 패키지를 static import했다.
빈 이름 없이 타입으로만 조회
빈 이름을 지정하지 않고 타입(.class)만으로도 빈을 조회할 수 있다.
getBean(타입) 의 형태로 호출한다.
getBean()에 타입을 전달하면 그 타입에 해당하는 스프링 빈을 리턴한다.
한가지 주의해야 할 점은, 빈 타입은 각 빈을 구분하는 key가 아니라는 점이다.
각 빈은 빈 이름을 통해 구분하며, 같은 타입의 빈은 여러개 있을 수 있다.
getBean(타입)의 형태로 조회할 때 대상 타입의 빈이 여러개 있으면 오류가 발생한다.
findBeanByType()
@Test
@DisplayName("이름 없이 타입으로만 조회")
void findBeanByType() {
// 이름 없이 타입으로만 조회 가능하다. MemberService.class를 전달하면 MemberService 타입의 빈만 조회 가능.
// 근데 MemberService 타입의 빈이 여러개면 어떡하지?
MemberService memberService = ac.getBean(MemberService.class);
System.out.println("memberService = " + memberService);
System.out.println("memberService.getClass() = " + memberService.getClass());
assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}
구현체 타입으로 조회
getBean(빈 이름, 구현체 타입)의 형태로 호출한다.
혹은 getBean(구현체 타입)의 형태로 호출한다.
인터페이스 타입이 아닌 스프링 빈의 구현체 타입으로 조회하는 방식이다.
스프링 빈을 등록할때 빈의 구현체의 정보도 같이 저장되기 때문이다.
다만 이러한 조회 방식은 구현에 의존하게 되고, 변경에 유연하지 않으므로 지양하는 것이 좋다.
findByBeanName2()
@Test
@DisplayName("구현체 타입으로 조회")
void findBeanByName2() {
MemberServiceImpl memberService = ac.getBean("memberService", MemberServiceImpl.class);
System.out.println("memberService = " + memberService);
System.out.println("memberService.getClass() = " + memberService.getClass());
assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}
findByBeanName3()
@Test
@DisplayName("구현체 타입으로만 조회")
void findBeanByName4() {
MemberServiceImpl memberService = ac.getBean(MemberServiceImpl.class);
System.out.println("memberService = " + memberService);
System.out.println("memberService.getClass() = " + memberService.getClass());
assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}
빈 이름으로 조회
빈의 이름만으로도 조회할 수 있다.
빈 이름은 빈을 구분하는 key이므로, 빈 이름만으로도 해당 빈을 조회할 수 있다.
다만, 타입을 지정하지 않았기 때문에 리턴 타입은 Object다.
getBean(빈 이름)의 형태로 호출한다.
findBeanByName4()
@Test
@DisplayName("빈 타입 없이 이름만으로 조회")
void findBeanByName3() {
Object bean = ac.getBean("memberService");
System.out.println("bean = " + bean);
System.out.println("bean.getClass() = " + bean.getClass());
assertThat(bean).isInstanceOf(MemberService.class);
}
빈 조회 실패 케이스
기본적으로 테스트 메서드를 작성하고 있기 때문에, 실패 케이스에 대한 검증도 필요하다.
getBean()을 통해 존재하지 않는 빈을 조회하면 NoSuchBeanDefinitionException 예외가 발생한다.
존재하지 않는 빈("xxxxx")을 조회하고, 해당 예외가 제대로 발생하는지 테스트해보자.
@Test
@DisplayName("빈 이름으로 조회 실패")
void findBeanByNameX() {
// 아래 코드가 실행될 때 예외가 제대로 터지는지도 테스트해야 한다.
// ac.getBean("xxxxx", MemberService.class);
// 이딴 빈은 없다. 그렇다면?
// MemberService noneBean = ac.getBean("xxxxx", MemberService.class);
// 해당 람다식을 실행하면 NoSuchBeanDefinitionException이 터지는지 테스트하는 코드.
// 해당 예외가 발생하면 테스트 통과.
assertThrows(NoSuchBeanDefinitionException.class,
() -> ac.getBean("xxxxx", MemberService.class));
}
assertThrows()는 jupyter의 Assertion에 정의된 메서드이며, 해당 패키지를 static import 했다.
assertThrows()에 예외에 대한 정보(.class)와 예외를 발생시키는 코드를 람다식으로 전달하면, 해당 코드가 그 예외를 발생시키는지 테스트할 수 있다.
메서드를 실행하면 테스트 통과라고만 뜨고 달리 출력되는 것은 없다.
테스트 클래스 전체 코드
beanfind.ApplicaionContextBasicFindTest.java
package hdxian.hdxianspringcore.beanfind;
import hdxian.hdxianspringcore.AppConfig;
import hdxian.hdxianspringcore.member.MemberService;
import hdxian.hdxianspringcore.member.MemberServiceImpl;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import static org.assertj.core.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.*;
class ApplicationContextBasicFindTest {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
@Test
@DisplayName("빈 이름으로 조회")
void findBeanByName() {
MemberService memberService = ac.getBean("memberService", MemberService.class);
System.out.println("memberService = " + memberService);
System.out.println("memberService.getClass() = " + memberService.getClass());
assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}
@Test
@DisplayName("이름 없이 타입으로만 조회")
void findBeanByType() {
// 이름 없이 타입으로만 조회 가능하다. MemberService.class를 전달하면 MemberService 타입의 빈만 조회 가능.
// 근데 MemberService 타입의 빈이 여러개면 어떡하지?
MemberService memberService = ac.getBean(MemberService.class);
System.out.println("memberService = " + memberService);
System.out.println("memberService.getClass() = " + memberService.getClass());
assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}
@Test
@DisplayName("구현체 타입으로 조회")
void findBeanByName2() {
MemberServiceImpl memberService = ac.getBean("memberService", MemberServiceImpl.class);
System.out.println("memberService = " + memberService);
System.out.println("memberService.getClass() = " + memberService.getClass());
assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}
@Test
@DisplayName("구현체 타입으로만 조회")
void findBeanByName3() {
MemberServiceImpl memberService = ac.getBean(MemberServiceImpl.class);
System.out.println("memberService = " + memberService);
System.out.println("memberService.getClass() = " + memberService.getClass());
assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}
@Test
@DisplayName("빈 이름으로 조회 실패")
void findBeanByNameX() {
// 아래 코드가 실행될 때 예외가 제대로 터지는지도 테스트해야 한다.
// ac.getBean("xxxxx", MemberService.class);
// 이딴 빈은 없다. 그렇다면?
// MemberService noneBean = ac.getBean("xxxxx", MemberService.class);
// 해당 람다식을 실행하면 NoSuchBeanDefinitionException이 터지는지 테스트하는 코드.
// 해당 예외가 발생하면 테스트 통과.
assertThrows(NoSuchBeanDefinitionException.class,
() -> ac.getBean("xxxxx", MemberService.class));
}
@Test
@DisplayName("빈 타입 없이 이름만으로 조회")
void findBeanByName4() {
Object bean = ac.getBean("memberService");
System.out.println("bean = " + bean);
System.out.println("bean.getClass() = " + bean.getClass());
assertThat(bean).isInstanceOf(MemberService.class);
}
}
'[inflearn] 스프링 핵심 원리 - 기본편 > 섹션 4 - 스프링 컨테이너와 스프링 빈' 카테고리의 다른 글
4-6. BeanFactory와 ApplicationContext (2) | 2023.11.27 |
---|---|
4-5. 스프링 빈 상속 관계 (0) | 2023.11.18 |
4-4. 동일 타입 빈 조회 (0) | 2023.11.18 |
4-2. 모든 빈 조회, 어플리케이션 빈 조회 (0) | 2023.11.07 |
4-1. 스프링 컨테이너 생성 (0) | 2023.11.07 |