Spring - 스프링 컨테이너 코드로 파악하기 2) 빈 생성
상당 부분이 직접 코드를 분석하여 정리한 내용이라 틀린 부분이 있을 수도 있습니다! 잘못된 부분은 알려주시면 바로 수정하겠습니다.🙇♂️
1. 빈 생성
객체 생성은 컨테이너 생성 시 실행되는 AbstractApplicationContext의 refresh 메서드에서 확인할 수 있다.
refresh() 메서드에서 BeanFactory, BeanPostProcessor 사전 설정 작업들을 수행한 후 아래의 코드에서 빈을 생성한다.
1
2
3
4
5
6
7
public void refresh() throws BeansException, IllegalStateException {
...
// Instantiate all remaining (non-lazy-init) singletons.(실제 싱글톤 빈 생성)
finishBeanFactoryInitialization(beanFactory);
...
}
실제 빈을 생성하는 finishBeanFactoryInitialization 메서드의 일부이다. 빈의 생성은 ConfigurableListableBeanFactory가 담당하고 있다.
1
2
3
4
5
6
7
8
9
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
...
// Allow for caching all bean definition metadata, not expecting further changes.
beanFactory.freezeConfiguration();
// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();
}
beanFactory.preInstantiateSingletons() 메서드에서 빈의 메타데이터인 BeanDefinition을 바탕으로 싱글톤 빈의 생성이 이루어진다.
1) BeanFactory가 doCreateBean을 호출하는 과정
DefaultListableBeanFactory는 ConfigurableListableBeanFactory를 구현하는데 DefaultListableBeanFactory의 부모 클래스인 AbstractAutowireCapableBeanFactory까지 이동하면 doCreateBean 메서드를 찾을 수 있다.
1: preInstantitateSingletons가 동일한 클래스의 preInstantitateSingleton을 호출
2: preInstantitateSingleton이 동일한 클래스의 instantitateSingleton을 호출
3: instantitateSingleton에서 최종적으로 동일한 클래스의 getBean를 호출한다.
1
2
3
4
5
6
7
8
9
10
private void instantiateSingleton(String beanName) {
//팩토리 빈이면
if (isFactoryBean(beanName)) {
...
}
//팩토리 빈이 아닌 일반적인 빈이면
else {
getBean(beanName);
}
}
4: getBean에서 동일한 클래스의 resolveBean을 호출한다.
1
2
3
4
5
6
7
public <T> T getBean(Class<T> requiredType, @Nullable Object... args) throws BeansException {
...
Object resolved = resolveBean(ResolvableType.forRawClass(requiredType), args, false);
...
return (T) resolved;
}
5: resolveBean에서 최종적으로 동일한 클래스의 resolveNamedBean을 호출한다.
6: resolveNamedBean에서 AbstractBeanFactory 클래스의 getBean을 호출한다.
7: AbstractBeanFactory 클래스의 getBean이 doGetBean을 호출한다.
8: doGetBean이 최종적으로 AbstractAutowireCapableBeanFactory의 createBean을 호출한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
String beanName = transformedBeanName(name);
Object beanInstance;
// Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
...
}
else {
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
...
// Check if bean definition exists in this factory.
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
...
}
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate")
.tag("beanName", name);
try {
if (requiredType != null) {
beanCreation.tag("beanType", requiredType::toString);
}
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// Guarantee initialization of beans that the current bean depends on.
// 의존하는 빈들을 먼저 생성해야 생성할 수 있도록
String[] dependsOn = mbd.getDependsOn();
//순환 참조 검사
if (dependsOn != null) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
registerDependentBean(dep, beanName);
try {
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
...
}
catch (BeanCreationException ex) {
...
}
}
}
// 싱글톤 빈 생성
// Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
//프로토타입 스코프 빈 생성
else if (mbd.isPrototype()) {
...
}
//유효한 스코프 이름이 아님
else {
...
}
}
//빈 생성 과정 에러 처리
catch (BeansException ex) {
...
}
finally {
beanCreation.end();
if (!isCacheBeanMetadata()) {
clearMergedBeanDefinition(beanName);
}
}
}
return adaptBeanInstance(name, beanInstance, requiredType);
}
9: createBean이 doCreateBean(String, RootBeanDefinition, Object[])을 호출한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
...
try {
//=====빈 인스턴스 생성=====
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isTraceEnabled()) {
logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
// A previously detected exception with proper bean creation context already,
// or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(
mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
}
}
2) doCreateBean
doCreateBean에서 실제 객체의 생성, 의존성 주입, refresh 메서드에서 등록된 BeanPostProcessor를 사용하여 빈의 전처리, 후처리가 수행된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// 1. 빈 생성
// Instantiate the bean.
...
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
...
// 2. 순환 참조 해결을 위해 완성되지 않은 경우 캐싱
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
...
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
//3. 의존성 주입
populateBean(beanName, mbd, instanceWrapper);
//4. 빈 초기화 및 BeanPostProcessor 적용
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
...
}
//5. 순환 참조 해결 시도. ExposedObject가 미완성 빈인지 확인하고 캐시로 해결.
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
// 순환 참조 해결할 수 없을 때
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
...
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
//6. 빈이 소멸할 때 처리할 작업이 있다면 등록
// Register bean as disposable.
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObject;
}
다음 포스트에서 doCreateBean 내부의 빈 라이프사이클 수행 과정을 조금 더 자세하게 알아볼 예정이다.