Skip to content

Latest commit

 

History

History
139 lines (113 loc) · 11.7 KB

File metadata and controls

139 lines (113 loc) · 11.7 KB

01 — 빈팩토리 계층과 핵심 추상화

무엇을 / 왜

컨테이너를 코드로 이해하려면 먼저 두 개의 축을 분리해서 봐야 한다.

  1. 컨테이너 계약(BeanFactory 계층) — "빈을 어떻게 꺼내고 조회하는가"를 정의하는 인터페이스들. 클라이언트가 보는 얼굴과, 설정자가 보는 얼굴, 컨테이너 내부가 보는 얼굴이 인터페이스 분리 원칙(ISP)에 따라 층층이 나뉜다.
  2. 빈 메타데이터(BeanDefinition) — "어떤 빈을 어떻게 만들지"를 담은 데이터. 실행 코드가 아니라 설정의 표현이며, 따라서 인스턴스화 전에 자유롭게 수정할 수 있다.

이 장은 이 두 축의 타입 지도를 그려, 이후 02~04장의 동작 흐름이 "어느 인터페이스의 어느 메서드"에서 일어나는지 좌표를 제공한다.

핵심 타입 — BeanFactory 계층

                         ┌───────────────────────┐
                         │      BeanFactory       │  클라이언트 뷰
                         │  getBean / containsBean │  "빈을 꺼낸다"
                         │  isSingleton / getType  │
                         └───────────┬───────────┘
                ┌────────────────────┼─────────────────────┐
                ▼                    ▼                     ▼
   ┌─────────────────────┐ ┌──────────────────┐ ┌────────────────────────┐
   │ HierarchicalBean    │ │ ListableBean     │ │ AutowireCapableBean    │
   │ Factory             │ │ Factory          │ │ Factory                │
   │ getParentBeanFactory│ │ 모든 빈 이름 열거  │ │ 컨테이너 밖 객체에       │
   │ (부모-자식 컨테이너)  │ │ getBeanNamesForType│ │ DI/초기화 적용          │
   └──────────┬──────────┘ └────────┬─────────┘ └───────────┬────────────┘
              │ +SingletonBeanRegistry        │                │
              ▼                               │                │
   ┌─────────────────────┐                    │                │
   │ ConfigurableBean    │                    │                │
   │ Factory             │  설정자 뷰          │                │
   │ addBeanPostProcessor │ registerScope      │                │
   │ setConversionService │ registerAlias      │                │
   └──────────┬──────────┘                    │                │
              │       ┌─────────────────────────────────────────┘
              ▼       ▼                        ▼
        ┌───────────────────────────────────────────┐
        │       ConfigurableListableBeanFactory       │  내부 종합 뷰
        │  getBeanDefinition / preInstantiateSingletons│
        │  ignoreDependencyType / freezeConfiguration  │
        └───────────────────────────────────────────┘

각 인터페이스의 존재 이유:

  • BeanFactory — 가장 기본인 "빈 꺼내기" 계약. getBean(name), getBean(Class), getBeanProvider(...), containsBean, isSingleton/isPrototype, getType. 여기 정의된 String FACTORY_BEAN_PREFIX = "&"FactoryBean 자체를 꺼낼 때 쓰는 접두사다(&myFactory → 팩토리 자신, myFactory → 팩토리가 만든 객체).
  • HierarchicalBeanFactory — 부모 컨테이너를 둘 수 있게 한다. getBean은 자식에서 못 찾으면 부모에 위임한다(자식이 같은 이름을 가리면 자식 우선).
  • ListableBeanFactory — 타입으로 빈을 열거한다(getBeanNamesForType, getBeansOfType). 단, 부모는 보지 않고 이 팩토리만 본다(전수 조회는 BeanFactoryUtils가 보강).
  • ConfigurableBeanFactory — 컨테이너를 구성한다. BeanPostProcessor 등록, 스코프 등록, ConversionService/EmbeddedValueResolver 설정, 빈 소멸. SingletonBeanRegistry도 상속해 외부 싱글톤을 수동 등록할 수 있다.
  • AutowireCapableBeanFactory — 컨테이너가 관리하지 않는 객체에도 DI·초기화·BeanPostProcessor를 적용한다. 서블릿이나 JPA 엔티티처럼 컨테이너 밖에서 생성된 객체에 의존성을 주입할 때 쓰인다(autowireBean, initializeBean).
  • ConfigurableListableBeanFactory — 위 모두를 합친 내부용 종합 인터페이스. getBeanDefinition, 비-지연 싱글톤 일괄 생성(preInstantiateSingletons), 설정 동결(freezeConfiguration).

핵심 타입 — 구현 계층

   FactoryBeanRegistrySupport
        │ (FactoryBean 결과 캐싱)
        ▼
   AbstractBeanFactory                implements ConfigurableBeanFactory
        │  · doGetBean 템플릿 (스코프 분기, depends-on)
        │  · getMergedLocalBeanDefinition (부모-자식 정의 병합)
        │  · BeanPostProcessor 목록 보관
        ▼
   AbstractAutowireCapableBeanFactory implements AutowireCapableBeanFactory
        │  · createBean / doCreateBean (인스턴스화·주입·초기화)
        │  · populateBean, initializeBean, autowireByType/Name
        ▼
   DefaultListableBeanFactory  ★      implements ConfigurableListableBeanFactory,
        │                                        BeanDefinitionRegistry
        │  · BeanDefinition 레지스트리(Map<String,BeanDefinition>)
        │  · resolveDependency (타입 기반 autowiring의 본체)
        │  · preInstantiateSingletons

세 클래스는 템플릿 메서드 패턴으로 역할을 나눈다. 위쪽은 "조회/스코프/수명주기의 뼈대"를, 가운데는 "한 빈을 실제로 만드는 절차"를, 맨 아래 DefaultListableBeanFactory는 "정의를 저장하고 타입으로 후보를 찾는" 레지스트리 역할을 더한다. DefaultSingletonBeanRegistry(03장)는 FactoryBeanRegistrySupport의 상위로서 싱글톤 캐시를 제공한다.

핵심 타입 — BeanDefinition 메타데이터

BeanDefinitionAttributeAccessor, BeanMetadataElement를 상속한 순수 데이터 인터페이스다. 클래스 이름, 스코프, 생성자 인자(ConstructorArgumentValues), 프로퍼티 값(MutablePropertyValues), init/destroy 메서드 이름, lazy/primary/fallback 같은 플래그를 담는다.

        BeanDefinition (interface)
            ▲        ▲
            │        │
   AbstractBeanDefinition (공통 상태/기본값)
       ▲        ▲          ▲
       │        │          │
 RootBean   ChildBean   GenericBean
 Definition  Definition  Definition
   │           │            │
   │  부모 참조  │   범용(파싱·   │
   │  병합 결과  │   프로그래밍 등록)
  • GenericBeanDefinition — 설정 시점에 쓰는 범용 정의. 부모 이름을 문자열로 들고 있다가 나중에 병합된다.
  • RootBeanDefinition — 런타임에 병합된 최종 정의. 컨테이너는 빈을 만들기 직전 부모-자식 정의를 합쳐 RootBeanDefinition(흔히 mbd = merged bean definition)을 만든다. 여기에는 해소된 타입(resolvedTargetType), 해소된 생성자(resolvedConstructorOrFactoryMethod) 같은 런타임 캐시 필드도 붙는다.
  • ChildBeanDefinition — 부모를 명시적으로 가리키는 자식 정의(요즘은 GenericBeanDefinition + setParentName을 더 많이 쓴다).

BeanDefinition인터페이스이고 수정 가능하다는 점이 핵심 설계다. 덕분에 BeanFactoryPostProcessor가 인스턴스화 전에 정의를 들여다보고 프로퍼티를 바꿔치기할 수 있다(예: @Value("${...}") 자리표시자 치환). 자세한 건 06장.

동작 흐름 — "타입으로 정의가 어떻게 좌표를 얻는가"

getBean("foo") 한 번이 계층을 어떻게 타고 내려가는지 한눈에:

  client.getBean("foo")
        │  (BeanFactory)
        ▼
  AbstractBeanFactory.doGetBean("foo")                 ◀ 템플릿 메서드
        │  1. transformedBeanName: 별칭·"&" 접두사 해소
        │  2. getSingleton("foo")  → 캐시 히트면 즉시 반환
        │  3. 부모 위임 여부 판단 (Hierarchical)
        │  4. getMergedLocalBeanDefinition("foo") → RootBeanDefinition
        │  5. 스코프 분기 (singleton / prototype / custom)
        ▼
  AbstractAutowireCapableBeanFactory.createBean(mbd)   ◀ 실제 생성 (02장)
        │
        ▼
  DefaultListableBeanFactory.resolveDependency(...)    ◀ 주입 대상 찾기 (04장)

doGetBean은 자신이 직접 객체를 만들지 않는다. 스코프를 판단해 "싱글톤이면 캐시 통해, 프로토타입이면 매번, 커스텀 스코프면 Scope.get() 통해" createBean언제 호출할지만 조율한다. 객체를 만드는 책임은 하위 클래스(createBean)로 위임된다 — 전형적인 템플릿 메서드 패턴.

핵심 메서드

  • AbstractBeanFactory.transformedBeanName(name) — 입력 이름을 정규화한다. &(FactoryBean) 접두사를 떼고, 별칭(alias)을 표준 이름으로 바꾼다. 모든 조회의 첫 단계.
  • getMergedLocalBeanDefinition(beanName) — 부모 정의가 있으면 부모-자식을 재귀적으로 병합해 RootBeanDefinition을 만들고 캐시한다. 이후 모든 생성/타입 판단은 이 병합 정의를 본다.
  • BeanFactory.getBeanProvider(Class) — 즉시 꺼내지 않고 지연 핸들(ObjectProvider)을 돌려준다. 선택적 의존성(getIfAvailable), 유일성 검사(getIfUnique), 스트리밍(orderedStream)을 지원해 @Autowired Optional<X>/List<X> 같은 시나리오의 프로그래밍 버전을 제공한다.

설계 포인트 / 확장점

  • 인터페이스 분리(ISP) — 같은 DefaultListableBeanFactory 인스턴스라도, 애플리케이션 코드에는 BeanFactory로, 후처리기에는 ConfigurableListableBeanFactory로, 외부 객체 주입에는 AutowireCapableBeanFactory다른 얼굴을 보여줄 수 있다.
  • 메타데이터와 실행의 분리BeanDefinition이 데이터이기에, 설정 소스(XML/애너테이션/Java/Groovy)가 무엇이든 결국 같은 BeanDefinition으로 수렴한다. 컨테이너 코어는 설정 형식을 모른다.
  • FactoryBean — 빈 생성 로직 자체를 빈으로 만드는 훅. getObject()가 반환하는 것이 실제 노출 빈이 된다(프록시 팩토리, JNDI 등 프레임워크가 애용).

정리

spring-beans의 골격은 "계약 계층(BeanFactoryConfigurableListableBeanFactory)"과 "구현 계층(AbstractBeanFactoryDefaultListableBeanFactory)", 그리고 그 사이를 흐르는 데이터인 "BeanDefinition"의 3요소로 이해할 수 있다. 클라이언트는 getBean만 보지만, 그 한 줄은 별칭 해소 → 정의 병합 → 스코프 분기 → 생성 → 의존성 해소라는 잘 짜인 템플릿을 따라 내려간다. 다음 장은 그 "생성" 단계를 현미경으로 본다.