이번 글에는 실제로 스프링 프레임워크에서 제공하는 IoC, DI, 통칭 스프링 컨테이너를 사용해보겠습니다. 또한 빈의 조회에 관련된 내용을 알아보겠습니다.
먼저 스프링 빈들을 설정하기 위한 간단한 예시를 보여드리겠습니다.
@Configuration // <- 빈(Bean)들을 설정 정보를 알리기 위한 어노테이션
public class MyConfiguration {
@Bean // 직접 등록한 첫번째 빈
public MyService myService() {
return new MyServiceImpl();
}
@Bean // 직접 등록한 두번째 빈
public MyController myController(MyService myService) {
return new MyControllerImpl(myService); // 생성자에 빈으로 등록되어 있는 myService메소드 호출
}
}
스프링 프레임워크의 스프링 컨테이너는 @Configuration이 붙은 클래스를 설정 정보로 사용.
스프링 컨테이너에서 처리 할 수 있는 하나 이상의 빈(Bean) 메소드를 클래스가 정의하는데 나타냄.
@Configuration을 클래스를 표시함으로써 스프링(Spring)은 자동으로 클래스에 대한 프록시를 생성 후 이를 사용해 빈(Bean)을 인스턴스화하고 구성하는 메소드를 호출.
@Configuration은 상황에 따라 @Bean, @ComponentScan, @Import, @Profile와 같은 어노테이션과 함께 사용.
스프링 빈은 스프링 컨테이너에 의해 관리되는 객체
@Configuration이 붙은 클래스에 @Bean이라 적힌 메소드를 모두 호출해 반환된 객체를 스프링 컨테이너에 등록하는데 이렇게 스프링 컨테이너에 등록된 객체를 스프링 빈이라 함.
스프링 빈의 이름은 기본적으로 @Bean이 붙은 메소드명으로 등록됨.
관련예시
// 스프링 컨테이너 생성
ApplicationContext ac = new AnnotationConfigApplicationContext(MyConfiguration.class);
참고: 스프링 컨테이너의 형태는 BeanFactory와 ApplicationContext입니다. 이 두 형태의 차이점은
추후 업로드하겠습니다.
스프링 컨테이너에 등록된 모든 빈 출력
ApplicationContext ac = new AnnotationConfigApplicationContext(MyConfiguration.class);
String[] beanNames = ac.getBeanDefinitionNames();// 스프링 빈의 이름들을 배열로 반환
for (String beanName : beanNames) {
Object bean = ac.getBean(beanName); // 빈 이름으로 객체(인스턴스)를 조회
System.out.println("beanName = " + beanName + " || Object = " + bean);
스프링 컨테이너에 등록된 모든 빈을 조회하려면 ApplicationContext 또는 BeanFactory 인터페이스의 getBeanDefinitionNames() 메소드를 사용할 수 있습니다. 이 메서드는 컨테이너에 정의된 모든 빈 이름의 배열을 반환합니다.
스프링 빈 조회 - 기본
ApplicationContext ac = new AnnotationConfigApplicationContext(MyConfiguration.class);
@Test
@DisplayName("빈 이름으로 조회")
void findBeanByName() {
MyService myService = ac.getBean("myService", MyService.class);
assertThat(myService).isInstanceOf(MyServiceImpl.class);
}
@Test
@DisplayName("이름 없이 타입으로 조회")
void findBeanByType() {
MyService myService = ac.getBean(MyService.class);
assertThat(myService).isInstanceOf(MyServiceImpl.class);
}
@Test
@DisplayName("구체 타입으로 조회")
void findBeanByName2() {
MyService myService = ac.getBean("myService", MyServiceImpl.class);
assertThat(myService).isInstanceOf(MyServiceImpl.class);
스프링 빈 조회 - 동일한 타입 중복일 때
이름 없이 타입으로 조회 시 같은 타입의 빈이 둘 이상이면 NoUniqueBeanDefinitionException의 예외가 발생한다.
ApplicationContext ac = new AnnotationConfigApplicationContext(SameBeanConfiguration.class);
@Test
@DisplayName("타입으로 조회 시 같은 타입이 중복이면 NoUniqueBeanDefinitionException예외 발생")
void findBeanByTypeDuplication() {
Assertions.assertThrows(NoUniqueBeanDefinitionException.class, () -> ac.getBean(MyService.class));
}
@Test
@DisplayName("타입으로 조회 시 같은 타입이 중복이면 빈 이름으로 지정해서 찾기")
void findBeanByName() {
MyService myService1 = ac.getBean("myService1", MyService.class);
org.assertj.core.api.Assertions.assertThat(myService1).isInstanceOf(MyServiceImpl.class);
}
@Configuration
static class SameBeanConfiguration {
@Bean
public MyService myService1() {
return new MyServiceImpl();
}
@Bean
public MyService myService2() {
return new MyServiceImpl();
}
}
스프링 빈 조회 - 특정 타입의 빈 조회
특정 유형타입의 빈을 조회하려면 ApplicationContext 또는 BeanFactory 인터페이스의 getBeansOfType() 메서드를 사용할 수 있습니다. 이 메서드는 클래스 또는 인터페이스를 매개변수로 사용하고 해당 클래스 또는 인터페이스를 구현하거나 확장하는 모든 빈의 맵을 반환합니다.
@Test
@DisplayName("특정 타입 빈 모두 출력")
void findAllBeanPointType() {
Map<String, MyService> beansOfType = ac.getBeansOfType(MyService.class); // MyService타입의 빈만 찾기
for (String key : beansOfType.keySet()) {
System.out.println("key = " + key + " || value = " + beansOfType.get(key));
}
System.out.println("beansOfType = " + beansOfType);
assertThat(beansOfType.size()).isEqualTo(2);
}