스프링 컨테이너와 IoC, DI, Bean 2편

ILLION·2023년 3월 7일
0

Spring Framework

목록 보기
2/5

이번 글에는 실제로 스프링 프레임워크에서 제공하는 IoC, DI, 통칭 스프링 컨테이너를 사용해보겠습니다. 또한 빈의 조회에 관련된 내용을 알아보겠습니다.

1. 스프링 빈(Bean) 설정 방법

먼저 스프링 빈들을 설정하기 위한 간단한 예시를 보여드리겠습니다.

@Configuration // <- 빈(Bean)들을 설정 정보를 알리기 위한 어노테이션
public class MyConfiguration {
    
    @Bean // 직접 등록한 첫번째 빈
    public MyService myService() {
        return new MyServiceImpl();
    }
    
    @Bean // 직접 등록한 두번째 빈
    public MyController myController(MyService myService) { 
        return new MyControllerImpl(myService); // 생성자에 빈으로 등록되어 있는 myService메소드 호출
    }
}
  1. 스프링 빈들을 설정하고 싶은 임의의 클래스 앞에 Configuration어노테이션을 붙여준다
  2. 각각의 메소드 앞에 Bean어노테이션을 붙여줌으로서 빈(Bean)으로 등록

2. @Configuration의 정의

  • 스프링 프레임워크의 스프링 컨테이너는 @Configuration이 붙은 클래스를 설정 정보로 사용.

  • 스프링 컨테이너에서 처리 할 수 있는 하나 이상의 빈(Bean) 메소드를 클래스가 정의하는데 나타냄.

  • @Configuration을 클래스를 표시함으로써 스프링(Spring)은 자동으로 클래스에 대한 프록시를 생성 후 이를 사용해 빈(Bean)을 인스턴스화하고 구성하는 메소드를 호출.

  • @Configuration은 상황에 따라 @Bean, @ComponentScan, @Import, @Profile와 같은 어노테이션과 함께 사용.

3. @Bean의 정의

  • 스프링 빈은 스프링 컨테이너에 의해 관리되는 객체

  • @Configuration이 붙은 클래스에 @Bean이라 적힌 메소드를 모두 호출해 반환된 객체를 스프링 컨테이너에 등록하는데 이렇게 스프링 컨테이너에 등록된 객체를 스프링 빈이라 함.

  • 스프링 빈의 이름은 기본적으로 @Bean이 붙은 메소드명으로 등록됨.

4. 스프링 컨테이너 생성 과정

관련예시

// 스프링 컨테이너 생성
ApplicationContext ac = new AnnotationConfigApplicationContext(MyConfiguration.class);
  • ApplicationContext를 스프링 컨테이너라고 한다.
  • ApplicationContext는 인터페이스이며, AnnotationConfigApplicationContext는 ApplicationContext의 구현객체이다.

참고: 스프링 컨테이너의 형태는 BeanFactory와 ApplicationContext입니다. 이 두 형태의 차이점은
추후 업로드하겠습니다.

5. 스프링 빈 조회

스프링 컨테이너에 등록된 모든 빈 출력

 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() 메소드를 사용할 수 있습니다. 이 메서드는 컨테이너에 정의된 모든 빈 이름의 배열을 반환합니다.

스프링 빈 조회 - 기본

  • xxxx.getBean(빈이름, 타입)
  • xxxx.getBean(타입)
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);
    }

profile
결과를 중요시하기보단 과정을 중요하게 생각하는 마음가짐

0개의 댓글