[Spring] 기본_스프링 컨테이너와 스프링 빈

gayoung·2022년 3월 14일
0

스프링 완전 정복

목록 보기
19/33

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

1-1. 스프링 컨테이너 생성

// 스프링컨테이너                         ApplicationContext 인터페이스의 구현체
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
  • 스프링 컨테이너 = ApplicationContext
  • ApplicationContext인터페이스
  • 스프링 컨테이너는 XML기반으로도 만들 수 있고, 어노테이션 기반의 자바 설정 클래스로 만들 수 있음
  • 현재, AppConfig.class를 구성 정보로 지정

1-2. 스프링 빈 등록

  • 빈 이름 = 메서드 이름
  • 빈 이름 직접 부여 가능 : @Bean(name="memberService2")

1-3. 스프링 빈 의존관계 설정

  • 설정 정보를 참고해서 의존 관계를 주입(DI)
  • 싱글톤 컨테이너로, 단순히 자바 코드를 호출하는 것과 차이가 있음
  • 동적인 객체 인스턴스 의존관계를 스프링이 연결해줌 -> 참조값 연결해줌 = 의존관계 단순히 주입!

2. 컨테이너에 등록된 빈 조회

2-1. 기본

AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

@Test
@DisplayName("빈 이름으로 조회")
void findBeanByName() {
    MemberSerivce memberService = ac.getBean("memberService", MemberSerivce.class);
    Assertions.assertThat(memberService).isInstanceOf(MemberServiceImpl.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 findBeanByType() {
    MemberSerivce memberService = ac.getBean(MemberSerivce.class);  // 같은 타입이 여러개인 경우 곤란하긴함
    Assertions.assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}

@Test
@DisplayName("구체 타입으로 조회")
void findBeanByName2() {
        // 스프링빈에 등록된 MemberService(@Bean)의 인스턴스 타입으로 결정하므로 구체타입으로 적어도 됨
        // 하지만, 구체타입으로 적으면 안좋음 -> 역할과 구현 구분하자 우리는 역할에 의존해야한다!!
        // 구체타입으로 작성하면 유연성 떨어짐
        MemberSerivce memberService = ac.getBean("memberService", MemberServiceImpl.class);
        Assertions.assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}

@Test
@DisplayName("빈 이름으로 조회X")
void findBeanByNameX() {
 org.junit.jupiter.api.Assertions.assertThrows(NoSuchBeanDefinitionException.class,
                () -> ac.getBean("xxxx", MemberSerivce.class));  // 에러터지면 성공!
}
  • ac.getBeanDefinitionNames() : 스프링에 등록된 모든 빈 이름을 조회
  • ac.getBean((빈이름), 타입) : 빈 이름으로 빈 객체(인스턴스)를 조회
  • 조회 대상 스프링 빈이 없으면 예외 발생
    • NoSuchBeanDefinitionException: No bean named 'xxxxx' available

2-2. 동일한 타입 둘 이상

AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(ApplicationContextSameBeanFindTest.SameBeanConfig.class);

@Test
@DisplayName("타입으로 조회 시 같은 타입이 둘 이상있으면, 중복오류 발생")
void findBeanByTypeDuplicate() {
    // MemberRepository bean = ac.getBean(MemberRepository.class);
    Assertions.assertThrows(NoUniqueBeanDefinitionException.class,
            () -> ac.getBean(MemberRepository.class));
}

@Test
@DisplayName("타입으로 조회 시 같은 타입이 둘 이상있으면, 빈 이름을 지정하면 됨")
void findBeanByName() {
    MemberRepository memberRepository = ac.getBean("memberRepository1", MemberRepository.class);  // 같은 타입이 여러개인 경우 곤란하긴함
    org.assertj.core.api.Assertions.assertThat(memberRepository).isInstanceOf(MemberRepository.class);
}

@Test
@DisplayName("특정 타입을 모두 조회하기")
void findAllBeanByType() {
    Map<String, MemberRepository> beansOfType = ac.getBeansOfType(MemberRepository.class);  // 같은 타입이 여러개인 경우 곤란하긴함
    for (String key : beansOfType.keySet()) {
        System.out.println("beansOfType = " + beansOfType);
    }

    System.out.println("beansOfType = " + beansOfType);
    org.assertj.core.api.Assertions.assertThat(beansOfType.size()).isEqualTo(2);
}

@Configuration  // static 장점: 이 클래스안에서만 사용하겠다는 의미
static class SameBeanConfig {

    @Bean
    public MemberRepository memberRepository1() {
        return new MemoryMemberRepository();
    }

    @Bean
    public MemberRepository memberRepository2() {
        return new MemoryMemberRepository();
    }

}
  • ac.getBeansOfType() : 해당 타입의 모든 빈을 조회
  • 동일한 타입이 둘 이상이면 에러 발생 -> 빈 이름 지정!

2-3. 상속관계

AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);

@Test
@DisplayName("부모 타입으로 조회 시, 자식이 둘 이상 있으면, 중복오류 발생")
void findBeanByParentTypeDuplicate() {
    // DiscountPolicy bean = ac.getBean(DiscountPolicy.class);
    assertThrows(NoSuchBeanDefinitionException.class,
            () -> ac.getBean(DiscountPolicy.class));
}

@Test
@DisplayName("부모 타입으로 조회 시, 자식이 둘 이상 있으면, 빈 이름 지정하면 됨")
void findBeanByParentTypeBeanName() {
    DiscountPolicy rateDiscountPolicy = ac.getBean("rateDiscountPolicy",DiscountPolicy.class);
    Assertions.assertThat(rateDiscountPolicy).isInstanceOf(RateDiscountPolicy.class);
}

@Test
@DisplayName("특정 하위 타입으로 조회")  // 좋은 방법은 아님
void findBeanBySubType() {
    DiscountPolicy rateDiscountPolicy = ac.getBean(RateDiscountPolicy.class);
    Assertions.assertThat(rateDiscountPolicy).isInstanceOf(RateDiscountPolicy.class);
}

@Test
@DisplayName("부모 타입으로 모두 조회")  // 좋은 방법은 아님
void findAllBeanByParentType() {
    Map<String, DiscountPolicy> beansOfType = ac.getBeansOfType(DiscountPolicy.class);  // 같은 타입이 여러개인 경우 곤란하긴함
    Assertions.assertThat(beansOfType.size()).isEqualTo(2);
    for (String key : beansOfType.keySet()) {
        System.out.println("key = " + key + " value = " + beansOfType.get(key));  // 실제로는 출력X
    }
}

@Test
@DisplayName("부모 타입으로 모두 조회 - Object")  // 좋은 방법은 아님
void findAllBeanByObjectType() {
    Map<String, Object> beansOfType = ac.getBeansOfType(Object.class);  // 같은 타입이 여러개인 경우 곤란하긴함
    Assertions.assertThat(beansOfType.size()).isEqualTo(2);
    for (String key : beansOfType.keySet()) {
        System.out.println("key = " + key + " value = " + beansOfType.get(key));  // 실제로는 출력X
    }
}

@Configuration
static class TestConfig {

    @Bean
    public DiscountPolicy rateDiscountPolicy() {
        return new RateDiscountPolicy();
    }

    @Bean
    public DiscountPolicy fixDiscountPolicy() {
        return new FixDiscountPolicy();
    }
}
  • 부모 타입으로 조회하면 자식 타입도 함께 조회됨
  • Object 타입으로 조회하면, 모든 스프링 빈 조회됨

3. BeanFactory와 ApplicationContext

3-1. BeanFactory

  • 스프링 컨테이너의 최상위 인터페이스
  • 스프링 빈을 관리하고 조회하는 역할
  • getBean() 제공
  • 지금까지 우리가 사용했던 대부분의 기능은 BeanFactory가 제공하는 기능

3-2. ApplicationContext

  • BeanFactory 기능을 모두 상속받아서 제공
  • 부가기능 제공(메세지소스를 활용한 국제화 기능, 환경변수, 어플리케이션 이벤트 등)
  • BeanFactory를 직접 사용할 일은 거의 없음. 부가기능이 포함된 ApplicationContext를 사용
  • 스프링 컨테이너 = BeanFactory나 ApplicationContext

4. 다양한 설정 형식 지원 - 자바 코드, XML

  • AppConfig.class 자리에 XML과 같은 다른 설정 형식 입력

5. 스프링 빈 설정 메타 정보 - BeanDefinition

  • 역할과 구현을 개념적으로 나눈 것
  • BeanDefinition = 빈 설정 메타정보
    • @Bean(java) , <bean>(xml) 당 각각 하나씩 메타 정보가 생성됨
  • 스프링 컨테이너는 이 메타정보를 기반으로 스프링 빈 생성
  • beanDefinition은 interface (추상) -> 스프링컨테이너는 추상화에만 의존
  • AnnotatedBeanDefinitionReader는 AppConfig.class파일 읽어서 beanDefinition 생성하는 것

0개의 댓글