본문 바로가기
개발 관련 공부/스프링 김영한 로드맵

[스프링 기본] 4. 스프링 컨테이너와 스프링 빈

by 슴새 2022. 10. 10.
반응형

스프링 컨테이너 생성

ApplicationContext applicationContext = new
        AnnotationConfigApplicationContext(AppConfig.class);

ApplicationContext가 스프링 컨테이너이다.

ApplicationContext는 인터페이스고, AnnotationConfigApplicationContext가 그 구현체이다.

스프링 컨테이너 만드는 방법

  • 어노테이션 기반
  • xml 기반(요즘은 잘 사용안함)

우리 예제에서는 AppConfig에 어노테이션을 붙여서 만들었다.

스프링 컨테이너를 생성할 때는 구성 정보를 지정해주야 한다. 여기서는 AppConfig.class 를 구성 정보로 지정했다.

스프링 빈은 빈 저장소에 @Bean이 붙은 애들을 등록한다. 보통은 메서드 이름이 빈 이름이지만 자신이 지정해 줄 수도 있다.

스프링 컨테이너는 설정 정보를 참고해서 의존관계를 주입(DI)한다.

컨테이너에 등록된 모든 빈 조회

테스트 코드에 beanfind 패키지-ApplicationContextInfoTest.java를 만들어 빈 조회를 테스트해보자.

class ApplicationContextInfoTest {
    AnnotationConfigApplicationContext ac = new
            AnnotationConfigApplicationContext(AppConfig.class); 
}

스프링 컨테이너 구현체 ac를 하나 만들고 이것을 이용하여 모든 빈 출력, 애플리케이션 빈 출력을 테스트할 수 있다.

@Test
@DisplayName("모든 빈 출력하기")
void findAllBean() {
    String[] beanDefinitionNames = ac.getBeanDefinitionNames();
    for (String beanDefinitionName : beanDefinitionNames) {
        Object bean = ac.getBean(beanDefinitionName);
        System.out.println("name=" + beanDefinitionName + " object=" +
                bean);
    }
}

모든 빈 출력은 다음과 같이 작성하면 된다.

getBeanDefinitionNames()과 getBean() 함수를 활용한다.

스프링 내부 빈들 사이에 직접 등록한 빈 5개가 보인다. (appConfig, MemberService,OrderService....)

애플리케이션 빈 출력은 위 코드에

getBeanDefinition(beanDefinitionName).getRole()함수를 호출해 if문으로 걸러내서 호출하면 된다.

리턴값이 Role ROLE_APPLICATION이면 직접 등록한 애플리케이션 빈이고, Role ROLE_INFRASTRUCTURE이면 스프링이 내부에서 사용하는 빈이다.

스프링 빈 조회- 기본

가장 기본적인 빈 조회 방법

  • getBean(빈이름,타입)
  • getBean(타입)
MemberService memberService = ac.getBean("memberService", MemberService.class);

빈 이름으로 조회

MemberService memberService = ac.getBean(MemberService.class);

타입만으로 조회

MemberServiceImpl memberService = ac.getBean("memberService", MemberServiceImpl.class);

구체 타입으로 조회(유연성 떨어짐)

Assertions.assertThrows(NoSuchBeanDefinitionException.class, () ->
        ac.getBean("xxxxx", MemberService.class));

조회대상 빈이 없으면 예외가 발생한다.

스프링 빈 조회 - 동일한 타입이 둘 이상

class ApplicationContextSameBeanFindTest {
    AnnotationConfigApplicationContext ac = new
            AnnotationConfigApplicationContext(SameBeanConfig.class);
    ...
    //타입 조회시 중복 오류 확인하기 위해 내부 클래스로 하나 만듦
    @Configuration
    static class SameBeanConfig {
        @Bean
        public MemberRepository memberRepository1() {
            return new MemoryMemberRepository();
        }
        @Bean
        public MemberRepository memberRepository2() {
            return new MemoryMemberRepository();
        }
    }
}

중복인 경우 조회 결과를 확인하기 위해 내부 클래스로 스프링 컨테이너를 하나 만들자.

assertThrows(NoUniqueBeanDefinitionException.class, () ->
        ac.getBean(MemberRepository.class));

타입으로 조회했는데 동일 타입의 빈이 둘 이상이면 오류가 발생한다.

Map<String, MemberRepository> beansOfType = ac.getBeansOfType(MemberRepository.class);
for (String key : beansOfType.keySet()) {
    System.out.println("key = " + key + " value = " + beansOfType.get(key));
}

이 경우 빈 이름을 지정하거나 getBeanOfType로 해당 타입의 모든 빈을 조회한다.

스프링 빈 조회 - 상속 관계

부모타입으로 조회하면 자식 타입도 함게 조회한다.

예를 들어 Object 타입으로 조회하면 모든 스트링 빈을 다 출력한다.

@Configuration
static class TestConfig {
    @Bean
    public DiscountPolicy rateDiscountPolicy() {
        return new RateDiscountPolicy();
    }
    @Bean
    public DiscountPolicy fixDiscountPolicy() {
        return new FixDiscountPolicy();
    }
}

마찬가지로 조회 결과 확인 위해 테스트 코드 내부에 컨테이너를 하나 만든다.

assertThrows(NoUniqueBeanDefinitionException.class, () ->
        ac.getBean(DiscountPolicy.class));

부모 타입으로 조회시 자식이 둘 이상이면 예외가 발생한다.

이 경우 빈 이름을 함께 지정해야 한다.

Map<String, DiscountPolicy> beansOfType = ac.getBeansOfType(DiscountPolicy.class);
assertThat(beansOfType.size()).isEqualTo(2);
for (String key : beansOfType.keySet()) {
    System.out.println("key = " + key + " value=" + beansOfType.get(key));
}

부모 타입으로 모두 조회할 수 있다.

부모 자신인 DiscoutPolicy가 출력되지 않은 이유는 빈 등록이 안되어서 인듯...

참고로 지금은 확인하려고 이렇게 하는데 테스트 코드에 출력문 넣는건 좋은 방법이 아니라고 한다.

BeanFactory와 ApplicationContext

BeanFactoy라는 최상위 인터페이스가 있고, 그를 상속하는 ApplicationContext 인터페이스가 있고, 그 밑에 우리가 사용했던 AnnotationConfigApplicationContext가 있다...

BeanFactoy

  • 스프링 빈을 관리하고 조회하는 역할
  • getBean등 지금까지 사용했던 대부분의 기능은 빈팩토리가 제공

ApplicationContext

  • BeanFactory 기능을 모두 상속받아서 제공
  • 그 외 많은 부가기능(환경변수, 리소스 조회 등)들을 제공

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

초반에, 스프링 컨테이너 만드는 방법에는 여러가지가 있다고 언급했다.(어노테이션,xml)

BeanDefinition(=빈 설정 메타정보)이라는 추상화 덕분에 스프링이 다양한 설정 형식을 지원할 수 있는 것이다.

어노테이션의 경우 @Bean, xml의 경우 <bean> 당 하나씩 메타 정보가 생성된다.

스프링 컨테이너는 이 메타정보를 기반으로 스프링 빈을 생성한다. 스프링 컨테이너는 자바 코드인지, XML인지 몰라도 된다. 오직 BeanDefinition만 알면 스프링 빈을 생성할 수 있다. 

 

이 포스팅은 인프런 김영한님의 '스프링 핵심 원리 기본편'을 듣고 정리한 것입니다.
강의 링크: https://url.kr/udopsk  
반응형

댓글