Spring Boot에서 Dependency Injection(DI)을 해주기 위해서 @RequiredArgsConstructor를 주로 사용하곤 합니다.
Dependency Injection 이 어떠한 과정을 통해서 이루어지는 것인지 디버깅을 통해서 알아보도록 하겠습니다.
productService 라는 serivce 클래스에 productRepository, wishClient 2개의 repository를 주입해 보겠습니다.
들어가기에 앞서, 바로 코드를 보면 과정이 길고 복잡하기에 흐름을 놓칠 수 있다고 생각되어
사용되는 클래스와 메서드들을 먼저 정리해 보았습니다.
(1번에서 설명한 코드가 2번에서 사용되는 형태로 진행됩니다.)
1. 목차 및 순서
- (Interface) [BeanUtils.java] instantiateClass
- 주어진 생성자와 인자를 사용하여 클래스의 인스턴스를 생성합니다.
- 리플렉션을 활용하여 접근 제한이 있는 생성자도 사용할 수 있게 합니다.
- (Class) [SimpleInstantiationStrategy.java] instantiate (여기서 위의 1번 코드 사용)
- 객체 생성 전략 중 하나로, BeanUtils.instantiateClass를 사용하여 실제 인스턴스화를 수행합니다.
- (Class) [ConstroctorResolver.java] instantiate (여기서 위의 2번 코드 사용)
- 생성자를 통한 인스턴스화를 시도하기 전에, 커스텀 인스턴스화 전략이 있는지 확인하고,
- 없다면 SimpleInstantiationStrategy를 사용합니다.
- (Class) [ConstroctorResolver.java] autowireConstructor (여기서 위의 3번 코드 사용) ★★★
- 생성자 주입을 위한 핵심 메서드로, 생성자 파라미터 타입에 맞는 빈을 찾아 의존성을 주입합니다.
- (Interface) [AbstractAutowireCapableBeanFactory.java] autowireConstructor (여기서 위의 4번 코드 사용)
- ConstructorResolver를 사용하여 특정 빈의 생성자에 의존성을 주입하는 과정을 관리합니다.
- (Interface) [AbstractAutowireCapableBeanFactory.java] createBeanInstance (여기서 위의 5번 코드 사용)
- 빈의 인스턴스를 생성하는 단계에서, 생성자 주입 방식을 선택하여 autowireConstructor를 호출합니다.
- (Interface) [AbstractAutowireCapableBeanFactory.java] doCreateBean (여기서 위의 6번 코드 사용)
- 빈의 전체 생성 과정을 관리하며, 인스턴스 생성 후 초기화까지의 과정을 담당합니다.
- (Interface) [AbstractAutowireCapableBeanFactory.java] CreateBean (여기서 위의 7번 코드 사용)
- 공개 메서드로, 외부에서 빈 생성을 요청할 때 호출되며, doCreateBean 메서드를 통해 빈 생성 과정을 시작합니다.
- (Interface) [AbstractBeanFactory.java] doGetBean (여기서 위의 8번 코드 사용)
- 요청된 빈의 인스턴스를 가져오는 과정에서, 빈이 아직 생성되지 않았다면 생성 과정을 시작합니다.
- (Class) [DefaultSingletonBeanRegistry.java] getSingleton
- 싱글톤 빈의 캐시를 관리하며, 요청된 빈의 싱글톤 인스턴스를 반환합니다.
- (Interface) [AbstractBeanFactory.java] doGetBean (여기서 위의 10번 코드 사용)
- getSingleton 메서드를 호출하여 싱글톤 빈 인스턴스를 가져옵니다.
- getObjectForBeanInstance 메서드를 호출하여 실제 반환될 빈 인스턴스를 처리합니다.
- (Interface) [AbstractBeanFactory.java] getBean (여기서 위의 11번 코드 사용)
- 빈을 요청하는 가장 일반적인 진입점으로, doGetBean을 호출하여 실제 빈을 로드합니다.
- (Class) [DependencyDescriptor.java] resolveCandiate (여기서 위의 12번 코드 사용)
- 의존성을 해결하기 위해 적합한 빈 후보를 찾습니다.
- (Class) [DefaultListableBeanFactory.java] doResolveDependency (여기서 위의 13번 코드 사용)
- 의존성을 해결하는 구체적인 로직을 담당하며, 필요한 빈을 찾거나 생성합니다.
- (Class) [DefaultListableBeanFactory.java] resolveDependency (여기서 위의 14번 코드 사용)
- 의존성 해결 요청을 받고, doResolveDependency를 호출하여 처리합니다.
2. 주요 Class 별 역할 및 메서드
위에서 언급된 클래스들은 Spring Framework의 의존성 주입 과정에서 중요한 역할을 수행합니다.
각 클래스의 주요 역할을 정리하면 다음과 같습니다.
1) ConstructorResolver
- 역할 : 생성자 기반 인스턴스화와 생성자 주입의 핵심 로직을 담당합니다.
- 주요 메서드 :
- instantiate : 커스텀 인스턴스화 전략을 확인하고, 없을 경우 SimpleInstantiationStrategy를 사용하여 객체를 인스턴스화합니다.
- autowireConstructor : 생성자 파라미터 타입에 맞는 적절한 빈을 찾아 의존성을 주입합니다. 이는 생성자 주입의 핵심 과정입니다.
2) AbstractAutowireCapableBeanFactory
- 역할 : 빈의 생성, 인스턴스화, 의존성 주입, 초기화 등을 종합적으로 관리합니다.
- 주요 메서드 :
- autowireConstructor : ConstructorResolver를 활용하여 특정 빈의 생성자에 의존성을 주입하는 과정을 조정합니다.
- createBeanInstance와 doCreateBean : 빈의 인스턴스를 생성하고 초기화하는 과정을 담당합니다. 여기서 생성자 주입 방식이 선택되면, autowireConstructor를 호출합니다.
3) AbstractBeanFactory
- 역할 : 빈을 요청하는 가장 일반적인 진입점을 제공합니다.
- 주요 메서드 :
- doGetBean과 getBean : 요청된 빈의 인스턴스를 가져오는 과정을 관리합니다. 빈이 아직 생성되지 않았다면, 생성 과정을 시작합니다.
4) DefaultListableBeanFactory
- 역할 : BeanFactory 인터페이스의 구현체로, 의존성 해결과 빈 관리의 구체적인 로직을 담당합니다.
- 주요 메서드 :
- doResolveDependency와 resolveDependency : 의존성을 해결하는 과정을 관리합니다. 필요한 빈을 찾거나 새로 생성합니다.
3. 코드 진행 과정
1) [BeanUtils.java] instantiateClass
ctor.newInstance(argsWithDefaultValues)를 호출하여 준비된 인자를 사용해 새로운 인스턴스를 생성합니다.
이 과정에서 생성자의 파라미터로 전달된 인자들을 기반으로 객체가 인스턴스화됩니다.
productService에 추가하려는 의존성인 productRepository와 wishClient 2개가 파라미터로 전달됩니다.
2) [SimpleInstantiationStrategy.java] instantiate
Spring의 객체 생성 전략 중 하나로, 주어진 생성자와 인자를 사용하여 객체를 인스턴스화하는 과정을 담당합니다.
메서드 오버라이드가 없고, 보안 관리자가 설정된 경우에도 안전하게 생성자 접근성을 변경하여 객체를 생성할 수 있습니다.
3) [ConstroctorResolver.java] instantiate
Spring Framework의 빈 생성 과정 중 하나를 나타내며, 특정 빈의 인스턴스를 생성하기 위해 사용됩니다.
보안 관리자의 존재 유무에 따라 다르게 동작하며, InstantiationStrategy를 사용하여 실제 인스턴스화를 수행합니다.
보안 관리자가 없는 경우, 직접 strategy.instantiate 메서드를 호출하여 빈의 인스턴스를 생성합니다.
4) [ConstroctorResolver.java] autowireConstructor
instantiate 메서드를 호출하여 빈의 인스턴스를 생성하고, 이 인스턴스를 BeanWrapperImpl 객체(bw)에 설정합니다.
instantiate 메서드는 주어진 생성자(constructorToUse)와 인자(argsToUse)를 사용하여 빈의 새 인스턴스를 생성하는 역할을 합니다.
constructorToUse : productService
argsToUse : productRepository, wishClient
생성된 인스턴스는 이후에 Spring IoC 컨테이너에 의해 관리되며, 필요한 의존성 주입, 초기화 콜백 실행 등의 추가 처리가 이루어집니다.
5) [AbstractAutowireCapableBeanFactory.java] autowireConstructor
Spring의 의존성 주입 과정 중 생성자 주입을 담당합니다.
ConstructorResolver를 사용하여 복잡한 생성자 선택 로직과 의존성 주입을 처리하며, 생성된 빈 인스턴스를 BeanWrapper로 포장하여 반환합니다.
6) [AbstractAutowireCapableBeanFactory.java] createBeanInstance
이 메서드는 주어진 빈 이름(beanName), 빈 정의(RootBeanDefinition mbd), 그리고 선택적 생성자 인자(args)를 사용하여 빈의 인스턴스를 생성하는 과정을 담당합니다.
determineConstructorsFromBeanPostProcessors를 호출하여 자동 주입에 사용할 후보 생성자를 결정합니다.
후보 생성자가 있거나, 빈 정의에서 자동 주입 모드가 설정되어 있거나, 생성자 인자 값이 설정되어 있거나, 명시적 인자가 제공된 경우
autowireConstructor를 호출하여 생성자를 통한 자동 주입을 시도합니다.
7) [AbstractAutowireCapableBeanFactory.java] doCreateBean
빈의 인스턴스를 생성합니다.
싱글톤 빈의 경우, 미리 생성된 인스턴스가 캐시에 있을 수 있으므로, 캐시에서 인스턴스를 가져옵니다.
캐시에 없는 경우, createBeanInstance 메서드를 호출하여 새 인스턴스를 생성합니다.
8) [AbstractAutowireCapableBeanFactory.java] CreateBean
doCreateBean 메서드를 호출하여 빈의 인스턴스를 실제로 생성하고 초기화합니다.
이 과정에는 인스턴스화, 의존성 주입, 초기화 콜백 실행 등이 포함됩니다.
9) [AbstractBeanFactory.java] doGetBean
doGetBean 메서드는 빈의 조회 및 생성 과정에서 중심적인 역할을 합니다.
빈이 요청될 때, 이 메서드는 빈의 인스턴스가 이미 존재하는지 싱글톤 캐시에서 확인하고, 존재하지 않는 경우 새로운 인스턴스를 생성합니다.
이 과정에서 부모 빈 팩토리의 조회, 의존성의 사전 처리, 그리고 실제 빈의 인스턴스화 및 초기화 과정이 포함됩니다.
10) [DefaultSingletonBeanRegistry.java] getSingleton
singletonObjects 맵에 대한 접근을 동기화하여, 동시성 문제를 방지합니다.
singletonObjects는 이미 생성된 싱글톤 인스턴스를 저장하는 캐시입니다.
먼저, 캐시에서 해당 빈 이름의 싱글톤 인스턴스를 조회합니다. 이미 존재하는 경우, 이를 직접 반환합니다.
캐시에 인스턴스가 없는 경우, singletonFactory.getObject()를 호출하여 새로운 싱글톤 인스턴스를 생성합니다.
이 과정에서 beforeSingletonCreation과 afterSingletonCreation 메서드를 호출하여 싱글톤 생성 전후의 처리를 수행합니다.
새로 생성된 싱글톤 인스턴스를 singletonObjects 캐시에 등록합니다.
11) [AbstractBeanFactory.java] doGetBean
getSingleton 메서드를 호출하여 싱글톤 빈 인스턴스를 가져옵니다.
getObjectForBeanInstance 메서드를 호출하여 실제 반환될 빈 인스턴스를 처리합니다.
12) [AbstractBeanFactory.java] getBean
빈을 요청하는 가장 일반적인 진입점으로, doGetBean을 호출하여 실제 빈을 로드합니다.
13) [DependencyDescriptor.java] resolveCandiate
의존성을 해결하기 위해 적합한 빈 후보를 찾습니다.
14) [DefaultListableBeanFactory.java] doResolveDependency
resolveCandidate 메서드는 주어진 정보(빈 이름, 타입, 빈 팩토리)를 사용하여 적절한 빈 인스턴스를 찾아내는 역할을 합니다.
DependencyDescriptor에 정의된 조건과 일치하는 빈을 현재 애플리케이션 컨텍스트에서 찾아내고, 해당 빈 인스턴스를 반환합니다.
15) [DefaultListableBeanFactory.java] resolveDependency
실제로 의존성을 해결하는 핵심 로직을 수행합니다.
여기서 의존성 기술자(descriptor), 요청 빈 이름(requestingBeanName), 자동 주입된 빈 이름들(autowiredBeanNames), 그리고 타입 변환기(typeConverter)를 사용하여,
요구되는 의존성에 맞는 빈을 찾아내고, 필요한 경우 타입 변환을 수행하여 반환합니다.
4. 정리
- 의존성 요청 : 컴포넌트가 초기화될 때, 생성자를 통해 필요한 의존성이 Spring IoC 컨테이너에 요청됩니다.
- 빈 이름 변환 및 조회 : 요청된 빈의 이름이 식별되고 (transformedBeanName), 싱글톤 캐시에서 해당 인스턴스를 조회합니다 (getSingleton). 이미 존재하는 경우, 즉시 반환됩니다.
- 빈 인스턴스 생성
- 인스턴스화 전략 결정 : BeanUtils.instantiateClass를 사용해 리플렉션을 통한 인스턴스 생성이 이루어집니다. 접근 제한된 생성자도 사용할 수 있습니다.
- SimpleInstantiationStrategy 사용 : 기본 객체 생성 전략으로, 실제 인스턴스화를 담당합니다.
- ConstructorResolver의 instantiate : 필요한 경우 커스텀 인스턴스화 전략을 확인하고 적용합니다.
- 생성자 주입
- ConstructorResolver의 autowireConstructor : 생성자 파라미터와 일치하는 타입의 빈을 찾아 의존성을 주입합니다.
- AbstractAutowireCapableBeanFactory의 autowireConstructor : 적절한 생성자에 의존성을 주입합니다.
- 빈 초기화
- createBeanInstance : 선택된 생성자 주입 방식에 따라 인스턴스를 생성합니다.
- doCreateBean : 인스턴스 생성 후, 초기화 과정을 포함한 빈의 전체 생성 과정을 관리합니다.
- createBean : 외부 요청에 의해 호출되며, 빈의 생성 과정을 시작합니다.
- 의존성 해결
- doGetBean : 요청된 빈이 아직 생성되지 않았다면, 해당 빈의 생성 과정을 시작합니다.
- resolveDependency : DefaultListableBeanFactory에서 추가적인 의존성 해결 요청을 받고, 필요한 빈을 찾거나 생성합니다.
'Spring Boot' 카테고리의 다른 글
[Spring Boot] Database Lock (Pessimistic Lock, Optimistic Lock, Named Lock) (0) | 2024.03.16 |
---|---|
[Spring Cloud] Feign Client 성능 최적화: 다중 호출에서 일괄 처리로 (1) | 2024.02.18 |