쌩로그

스프링 핵심원리 기본편 - Ch04. 스프링 컨테이너와 스프링 빈 본문

Spring/Spring & Spring Boot

스프링 핵심원리 기본편 - Ch04. 스프링 컨테이너와 스프링 빈

.쌩수. 2023. 12. 17. 00:08
반응형

목록

  1. 포스팅 개요
  2. 본론
      2-1. 스프링 컨테이너 생성
      2-2. 컨테이너에 등록된 모든 빈 조회
      2-3. 스프링 빈 조회 - 기본
      2-4. 스프링 빈 조회 - 동일한 타입이 둘 이상
      2-5. 스프링 빈 조회 - 상속 관계
      2-6. BeanFactory와 ApplicationContext
      2-7. 다양한 설정 형식 지원 - 자바 코드, XML
      2-8. 스프링 빈 설정 메타 정보 - BeanDefinition
  3. 요약

1. 포스팅 개요

인프런에서 영한님의 스프링 핵심 원리 기본편 Section04 스프링 컨테이너와 스프링 빈를 학습하며 정리한 포스팅이다.

| 참고 이전 포스팅

2. 본론

2-1. 스프링 컨테이너 생성

스프링 컨테이너가 생성되는 과정은 다음과 같다.

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
  • ApplicationContext를 스프링 컨테이너라 한다.
  • ApplicationContext는 인터페이스다.
  • 스프링 컨테이너는 XML을 기반으로 만들 수 있고, 애노테이션 기반의 자바 설정 클래스로 만들 수 있다.
  • 위의 코드는 애노테이션 기반의 자바 설정 클래스로 스프링 컨테이너를 만든 것이다.
  • AnnotationConfigApplicationContext클래스는 ApplicationContext 인터페이스의 구현체이다.

참고

스프링 컨테이너를 부를 때 BeanFactory , ApplicationContext 로 구분해서 이야기한다.
이 부분은 뒤에서 설명한다.
BeanFactory를 직접 사용하는 경우는 거의 없고, 일반적으로 ApplicationContext를 스프링 컨테이너라 한다.

스프링 컨테이너의 생성과정은 다음과 같다.

new AnnotationConfigApplicationContext(AppConfig.class); 코드로 AppConfig.class 정보로 스프링 컨테이너가 생성된다.
스프링 컨테이너 내부에는 빈 이름을 key로, 빈 객체를 value로 가지는 스프링 빈 저장소가 있다.

AppConfig.class 정보로 스프링 빈을 스프링 빈 저장소에 등록한다.

참고로 빈 이름은 메서드 이름을 사용하고, 빈 이름을 @Bean 애너테이션에 name 속성으로 직접 부여할 수 도 있다.

주의

빈 이름은 항상 다른 이름을 부여해야 한다. 같은 이름을 부여하면, 다른 빈이 무시되거나, 기존 빈을 덮어버리거나 설정에 따라 오류가 발생한다.
스프링 부트는 경고를 날리면서 팅기는 것이 기본 설정이다.

스프링 빈이 다음 그림과 같이 스프링 컨테이너에 등록되어있다.

이후 구성정보에 따라 의존관게를 주입한다.

과정이 자바 코드를 호출하는 것 같지만 차이가 있는데, 이 차이는 싱글톤 컨테이너 파트에서 설명한다.

참고로 스프링은 빈을 생성하고, 의존관계를 주입하는 단계는 나누어져 있다.
그런데 이렇게 자바 코드로 스프링 빈을 등록하면 생성자를 호출하면서 의존관계 주입도 한번에 처리된다.

(memberService()를 호출하고, memberRepository()를 호출하고, MemoryMemberRepository()가 생성되어 주입되고, orderService를 호출하면서 나머지 discountPolicy()를 호출하고, RateDiscountPolicy가 생성되어 주입되는 식으로 한 번에 엮인다.)

더 자세한 내용은 이후에 나온다.

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

테스트 코드를 다음과 같이 작성해보자.

class ApplicationContextInfoTest {

    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

    @Test
    @DisplayName("모든 빈 출력하기")
    void findAllBean() {
        String[] beanDefinitionNames = ac.getBeanDefinitionNames();

        for (String beanDefinitionName : beanDefinitionNames) {
            Object bean = ac.getBean(beanDefinitionName);
            // name = key, object = value
            System.out.println("name = " + beanDefinitionName + ", object = " + bean);
        }
    }
}

결과는 다음과 같다.

name = appConfig, object = hello.core.AppConfig$$SpringCGLIB$$0@726e5805
name = memService, object = hello.core.member.MemberServiceImpl@40c80397
name = memberRepository, object = hello.core.member.MemoryMemberRepository@4b672daa
name = orderService, object = hello.core.order.OrderServiceImpl@ea9b7c6
name = discountPolicy, object = hello.core.discount.RateDiscountPolicy@e077866

위는 스프링 내부적으로 쓰는 빈을 제외했다.
참고로 AppConig빈으로 등록된 것을 확인할 수 있다.

내가 등록한 빈만 확인하려면 다음과 같이 하면된다.

@Test
    @DisplayName("애플리케이션 빈 출력하기")
    void findApplicationBean() {
        String[] beanDefinitionNames = ac.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            BeanDefinition beanDefinition = ac.getBeanDefinition(beanDefinitionName);

            // 내가 등록한 빈이라면,
            if(beanDefinition.getRole() == BeanDefinition.ROLE_APPLICATION) {
                Object bean = ac.getBean(beanDefinitionName);
                // name = key, object = value
                System.out.println("name = " + beanDefinitionName + ", object = " + bean);
            }
        }
    }

출력 결과는 다음과 같다.

참고로 BeanDefinition Role에는 다음과 같은 것들이 있다.

  • Role ROLE_APPLICATION: 직접 등록한 애플리케이션 빈
  • Role ROLE_INFRASTRUCTURE: 스프링이 내부에서 사용하는 빈

다음은 ROLE_INFRASTRUCTURE 설정을 주어서 스프링 내부에서 사용되는 빈을 출력한 결과다.

2-3. 스프링 빈 조회 - 기본

getBean() 메서드를 사용하면된다.

  • getBean(빈이름, 타입)

  • getBean(타입)

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

    @Test
    @DisplayName("빈이 조회되지 않아 예외 발생")
    void findBeanButExcpetion() {
        ac.getBean("ll");
    }

    결과는 다음과 같다.

테스트를 해보자.

import static org.assertj.core.api.Assertions.*;

class ApplicationcontextBasicFindTest {
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

    @Test
    @DisplayName("빈 이름으로 조회")
    void findBeanByName() {
        MemberService memberService = ac.getBean("memberService", MemberService.class);
        assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
    }

    @Test
    @DisplayName("이름 없이 타입으로만 조회")
    void findBeanByType() {
        MemberService memberService = ac.getBean(MemberService.class);
        assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
    }

    @Test
    @DisplayName("구체 타입으로 조회")
    void findBeanByName2() {
        MemberService memberService = ac.getBean("memberService", MemberServiceImpl.class);
        assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
    }

    @Test
    @DisplayName("빈 이름으로 조회 X ")
    void findBeanByNameX() {
    // ac.getBean("xxxxx", MemberService.class); // 찾을 수 없어서 NoSuchBeanDefinitionException이 발생한다.
    assertThrows(NoSuchBeanDefinitionException.class,
            () -> ac.getBean("xxxxx", MemberService.class));
    }

}

결과는 다음과 같다.

인터페이스말고, 구현체로도 조회가 된다. 하지만 구체적으로 적는 건 좋지 않다.
왜냐하면 역할이 아니라 구현체에 의존하는 코드기 때문이다.

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

타입으로 조회시 같은 타입의 스프링 빈이 둘 이상이면 오류가 발생하는데, 이때는 빈 이름을 지정하면 된다.
참고로 ac.getBeansOfType()을 사용하면 해당 타입의 모든 빈을 조회할 수 있다.

동일한 타입이 둘 이상일 때 어떤 결과가 나오는지 보자.

class ApplicationContextSameBeanFindTest {

    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SameBeanConfig.class);

    @Test
    @DisplayName("타입으로 조회시 같은 타입이 둘 이상 있으면. 중복 오류가 빌셍힌디.")
    void findBeanByTypeDuplicatie() {
        MemberRepository bean = ac.getBean(MemberRepository.class);
    }

    @Configuration
    static class SameBeanConfig {

        @Bean
        public MemberRepository memberRepository1() {
            return new MemoryMemberRepository();
        }

        @Bean
        public MemberRepository memberRepository2() {
            return new MemoryMemberRepository();
        }
    }
}

실행결과는 다음과 같다.

예외 메세지는 다음과 같다.

org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'hello.core.member.MemberRepository' available: expected single matching bean but found 2: memberRepository1,memberRepository2

변경된 테스트 코드는 다음과 같다.
@DisplayName으로 어떤 테스트인지 확인할 수 있다.

...

import static org.assertj.core.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.*;

...

@Test
@DisplayName("타입으로 조회시 같은 타입이 둘 이상 있으면. 중복 오류가 빌셍힌디.")
void findBeanByTypeDuplicatie() {
    // ac.getBean(MemberRepository.class);
    assertThrows(NoUniqueBeanDefinitionException.class,
            () -> ac.getBean(MemberRepository.class));
}

@Test
@DisplayName("타입으로 조회시 같은 타입이 둘 이상 있으면, 빈 이름을 지정하면 된다.")
void findBeanByName() {
    MemberRepository memberRepository = ac.getBean("memberRepository1", MemberRepository.class);
    assertThat(memberRepository).isInstanceOf(MemberRepository.class);
}

@Test
@DisplayName("특정 타입을 모두 조회하기")
void findAllBeanByType() {
    Map<String, MemberRepository> beansOfType = ac.getBeansOfType(MemberRepository.class);
    for (String key : beansOfType.keySet()) {
        System.out.println("key = " + key + ", value = " + beansOfType.get(key)); // key와 value 출력

        System.out.println("beansOfType = " + beansOfType);
        assertThat(beansOfType.size()).isEqualTo(2); // 두 개인지 확인
    }
}

테스트 결과다.

참고로 "타입으로 조회시 같은 타입이 둘 이상 있으면, 빈 이름을 지정하면 된다."의 테스트 결과는 다음과 같다.

2-5. 스프링 빈 조회 - 상속 관계

스프링 빈의조회 기본 대원칙은 부모 타입으로 조회시 자식 타입의 빈들이 다 조회된다.
따라서 모든 자바 객체의 최고 부모인 'Object' 타입으로 조회하면 모든 스프링 빈을 조회한다.

다음은 테스트 코드이다.

import static org.assertj.core.api.Assertions.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));
        }
    }


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

}

결과는 다음과 같다.

2-6. BeanFactory와 ApplicationContext

BeanFactoryApplicationContext의 관계는 다음 그림과 같다.

BeanFactory

  • 스프링 컨테이너의 최상위 인터페이스다.
  • 스프링 빈을 관리하고 조회하는 역할을 담당한다.
  • getBean()을 제공한다.
  • 현재까지 사용했던 대부분의 기능은 BeanFactory가 제공하는 기능이다.

ApplicationContext

  • BeanFactory의 기능을 모두 상속받아서 제공한다.

BeanFactory와 ApplicationContext의 차이점

  • 애플리케이션을 개발할 때는 빈을 관리하고 조회하는 기능 뿐만 아니라, 수 많은 부가기능이 필요함. 그 부가기능들까지도 포함하는 인터페이스다. 다음 그림을 참고하자.

다음은 코드로 확인한 결과다.

MessageSource

  • 메시지소스를 활용한 국제화 기능을 제공한다.
  • 예) 한국에서 들어오면 한국어로, 영어권에서 들어오면 영어로 출력

EnvironmentCapable

  • 환경변수 관련 기능을 제공한다.
  • 로컬, 개발, 운영 환경 등을 구분해서 처리한다.

ApplicationEventPublisher

  • 애플리케이션 이벤트 기능을 제공한다.
  • 이벤트를 발행하고 구독하는 모델을 편리하게 지원

ResourcePatternResolver(ResourceLoader를 상속받음)

  • 편리한 리소스 조회기능을 제공한다.
  • 파일, 클래스패스, 외부 url 등에서 추상화를 통해 리소스를 편리하게 조회하게 해준다.

정리

  • ApplicationContext는 BeanFactory의 기능을 상속받는다.
  • ApplicationContext는 빈 관리기능 + 편리한 부가 기능을 제공한다.
  • BeanFactory를 직접 사용할 일은 거의 없다. 부가기능이 포함된 ApplicationContext를 사용한다.
  • BeanFactory나 ApplicationContext를 스프링 컨테이너라 한다.

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

스프링은 다양한 설정 형식을 지원한다.
스프링 컨테이너는 다양한 형식의 설정 정보를 받을 수 있도록 유연하게 설계되어 있다.

AnnotationConfigApplicationContext

  • 애너테이션 기반 자바 코드 설정에 사용한다.
  • AnnotationConfigApplicationContext를 사용하면서 자바 코드로된 설정 정보를 넘기면 된다.

GenericXmlApplicationContext

  • XML 설정 사용시 사용된다.

  • 최근에는 스프링 부트를 많이 사용하면서 XML기반의 설정은 잘 사용하지 않는다.

    • 아직 많은 프로젝트들이 XML로 되어 있다.
    • XML을 사용하면 컴파일 없이 빈 설정 정보를 변경할 수 있는 장점이 있다.
    • 한번쯤 배워두는 것도 괜찮음.
  • GenericXmlApplicationContext를 사용하면서 xml 설정 파일을 넘기면 된다.

resources 우클릭 - new - xml Configuration File - Spring Config를 통해서 xml파일을 생성한다.
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">

    <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>

참고로 파일 형식외엔 AppConfig.java 파일과 판박이다.
또 참고로 orderService의 생성자를 지정해줄 떄 순서가 올바르게 되어야 한다.

@Configuration
public class AppConfig {

    @Bean
    public MemberService memberService() {
        return new MemberServiceImpl(memberRepository());
    }

    @Bean
    public static MemoryMemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }

    @Bean
    public OrderService orderService() {
        return new OrderServiceImpl(memberRepository(), discountPolicy());
    }

    @Bean
    public DiscountPolicy discountPolicy() {
//        return new FixDiscountPolicy();
        return new RateDiscountPolicy();
    }
}

테스트 코드는 다음과 같다.

import static org.assertj.core.api.Assertions.assertThat;

public class XmlAppContext {

    @Test
    void XmlAppContext() {
        ApplicationContext ac = new GenericXmlApplicationContext("appConfig.xml"); // xml에서 가져온다.
        MemberService memberService = ac.getBean("memberService", MemberService.class);
        assertThat(memberService).isInstanceOf(MemberService.class);
    }
}

결과는 다음과 같다.

appConfig.xmlAppConfig.java의 설정 정보를 비교해보면 거의 같다.

2-8. 스프링 빈 설정 메타 정보 - BeanDefinition

스프링은 BeanDefinition이라는 추상화를 통해서 다양한 설정 형식을 지원한다.
빈 정보에 대한 것을 추상화해놓은 것이다. 이는 역할과 구현을 개념적으로 나눈 것이다.
예를 들면 다음과 같다.

  • XML을 읽어서 BeanDefinition을 만든다.
  • 자바 코드를 읽어서 BeanDefinition을 만든다.
  • 스프링 컨테이너는 설정이 자바 코드로 되어있는지, XML인지 몰라도 된다. 그냥 BeanDefinition만 알면 된다.

BeanDefinition을 빈 설정 메타정보라 한다.
@Bean , <bean>각각 하나씩 메타 정보가 생성된다.
스프링 컨테이너는 이 메타정보를 기반으로 스프링 빈을 생성한다.

위 이야기는 다음 그림으로 이해할 수 있다.

조금 더 깊이 있게 들어가보면...

  • AnnotationConfigApplicationContextAnnotatedBeanDefinitionReader를 사용해서 AppConfig.class를 읽고, BeanDefinition을 생성한다.
  • GenericXmlApplicationContextXmlBeanDefinitionReader를 사용해서 appConfig.xml의 설정 정보를 읽고, BeanDefinition을 생성한다.
  • 새로운 형식의 설정 정보가 추가되면, XxxBeanDefinitionReader를 만들어서 BeanDefinition을 생성하면 된다.

BeanDefinition 살펴보기

테스트 코드가 다음과 같을 때

public class BeanDefinitionTest {

    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
//    GenericXmlApplicationContext ac = new GenericXmlApplicationContext("appConfig.xml"); // 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$$SpringCGLIB$$0]; scope=singleton; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodNames=null; destroyMethodNames=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; initMethodNames=null; destroyMethodNames=[(inferred)]; defined in hello.core.AppConfig

beanDefinitionName = memberRepository, beanDefinition = Root bean: class [hello.core.AppConfig]; scope=; abstract=false; lazyInit=null; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=memberRepository; initMethodNames=null; destroyMethodNames=[(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; initMethodNames=null; destroyMethodNames=[(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; initMethodNames=null; destroyMethodNames=[(inferred)]; defined in hello.core.AppConfig

다음은 xml의 메타 정보다.

beanDefinitionName = memberService, beanDefinition = Generic bean: class [hello.core.member.MemberServiceImpl]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodNames=null; destroyMethodNames=null; defined in class path resource [appConfig.xml]

beanDefinitionName = memberRepository, beanDefinition = Generic bean: class [hello.core.member.MemoryMemberRepository]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodNames=null; destroyMethodNames=null; defined in class path resource [appConfig.xml]

beanDefinitionName = orderService, beanDefinition = Generic bean: class [hello.core.order.OrderServiceImpl]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodNames=null; destroyMethodNames=null; defined in class path resource [appConfig.xml]

beanDefinitionName = discountPolicy, beanDefinition = Generic bean: class [hello.core.discount.RateDiscountPolicy]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodNames=null; destroyMethodNames=null; defined in class path resource [appConfig.xml]

메타정보들은 다음과 같다.

  • BeanClassName: 생성할 빈의 클래스 명(자바 설정 처럼 팩토리 역할의 빈을 사용하면 없음)
  • factoryBeanName: 팩토리 역할의 빈을 사용할 경우 이름, 예) appConfig
  • factoryMethodName: 빈을 생성할 팩토리 메서드 지정, 예) memberService
  • Scope: 싱글톤(기본값)
  • lazyInit: 스프링 컨테이너를 생성할 때 빈을 생성하는 것이 아니라, 실제 빈을 사용할 때 까지 최대한 생성을 지연처리 하는지 여부
  • InitMethodName: 빈을 생성하고, 의존관계를 적용한 뒤에 호출되는 초기화 메서드 명
  • DestroyMethodName: 빈의 생명주기가 끝나서 제거하기 직전에 호출되는 메서드 명
  • Constructor arguments, Properties: 의존관계 주입에서 사용한다. (자바 설정 처럼 팩토리 역할의
    빈을 사용하면 없음)

이런 것들이 있다하고 넘어가면 됨.

정리

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

참고

스프링에 빈을 등록하는 방법은 두 가지로 크게 정리된다.
직접 스프링 빈을 등록하는 방법팩토리 메소드를 사용해서 등록하는 방법이 있다.
참고로 AppConfig.java와 같은 방법은 팩토리 메소드를 사용해서 등록하는 방법이다.
또한 appConfig.xml을 이용한 방법은 직접 스프링 빈을 등록하는 방법이다.

자바 기반 빈 Definition에는 factoryBeanName와 factoryMethodName의 값이 있지만,
xml 기반 빈 Definition에는 factoryBeanName와 factoryMethodName의 값이 없다.

xml로 설정하면서 Bean에 대한 클래스가 밖으로 드러났지만, config로 설정하는 방식으로 바뀌면서 팩토리 메소드를 사용하게 되었다.

3. 요약

스프링 컨테이너Bean을 생성하고 연결하고 조회하는 방법 등을 알아보았다.
다음 포스팅은 싱글톤 컨네이너에 대한 내용이다.

728x90
Comments