오봉이와 함께하는 개발 블로그

Spring - 스프링 컨테이너와 빈 - 5(스프링 빈 설정 메타 정보 - BeanDefinition) 본문

BE/Spring

Spring - 스프링 컨테이너와 빈 - 5(스프링 빈 설정 메타 정보 - BeanDefinition)

오봉봉이 2022. 6. 6. 18:41
728x90

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

  • xml, java, groovy 등 다양한 설정 형식을 지원할 수 있는 이유는 BeanDefinition이라는 추상화가 있기 때문.
    • 쉽게 표현하면 역할과 구현을 개념적으로 나눈 것
  • BeanDefinition은 읽어서 만들기만 하면 된다.
    • XML을 읽어서 BeanDefinition을 만든다.
    • 자바 코드를 읽어 BeanDefinition을 만든다.
    • 스프링 컨테이너는 자바든 XML이든 BeanDefinition만 알면 된다.
  • BeanDefinition을 빈 설정 메타정보라 함.
    • @Bean, <bean>당 각각 하나씩 메타 정보가 생성됨.
  • 스프링 컨테이너는 메타정보를 기반으로 스프링 빈을 생성함.

코드 레벨로 더 깊게 들어가자

  • AnnotationConfigApplicationContext는 AnnotatedBeanDefinitionReader를 사용해서 AppConfig.class를 읽고 BeanDefinition을 생성함
  • GenericXmlApplicationContext는 XmlBeanDefinitionReader를 사용해서 appConfig.xml 설정 정보를 읽고 BeanDefinition을 생성함.

위와 같이 새로운 형식의 설정 정보가 추가되면 XXXBeanDefinitionReader를 만들어 BeanDefinition을 생성하면 됨

BeanDefinition 살펴보기

BeanDefinition 정보

코드

package hello.core.beandefinition;

import hello.core.AppConfig;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.config.BeanDefinition;
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);
            }
        }
    }
}

출력

beanDefinitionName = appConfig beanDefinition = Generic bean: class [hello.core.AppConfig$$EnhancerBySpringCGLIB$$713455cd]; scope=singleton; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null
beanDefinitionName = memberService beanDefinition = Root bean: class [null]; scope=; abstract=false; lazyInit=null; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=appConfig; factoryMethodName=memberService; initMethodName=null; destroyMethodName=(inferred); defined in hello.core.AppConfig
beanDefinitionName = memberRepository beanDefinition = Root bean: class [null]; scope=; abstract=false; lazyInit=null; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=appConfig; factoryMethodName=memberRepository; initMethodName=null; destroyMethodName=(inferred); defined in hello.core.AppConfig
beanDefinitionName = orderService beanDefinition = Root bean: class [null]; scope=; abstract=false; lazyInit=null; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=appConfig; factoryMethodName=orderService; initMethodName=null; destroyMethodName=(inferred); defined in hello.core.AppConfig
beanDefinitionName = discountPolicy beanDefinition = Root bean: class [null]; scope=; abstract=false; lazyInit=null; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=appConfig; factoryMethodName=discountPolicy; initMethodName=null; destroyMethodName=(inferred); defined in hello.core.AppConfig
  • BeanClassName
    • 생성할 빈의 클래스 명
    • 자바 설정 처럼 팩토리 역할의 빈을 사용하면 없다.
  • factoryBeanName
    • 팩토리 역할의 빈을 사용할 경우 이름 (예 : appConfig)
    • xml을 사용할 때
  • factoryMethodName
    • 빈을 생성할 팩토리 메소드 지정 (예 : memberService)
    • Java 코드를 사용할 때
  • Scope
    • 싱글톤(기본값)
  • lazyInit
    • 스프링 컨테이너를 생성할 때 빈을 생성하는 것이 아닌, 실제 빈을 사용할 때 까지 최대한 생성을 지연처리 하는지에 대한 여부
  • InitMethodName
    • 빈을 생성하고, 의존관계를 적용한 뒤 호출되는 초기화 메소드 명
  • DestroyMethodName
    • 빈의 생성 주기가 끝나 제거하기 직전에 호출되는 메소드 명
  • Constructor arguments, Properties
    • DI에서 사용
    • 자바 설정 처럼 팩토리 역할의 빈을 사용하면 없다.

정리

  • BeanDefinition을 직접 생성해서 스프링 컨테이너를 등록할 수 있지만, 실무에서 직접 정의하거나 사용할 일은 거의 없다.
    • 고로 어려우면 그냥 넘어가도 됨.....
  • BeanDefinition에 대해서 너무 깊이있게 이해하기 보단, 스프링이 다양한 형태의 설정 정보를 BeanDefinition으로 추상화해서 사용하는 것 정도만 이해하면 됨.
  • 가끔 스프링 코드나 스프링 관련 오픈 소스의 코드를 볼 때, BeanDefinition이 보일 때가 있으면 위 케머니즘을 떠올리면 됨.

AnnotationConfigApplicationContext와 GenericXmlApplicationContext

  • 둘이 출력 정보가 다르다.
  • 스프링에 빈을 등록하는 방법은 여러가지가 있는데, 크게 두 가지로 보면 직접 스프링 빈을 등록하는 방법(xml)과 우회해서 등록하는 FactoryMethod(AppConfig.class : 자바 코드를 사용)라는 방법이 있다.
    • FactoryMethod는 외부에서 메소드를 호출하여 생성하는 방식이다.
출처 : 인프런 김영한 지식공유자님의 스프링 완전 정복 로드맵 강의
728x90
Comments