꼬물꼬물

스프링 컨테이너와 스프링 빈 [다양한 설정 형식(XML), 메타정보] 본문

카테고리 없음

스프링 컨테이너와 스프링 빈 [다양한 설정 형식(XML), 메타정보]

멩주 2022. 9. 16. 11:49

BeanFactory와 Application Context

  • BeanFactory
    • 스프링 컨테이너의 최상위 인터페이스
    • 스프링 빈을 관리하고 조회하는 역할 담당
    • getBean()으로 조회 가능
  • ApplicationContext
    • BeanFactory 기능을 모두 상속받는다. <- 그렇다면 둘의 차이는?
    • 애플리케이션 개발에는 빈 관리/조회 기능 뿐만 아니라 많은 부가기능이 필요하다.

  • 메시지 소스를 활용한 국제화 기능: 한국에서 들어오면 한국어로, 영어로 들어오면 영어로 출력한다.
  • 환경 변수: 로컬(개발), 개발(테스트 서버), 운영(실제) 환경 등을 구분해 처리
  • 애플리케이션 이벤트: 이벤트를 발행하고 구독하는 모델을 편리하게 지원
  • 편리한 리소스 조회: 파일, 클래스 패스, 외부 등에서 리소스를 편리하게 조회
  • 이는 애플리케이션 만드는데 필요한 공통 기능들

BeanFactory와 ApplicationContext 모두 스프링 컨테이너라고 할 수 있다.


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

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

GenericXml: 자바 코드가 아닌 XML 문서를 설정 정보로 사용

 

  • 어노테이션 기반 자바 코드 설정 사용
    • new AnnotationConfigApplicationContext(AppConfig.class)
    • AnnotationConfigApplicationContext 클래스를 사용하면 자바 코드로된 설정정보를 넘기면 된다.
  • XML 설정 사용
    • 최근 스프링 부트 사용하면서 XML 기반의 설정은 잘 사용하지 않음.그렇지만 아직 많은 레거시 프로젝트들이 XML로 되어 있고, XML을 사용하면 컴파일 없이 빈 설정 정보를 변경할 수 있는 장점이 있어 배워두자.
    • GenericXmlApplicationContext을 사용해 xml 설정 파일을 넘기면 된다.

 

public class XmlAppContext {

    @Test
    void xmlAppContext(){
        ApplicationContext ac = new GenericXmlApplicationContext("appConfig.xml");
        MemberService memberService = ac.getBean("memberService", MemberService.class);
        Assertions.assertThat(memberService).isInstanceOf(MemberService.class);
    }
}

resource/appConfig.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- class는 실제 구체 -->
    <bean id="memberService" class="hello.core.member.MemberServiceImpl">
        <!-- 생성자로 만들기 -->
        <constructor-arg name="memberRepository" ref="memberRepository" />
    </bean>

    <bean id="memberRepository" class="hello.core.member.MemoryMemberRepository"/>

    <bean id="orderService" class="hello.core.order.OrderServiceImpl">
        <!-- ref는 참조 -->
        <constructor-arg name="memberRepository" ref="memberRepository"/>
        <constructor-arg name="discountPolicy" ref="discountPolicy"/>
    </bean>

    <bean id="discountPolicy" class="hello.core.discount.RateDiscountPolicy"/>

</beans>

스프링의 유연한 설정들을 확인할 수 있다.


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

  • 스프링은 어떻게 이런 다양한 설정 형식을 지원할까? <- BeanDefinition 추상화가 있다!
  • 즉, 역할과 구현을 개념적으로 나눈 것.
    • XML을 읽어서 BeanDefinition을 만든다.
    • 자바 코드를 읽어서 BeanDefinition을 만든다.
    • 스프링 컨테이너는 자바 코드인지, XML인지 몰라도 된다. 오직 BeanDefinition만 알면 된다!
  • BeanDefinition을 빈 설정 메타 정보라고 한다,
    • @Bean, <bean> 당 각각 하나씩 메타 정보가 생성된다.
  • 스프링 컨테이너는 이 메타 정보를 기반으로 스프링 빈을 생성한다.
  • 스프링 컨테이너는 BeanDefinition에만 의존한다. -> 자바인지 xml인지 상관 안 해! <- 추상화에만 의존

BeanDefinition: 빈 설정 메타 정보

  • AnnotationConfigApplicationContext는 AnnotatedBeanDefinitionReader를 사용해 AppConfig.class를 읽고 BeanDefinition을 생성한다.
  • GernericXmlApplicationContext는 XmlBeanDefinitionReader를 사용해 appConfig.xml 설정 정보를 읽고 BeanDefinition을 생성한다.
  • 새로운 형식의 설정 정보가 추가되면, XxxBeanDefinitionReader를 만들어 BeanDefinition을 생성한다.

 

BeanDefinition 정보

  • scope=; : 할당 X == 싱글톤
  • lazyInit=null; : 보통의 스프링 빈은 스프링 컨테이너가 뜰 때, 등록되지만 Lazy는 실제 사용하는 시점에 스프링 빈을 초기화 한다.
  • 메타 정보를 기반으로 실제 인스턴스를 생성한다.
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);
            }
        }
    }
}

 

정리

  • BeanDefinition을 직접 생성해 스프링 컨테이너에 등록할 수 있다. 하지만 BeanDefinition을 직접 정의하거나 사용할 일은 거의 없다.
  • 스프링이 다양한 형태의 설정 정보를 BeanDefinition으로 추상화해 사용한다.