IoC Conatiner in SpringApplication.run() - 2

koonlx·2024년 11월 9일

Spring

목록 보기
6/7

registerSingleton 메서드 및 연관 메서드 상세 설명

Spring의 registerSingleton 메서드는 DefaultListableBeanFactory에서 싱글톤 빈을 등록할 때 호출된다. 싱글톤 빈의 수동 등록 및 타입별 캐시 초기화 작업을 수행하여 애플리케이션 전반에서 동일한 빈 인스턴스를 사용할 수 있도록 한다. 이 과정에서 Consumer, Predicate, synchronized와 같은 함수형 인터페이스와 동기화 키워드가 사용된다. 또한, 순환 참조와 빈 초기화 지연 문제를 해결하기 위한 earlySingletonObjects와 singletonFactories 캐시, 빈 생성의 안정성을 보장하는 manualSingletonNames 리스트 관리도 이 과정에 포함된다.

registerSingleton() 호출 흐름

@Override
public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {
    super.registerSingleton(beanName, singletonObject);
    updateManualSingletonNames(set -> set.add(beanName), set -> !this.beanDefinitionMap.containsKey(beanName));
    clearByTypeCache();
}
  • super.registerSingleton(beanName, singletonObject);
    DefaultSingletonBeanRegistry의 registerSingleton 메서드를 호출하여 singletonObjects 캐시에 beanName과 singletonObject를 등록합니다.
    이 과정에서 이미 동일한 이름의 빈이 존재하면 IllegalStateException을 던져 중복 등록을 방지한다.
  • updateManualSingletonNames(set -> set.add(beanName), set -> !this.beanDefinitionMap.containsKey(beanName));
    수동으로 등록된 싱글톤 빈 이름 목록을 업데이트한다. updateManualSingletonNames는 Consumer와 Predicate를 사용하여 빈의 수동 등록을 관리한다.
    • Consumer<Set<String>>: 빈 이름(beanName)을 manualSingletonNames 목록에 추가하는 역할을 한다. 여기서는 set -> set.add(beanName) 표현식을 사용하여 특정 조건을 만족하면 beanName을 추가한다.
    • Predicate<Set<String>>: beanDefinitionMap에 beanName이 존재하지 않는 경우에만 manualSingletonNames에 추가한다. 이는 기존에 등록된 빈과 충돌하지 않도록 조건을 검사하는 역할을 한다.
    • 빈 생성 시작 시점 구분 (hasBeanCreationStarted()):
      hasBeanCreationStarted() 메서드는 빈 생성이 이미 시작된 이후에는 manualSingletonNames를 변경하지 않도록 안정성을 보장한다.
      빈 생성이 시작되지 않았으면, 직접 manualSingletonNames 목록에 추가하고, 빈 생성이 시작된 후에는 기존 manualSingletonNames를 복사해 업데이트하여 빈 생성 중 불안정한 상태에서의 수동 등록을 안정적으로 관리한다.
  • clearByTypeCache()
    clearByTypeCache는 타입별 캐시를 초기화하여 새로운 빈 정보가 반영될 수 있도록 한다.
    새로운 빈이 등록되면 기존의 타입별 캐시가 유효하지 않을 수 있으므로, 이를 초기화하여 최신 상태를 유지하게 한다.
    이 타입 캐시는 빈 검색 성능을 높이는 데 중요한 역할을 하며, clearByTypeCache()는 빈이 새로 추가될 때마다 최신 정보를 반영하여 정확성을 보장한다.

DefaultSingletonBeanRegistry.registerSingleton()

DefaultSingletonBeanRegistry는 Spring IoC 컨테이너에서 싱글톤 빈을 등록하고 관리하는 핵심 레지스트리 클래스이다. 여기서 registerSingleton은 싱글톤 캐시에 빈을 등록하는 핵심 로직을 포함한다.

@Override
public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {
    Assert.notNull(beanName, "Bean name must not be null");
    Assert.notNull(singletonObject, "Singleton object must not be null");
    synchronized (this.singletonObjects) {
        Object oldObject = this.singletonObjects.get(beanName);
        if (oldObject != null) {
            throw new IllegalStateException("Could not register object [" + singletonObject +
                    "] under bean name '" + beanName + "': there is already object [" + oldObject + "] bound");
        }
        addSingleton(beanName, singletonObject);
    }
}
  • 유효성 검사: beanName과 singletonObject가 null이 아닌지 검사한다.
  • 중복 등록 검사: singletonObjects 캐시에서 동일한 이름의 빈이 이미 존재하는지 확인한다. 동일한 이름의 빈이 있다면 예외를 던져 중복 등록을 방지한다.
  • 동기화 블록 (synchronized): singletonObjects 캐시에 대한 접근을 synchronized로 감싸서, 여러 스레드가 동시에 이 맵을 수정하지 못하게 한다. 이를 통해 멀티스레드 환경에서 데이터 일관성을 보장하고, 빈 등록 과정에서 발생할 수 있는 경쟁 상태를 방지한다.
    • synchronized 키워드는 동기화 처리를 통해 하나의 스레드만 이 블록에 접근하여 빈을 안전하게 등록할 수 있도록 한다.
  • addSingleton 호출: 중복이 아니라면 addSingleton 메서드를 호출하여 실제로 singletonObjects에 빈을 등록한다.

addSingleton()

addSingleton 메서드는 singletonObjects 캐시에 빈을 저장하여 이후에 빈을 재사용할 수 있도록 한다. 동시에, 순환 참조 해결과 초기화 지연 문제 해결을 위해 earlySingletonObjects와 singletonFactories 캐시에서 해당 빈을 제거한다.

protected void addSingleton(String beanName, Object singletonObject) {
    synchronized (this.singletonObjects) {
        this.singletonObjects.put(beanName, singletonObject);
        this.singletonFactories.remove(beanName);
        this.earlySingletonObjects.remove(beanName);
        this.registeredSingletons.add(beanName);
    }
}
  • singletonObjects.put(beanName, singletonObject): singletonObjects 맵에 beanName과 singletonObject를 저장하여 싱글톤 빈이 IoC 컨테이너에 등록되도록 한다.
  • 캐시 정리:
    • earlySingletonObjects와 singletonFactories 는 순환 참조 및 초기화 지연 문제를 해결하기 위한 캐시로, 초기화 중이거나 미완성된 빈을 잠시 저장하는 역할을 한다.
    • addSingleton에서는 최종적으로 빈이 등록되었음을 의미하므로, earlySingletonObjects와 singletonFactories에서 beanName을 제거하여 불완전한 상태가 아님을 명확히 한다.
    • registeredSingletons.add(beanName)을 통해 수동으로 등록된 빈 이름을 목록에 추가하여 다른 빈이나 컴포넌트에서 동일한 빈 이름으로 빈을 조회할 수 있게 한다.

updateManualSingletonNames()

updateManualSingletonNames 메서드는 수동으로 등록된 싱글톤 빈 이름을 관리하며, 빈이 정의되지 않은 경우 수동으로 추가된 싱글톤을 manualSingletonNames 목록에 추가한다.

private void updateManualSingletonNames(Consumer<Set<String>> action, Predicate<Set<String>> condition) {
    if (hasBeanCreationStarted()) {
        synchronized (this.beanDefinitionMap) {
            if (condition.test(this.manualSingletonNames)) {
                Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
                action.accept(updatedSingletons);
                this.manualSingletonNames = updatedSingletons;
            }
        }
    } else {
        if (condition.test(this.manualSingletonNames)) {
            action.accept(this.manualSingletonNames);
        }
    }
}
  • Consumer와 Predicate의 사용:
    • Consumer<Set<String>>: manualSingletonNames 목록에 빈 이름을 추가하는 작업을 정의한다. action.accept(updatedSingletons)에서 실행된다.
    • Predicate<Set<String>>: manualSingletonNames 목록이 업데이트 가능한지를 검사합니다. 여기서는
      set -> !this.beanDefinitionMap.containsKey(beanName)를 통해 빈 정의가 없는 경우에만 수동 등록하도록 조건을 확인한다.
  • 빈 생성 여부 확인 (hasBeanCreationStarted):
    • hasBeanCreationStarted() 메서드를 통해 빈 생성이 이미 시작된 이후라면, manualSingletonNames를 새로운 객체로 복사해 업데이트하여, 빈 생성 중 수동 등록 리스트가 변경되는 불안정한 상태를 방지한다.

clearByTypeCache()

clearByTypeCache 메서드는 타입별 캐시를 초기화하여 새로 추가된 빈이 올바르게 반영될 수 있도록 한다.

private void clearByTypeCache() {
    this.allBeanNamesByType.clear();
    this.singletonBeanNamesByType.clear();
}
  • 캐시 초기화:
    • allBeanNamesByType와 singletonBeanNamesByType는 빈을 타입별로 캐시하여 빈 조회 성능을 높이지만, 빈이 새롭게 추가되면 해당 캐시가 최신 상태를 반영하지 않을 수 있다.
    • 새로 등록된 빈을 반영하기 위해 캐시를 초기화하여, 이후 빈 조회 시 최신 정보를 사용하게 한다.

해당 코드 부분은 SpringApplicaion.run() 메소드 내에서 호출된 prepareContext() 메소드로 IoC Container의 register()에 해당한다고 볼 수 있다. Spring Framework에서는 Singleton 패턴으로 Bean을 등록하는 것을 알 수 있다.

Spring IoC 컨테이너에서의 register 과정 요약

  • 빈 등록 (register): registerSingleton 메서드는 특정 빈을 IoC 컨테이너에 등록하는 과정을 의미하며, beanName과 singletonObject를 IoC 컨테이너의 싱글톤 캐시에 추가하여 애플리케이션이 빈을 사용할 수 있도록 준비한다.
  • 빈 생명 주기 관리: earlySingletonObjects, singletonFactories와 같은 캐시는 순환 참조 문제 해결과 지연 초기화 관리를 지원하여, 등록된 빈이 올바르게 초기화되도록 돕는다. 이는 IoC 컨테이너가 빈을 관리할 때 발생할 수 있는 복잡한 상황을 처리하기 위한 메커니즘이다.
  • 타입별 캐시 관리: clearByTypeCache()와 같은 캐시 초기화 작업은 IoC 컨테이너가 빈을 더 효율적으로 검색할 수 있게 지원하며, 이를 통해 빈을 빠르게 찾고 의존성 주입이 원활히 이루어지도록 한다.
profile
Server Developer

0개의 댓글