Bean 생성
AnnotationConfigApplicationContext
AnnotationConfigApplicationContext
클래스는 어노테이션을 읽어 bean을 등록하는 역할을 담당하고 있다.
그 중에서도 기본 생성자에 포함되어 반드시 호출하게 되어 있는 register라는 메소드를 살펴보았다.
/**
* Register one or more component classes to be processed.
* <p>Note that {@link #refresh()} must be called in order for the context
* to fully process the new classes.
* @param componentClasses one or more component classes — for example,
* {@link Configuration @Configuration} classes
* @see #scan(String...)
* @see #refresh()
*/
@Override
public void register(Class<?>... componentClasses) {
Assert.notEmpty(componentClasses, "At least one component class must be specified");
this.reader.register(componentClasses);
}
새 클래스를 완전히 처리하기 위해 반드시 호출된다고 한다.
여기의 두 번째 doRegisterBean 메소드를 따라가면 AnnotatedBeanDefinitionReader
클래스에 도달한다.
AnnotatedBeanDefinitionReader
AnnotatedBeanDefinitionReader
의 주석에는 Convenient adapter for programmatic registration of bean classes 라는 설명이 붙어 있다.
그리고 아까 확인하고자 했던 doRegisterBean의 java doc에는 Register a bean from the given bean class, deriving its metadata from class-declared annotations. 라고 설명되어 있다.
개발자가 annotation 기반으로 bean을 생성하면 이 클래스의 doRegisterBean 메소드에서 등록을 담당하는 것이다.
/**
* Register a bean from the given bean class, deriving its metadata from
* class-declared annotations.
* @param beanClass the class of the bean
* @param name an explicit name for the bean
* @param qualifiers specific qualifier annotations to consider, if any,
* in addition to qualifiers at the bean class level
* @param supplier a callback for creating an instance of the bean
* (may be {@code null})
* @param customizers one or more callbacks for customizing the factory's
* {@link BeanDefinition}, e.g. setting a lazy-init or primary flag
* @since 5.0
*/
private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
@Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
@Nullable BeanDefinitionCustomizer[] customizers) {
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
return;
}
abd.setInstanceSupplier(supplier);
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
abd.setScope(scopeMetadata.getScopeName());
String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
if (qualifiers != null) {
for (Class<? extends Annotation> qualifier : qualifiers) {
if (Primary.class == qualifier) {
abd.setPrimary(true);
}
else if (Lazy.class == qualifier) {
abd.setLazyInit(true);
}
else {
abd.addQualifier(new AutowireCandidateQualifier(qualifier));
}
}
}
if (customizers != null) {
for (BeanDefinitionCustomizer customizer : customizers) {
customizer.customize(abd);
}
}
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}
이 메소드는 bean의 메타데이터를 저장하는 클래스를 몇 개 사용하고 있다.
맨 첫줄의 AnnotatedGenericBeanDefinition
는 bean의 정의를 갖는 클래스이다.
부모 클래스를 추적하다 보면 추상 클래스인 AbstractBeanDefinition
에 도달하게 된다.
AbstractBeanDefinition
AbstractBeanDefinition
는 bean의 속성을 정의하는 클래스이다.
bean을 생성할 때 굉장히 많은 옵션을 사용할 수 있는데,
(Autowired를 주입받는 기준이라던가, Primary bean으로 생성할 것인지, lazy 로딩 옵션을 사용할 것인지 등)
이 곳에 그 정보들이 담겨있다.
위부터 필드를 몇 개 적어보았다.
Scope 관련 (static final 변수)
- String SCOPE_DEFAULT : bean의 scope (기본은 싱글턴이라고 주석에 설명되어 있다.)
AUTOWIRE 옵션 (static final 변수)
- int AUTOWIRE_NO
- int AUTOWIRE_BY_NAME
- int AUTOWIRE_BY_TYPE
- int AUTOWIRE_CONSTRUCTOR
의존성 점검 옵션 (static final 변수)
- int DEPENDENCY_CHECK_NONE
- int DEPENDENCY_CHECK_OBJECTS
- int DEPENDENCY_CHECK_SIMPLE
- DEPENDENCY_CHECK_SIMPLE
객체 저장
- Object beanClass : 스프링이 관리하는 bean의 실체
... 등등 굉장히 많은 필드가 있다. 다 쓰긴 너무 많으니 직접 살펴보자.
bean을 인스턴스로 직접 생성하지 않고 정보를 저장한다는 점이 중요한 부분이다.
아마 bean scope(프로토타입인 경우)에 따라 인스턴스를 계속 생성해야 하기 때문인 것으로 생각된다.
다음으로 볼 수 있는 클래스는 ScopeMetadata
이다.
ScopeMetadata
ScopeMetadata
는 bean Scope의 메타 데이터를 저장한다.
클래스를 열자마자 맨 윗줄에 중요한 정보 하나를 확인할 수 있다.
private String scopeName = BeanDefinition.SCOPE_SINGLETON;
private ScopedProxyMode scopedProxyMode = ScopedProxyMode.NO;
Default scope는 싱글톤이며 proxy 모드를 사용하지 않는다.
(proxy 모드는 bean scope를 공부하면 알 수 있다)
BeanDefinitionHolder
bean 생성 과정에서 마지막으로 볼 수 있는 메타 데이터 클래스이다.
Spring 개발자들은 이 클래스를 이렇게 설명하고 있다.
/**
* Holder for a BeanDefinition with name and aliases.
* Can be registered as a placeholder for an inner bean.
*
* <p>Can also be used for programmatic registration of inner bean
* definitions. If you don't care about BeanNameAware and the like,
* registering RootBeanDefinition or ChildBeanDefinition is good enough.
*
* @author Juergen Hoeller
* @since 1.0.2
* @see org.springframework.beans.factory.BeanNameAware
* @see org.springframework.beans.factory.support.RootBeanDefinition
* @see org.springframework.beans.factory.support.ChildBeanDefinition
*/
name과 alias를 갖는 홀더이며 BeanDefinitionReaderUtils
의 registerBeanDefinition 메소드 인자로 넘겨주기 위해 사용한다.
메소드로 넘겨주는 두 번째 인자는 BeanDefinitionRegistry
인터페이스인데, 실질적으로 bean이 등록되는 곳이다.
/**
* Register the given bean definition with the given bean factory.
* @param definitionHolder the bean definition including name and aliases
* @param registry the bean factory to register with
* @throws BeanDefinitionStoreException if registration failed
*/
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// Register bean definition under primary name.
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// Register aliases for bean name, if any.
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
메소드 위에서부터 두 번째 라인에 있는 메소드를 보자.
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
이 메소드를 설명하는 주석엔 "Register a new bean definition with this registry." 라고 적혀있다.
registerBeanDefinition의 정의를 타고 올라가면 DefaultListableBeanFactory
클래스에 도달한다.
아래는 해당 클래스의 registerBeanDefinition 메소드 원문이다.
//---------------------------------------------------------------------
// Implementation of BeanDefinitionRegistry interface
//---------------------------------------------------------------------
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
else if (existingDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if (logger.isInfoEnabled()) {
logger.info("Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
existingDefinition + "] with [" + beanDefinition + "]");
}
}
else if (!beanDefinition.equals(existingDefinition)) {
if (logger.isDebugEnabled()) {
logger.debug("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
synchronized (this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
removeManualSingletonName(beanName);
}
}
else {
// Still in startup registration phase
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
removeManualSingletonName(beanName);
}
this.frozenBeanDefinitionNames = null;
}
if (existingDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
}
첫 주석부터 BeanDefinitionRegistry
의 구현체라고 쓰여 있는 것을 볼 수 있다.
중간에 나타나는 BeanDefinition
은 beanDefinitionMap 필드에서 get(beanDefinitionMap) 메소드를 실행 결과를 저장한다.
beanDefinitionMap은 뭘까?
/** Map of bean definition objects, keyed by bean name. */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
beanDefinitionMap은 이름 그대로 beanDefinition을 저장한 Map이었다.
BeanDefinition
을 저장하는 과정을 마저 살펴보자.
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
...
} else {
...
}
BeanDefinition
을 beanDefinitionMap에서 꺼내온다.
현재 등록 중인 bean은 이전에 등록한 적이 없는 새삥이다.
개발자는 if문이 아니라 else문에서 bean의 정의를 저장한다고 의도했을 것이다.
else문을 확인해보자.
else {
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
synchronized (this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
removeManualSingletonName(beanName);
}
}
else {
// Still in startup registration phase
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
removeManualSingletonName(beanName);
}
this.frozenBeanDefinitionNames = null;
}
첫 문장부터 조건문이 나온다.
이름만 보면 Spring의 라이프사이클 상 bean 생성 단계가 시작되었냐는 것으로 생각된다.
/**
* Check whether this factory's bean creation phase already started,
* i.e. whether any bean has been marked as created in the meantime.
* @since 4.2.2
* @see #markBeanAsCreated
*/
protected boolean hasBeanCreationStarted() {
return !this.alreadyCreated.isEmpty();
}
실제로 주석을 살펴보면 그렇다고 한다.
그럼 여기서도 else문으로 이동한다.
아직은 bean 등록 단계이기 때문이다.
else문에서는 beanDefinitionMap에 실제로 bean 정의 데이터를 저장한다.
this.beanDefinitionMap.put(beanName, beanDefinition);
BeanDefinition
은 이름값을 아주 잘 하는 인터페이스로, scope 등 bean에 대한 정의가 담겨 있다.
더 자세한 정보는 공식 레퍼런스를 참조하자.
이 곳에 bean의 정의를 등록하는 것으로 생성을 위한 절차는 끝난다.
다음엔 정의가 등록된 bean의 인스턴스를 어떻게 생성하는지 탐구해볼 예정이다.
'Java > Spring framework' 카테고리의 다른 글
Spring boot 자동 설정 분석 (0) | 2020.03.15 |
---|---|
Spring framework 소스코드 읽어보기 - Bean 생성 원리 (3) (0) | 2020.02.23 |
Spring framework core (12) - Spring AOP (0) | 2020.01.24 |
Spring framework core (11) - SpEL (0) | 2020.01.24 |
Spring framework core (10) - Data binding (0) | 2020.01.17 |