ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Spring] 스프링 컨테이너 XML로 설정하기, 스프링 빈 설정 메타정보(BeanDefinition)
    Back-end/Spring 2022. 3. 4. 14:40

    안녕하세요 이번 포스팅은 스프링 컨테이너를 XML로 설정하는 것을 알아보겠습니다.

     

    스프링 컨테이너는 다양한 형식의 설정 정보를 받아들일 수 있게 유연하게 설계되어 있습니다.

    (자바 코드, XML, Groovy 등등)

     

     

    지금 까지는 new AnnotationConfigApplicationContext(AppConfig.class) 이렇게 해서 자바 코드로 컨테이너를 생성했습니다.

     

    XML 설정 사용

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

    GenericXmlApplicationContext를 사용하면서 xml 설정 파일을 넘기면 됩니다.

    이런 설정 파일이나 정적 파일은 src/main/resources에 저장해주면 됩니다.

    <?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">
        
         <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">
             <constructor-arg name="memberRepository" ref="memberRepository" />
             <constructor-arg name="discountPolicy" ref="discountPolicy" />
         </bean>
         
         <bean id="discountPolicy" class="hello.core.discount.RateDiscountPolicy" />
    </beans>
    import hello.core.member.MemberService;
    import org.junit.jupiter.api.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.GenericXmlApplicationContext;
    import static org.assertj.core.api.Assertions.*;
    
    public class XmlAppContext {
         @Test
         void xmlAppContext() {
         ApplicationContext ac = new
        		GenericXmlApplicationContext("appConfig.xml");
    
         MemberService memberService = ac.getBean("memberService",
        		MemberService.class);
         assertThat(memberService).isInstanceOf(MemberService.class);
         }
    }

    AppConfig.java와 appConfig.xml을 비교해보면 형태만 다를 뿐 기본적으로 거의 비슷합니다.

    눈에 띄는 점은 빈을 생성할 때 class=""에 클래스의 패키지 디렉터리를 명시해줘야 한다는 것입니다.

     

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

    스프링이 이렇게 다양한 설정 형식을 지원할 수 있는 이유의 중심에는 BeanDefinition이라는 추상화가 있습니다.

    쉽게 말해서 역할과 구현을 개념적으로 나눈 것입니다.

     

    스프링 컨테이너는 자바 코드인지,  XML인지 몰라도 됩니다. 오직 BeanDefinition 만 알면 되고 BeanDefinition을 빈 설정 정보라 합니다. 메타 데이터의 정의는 데이터에 대한 데이터라는 뜻으로, 스프링 빈 설정 정보 메타 정보라 할 때도 비슷한 맥락입니다.

    즉, BeanDefinition은 빈에 대한 여러 데이터를 가지고 있다고 생각하면 됩니다.

    또한, @Bean, <bean> 당 각각 하나씩 메타 정보가 생성되고 스프링 컨테이너는 이 메타정보를 기반으로 스프링 빈을 생성합니다.

     

     

    - AnnotationConfigApplicationContext는 AnnotatedBeanDefinitionReader를 사용해서 AppConfig.class를 읽고 BeanDefinition을 생성합니다.

     

    - GenericXmlApplicationContext는 XmlBeanDefinitionReader를 사용해서 appConfig.xml 설정 정보를 읽고 BeanDefinition을 생성합니다.

     

    - 새로운 형식의 설정 정보가 추가되면, XxxBeanDefinitionReader를 만들어서 BeanDefinition을 생성하면 됩니다.

     

    예제 코드

    import hello.core.AppConfig;
    import org.junit.jupiter.api.DisplayName;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.MutablePropertyValues;
    import org.springframework.beans.factory.config.BeanDefinition;
    import org.springframework.beans.factory.config.ConstructorArgumentValues;
    import
    org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import org.springframework.context.support.GenericXmlApplicationContext;
    
    public class BeanDefinitionTest {
    	 AnnotationConfigApplicationContext ac = new
    		AnnotationConfigApplicationContext(AppConfig.class);
    	// GenericXmlApplicationContext ac = new GenericXmlApplicationContext("appConfig.xml");
     
         @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 정보에는 Scope, lazyInit 등 다양한 정보가 담겨있는 것을 볼 수 있습니다.

     

    정리

    BeanDefinition을 직접 생성해서 스프링 컨테이너에 등록할 수도 있습니다. 하지만 실무에서는 BeanDefinition을 직접 정의하거나 사용할 일은 거의 없다고 합니다.

    이것에 대해서 너무 깊게 이해하기보다는 스프링이 다양한 형태의 설정 정보를 BeanDefinition으로 추상화해서 사용하는 것 정도만 이해하면 됩니다.

     

    다음 포스팅에서는 싱글톤 컨테이너에 대해 알아보겠습니다.

Designed by Tistory.