BeanFactory와 ApplicationContext

황준하·2023년 7월 20일

Spring 기본

목록 보기
19/38
post-thumbnail

스프링 빈 조회 - 상속관계

package hello.core.beanfind;

import ...

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;

public class ApplicationContextExtendsFindTest {

    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);


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

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

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

    @Test
    @DisplayName("부모 타입으로 모두 조회하기")
    void findAllBeanByParentType() {
        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));
        }
    }

    /*
    **스프링빈에 등록된 모든 bean이 다 출력됨 (모든게 Object 파일이라서)
     */
    @Test
    @DisplayName("부모 타입으로 모두 조회하기 - Object")
    void findAllBeanByType() {
        Map<String, Object> beansOfType = ac.getBeansOfType(Object.class);
        for (String key : beansOfType.keySet()) {
            System.out.println("key = " + key + " value = " + beansOfType.get(key));
        }
    }

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

        @Bean
        public DiscountPolicy fixDiscountPolicy() {
            return new FixDiscountPolicy();
        }
    }
}

스프링 빈을 조회할일은 거의 없지만 기본적이기 때문에 알아둬야 함.

BeanFactory와 ApplicationContext

BeanFactory

  • 스프링 컨테이너의 최상위 인터페이스다.
  • 스프링 빈을 관리하고 조회하는 역할을 담당한다.
  • getBean() 제공

ApplicationContext

  • BeanFactory 기능을 모두 상속받아서 제공한다.
  • 애플리케이션을 개발할때는 빈을 관리하고 조회하는 기능은 물론이고, 수 많은 부가기능이 필요하다.

ApplicationContext의 부가기능

  • "메세지소스를 활용한 국제화 기능" - 한국에[서 들어오면 한국어로, 영어권에서 들어오면 영어로 출력
  • "환경변수" - 로컬, 개발, 운영등을 구분해서 처리
  • "애플리케이션 이벤트" - 이벤트를 발행하고 구독하는 모델을 편리하게 지원
  • "편리한 리소스 조회" - 파일, 클래스패스, 외부 등에서 리소스를 편리하게 조회

BeanFactory를 직접 사용할 일은 거의 없다. 부가기능이 포함된 ApplicationContext를 사용한다.
BeanFactory나 ApplicationContext를 스프링 컨테이너라 한다.

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

  • 스프링 컨테이너는 다양한 형식의 설정 정보를 받아드릴 수 있게 유연하게 설계되어 있다.
  • 자바코드, XML, Groovy 등등

애노테이션 기반 자바 코드 설정 사용

  • 지금까지 했던 것
  • new AnnotationConfigApplicationContext(AppConfig.class)

XML 설정 사용

  • 최근에는 스프링 부트를 많이 사용하면서 잘 사용하지 않는다. 아직 많은 레거시 프로젝트 들이 XML로 되어 있고,
  • 또 XML을 사용하면 컴파일 없이 빈 설정 정보를 변경할 수 있는 장점도 있다.
  • GenericXmlApplictionContext를 사용하면서 xml 설정 파일을 넘기면 된다.

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

  • 스프링은 어떻게 이런 다양한 설정 형식을 지원할까? 그 중심에는 BeanDefinition이라는 추상화가 있다.
  • 쉽게 이야기해서 "역할과 구현은 개념적으로 나눈 것"이다.
  • BeanDefinition을 빈 설정 메타정보라 한다.
  • 스프링 컨테이너는 이 메타정보를 기반으로 스프링 빈을 생성한다.

BeanDefinition 살펴보기

public class BeanDefinitionTest {
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

    @Test
    @DisplayName("빈 설정 메타정보 확인")
    void findApplicationBean() {
        String[] beanDefinitionNames = ac.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            BeanDefinition beanDefinition = ac.getBeanDefinition(beanDefinitionName);

            if (beanDefinition.getRole() == BeanDefinition.ROLE_APPLICATION) {
                System.out.println("beanDefinitionName = " + beanDefinitionName +
                        " beanDefinition = " + beanDefinition);
            }
        }
    }
}

실행화면

beanDefinitionName = appConfig 
beanDefinition = Generic bean: class [hello.core.AppConfig$$EnhancerBySpringCGLIB$$802c8ae0]; 
scope=singleton; abstract=false; lazyInit=null; 
autowireMode=0; dependencyCheck=0; 
autowireCandidate=true; primary=false; 
factoryBeanName=null; factoryMethodName=null; 
initMethodName=null; destroyMethodName=null
  • BeanDefinition을 직접 생성해서 스프링 컨테이너에 등록할 수도 있다. 하지만 실무에서 직접 정의하거나 사용할 일은 거의 없다.
  • 스프링의 다양한 형태의 설정 정보를 BeanDefinition으로 추상화해서 사용하는 것 정도만 이해하면 된다.

0개의 댓글