본문 바로가기
IT/Spring

Annotation Bean register

by 봉즙 2023. 9. 7.

ApplicationContext 의 구현체

GenericXmlApplicationContext
의존성 주입을 지원하기 위한 클래스이며, XML 기반의 설정 파일을 사용하여 Bean 을 관리하게 해준다.

AnnotationConfigApplicationContext
XML이 아닌 어노테이션을 통해 Bean 을 관리하게 해준다.

AnnotationConfigApplicationContext

public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {

    @Override
    public void register(Class<?>... componentClasses) {
        Assert.notEmpty(componentClasses, "At least one component class must be specified");
        StartupStep registerComponentClass = this.getApplicationStartup().start("spring.context.component-classes.register")
                .tag("classes", () -> Arrays.toString(componentClasses));
        this.reader.register(componentClasses);
        registerComponentClass.end();
    }
}

AnnotationConfigApplicationContext 에서 사용한 register()

AnnotatedBeanDefinitionReader.class 에 있는

public class AnnotatedBeanDefinitionReader {

    public void register(Class<?>... componentClasses) {
        for (Class<?> componentClass : componentClasses) {
            registerBean(componentClass);
        }
    }

    public void registerBean(Class<?> beanClass) {
        doRegisterBean(beanClass, null, null, null, null);
    }

    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.setAttribute(ConfigurationClassUtils.CANDIDATE_ATTRIBUTE, Boolean.TRUE);
        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);
    }

}

doRegisterBean() 메서드에 의해 Bean이 등록된다.

ScopeMetadata는 Bean Scope의 정보를 가진다.

BeanDefinitionHolder는 BeanDefinition와 name, alias 정보를 가진다.

마지막에 BeanDefinitionReaderUtilsregisterBeanDefinition()를 호출한다.

public abstract class BeanDefinitionReaderUtils {
    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);
            }
        }
    }
}

registerBeanDefinition()registerAlias()을 사용하여 Bean을 등록한다.

두 메서드 모두 Argument 로 넘겨준 registry 에 있는 메서드이다.

BeanDefinitionRegistry는 abstract 이며 디버그로 확인해보면 AnnotationConfigApplicationContext 가 사용되었음을 알 수 있다.

public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {

    private final AnnotatedBeanDefinitionReader reader;

    private final ClassPathBeanDefinitionScanner scanner;

    public AnnotationConfigApplicationContext() {
        StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create");
        this.reader = new AnnotatedBeanDefinitionReader(this);
        createAnnotatedBeanDefReader.end();
        this.scanner = new ClassPathBeanDefinitionScanner(this);
    }
}

생성자에서 AnnotatedBeanDefinitionReader를 생성하는데 자기 자신을 넘겨준다.

AnnotationConfigApplicationContextGenericApplicationContext를 상속하고 AnnotationConfigRegistry 구현하였다.

public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {

    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
            throws BeanDefinitionStoreException {

        this.beanFactory.registerBeanDefinition(beanName, beanDefinition);
    }
}

여기서 DefaultListableBeanFactory.registerBeanDefinition() 를 호출한다.

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
        implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {

    @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 abd) {
            try {
                abd.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 (isAlias(beanName)) {
                if (!isAllowBeanDefinitionOverriding()) {
                    String aliasedName = canonicalName(beanName);
                    if (containsBeanDefinition(aliasedName)) {  // alias for existing bean definition
                        throw new BeanDefinitionOverrideException(
                                beanName, beanDefinition, getBeanDefinition(aliasedName));
                    } else {  // alias pointing to non-existing bean definition
                        throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                                "Cannot register bean definition for bean '" + beanName +
                                        "' since there is already an alias for bean '" + aliasedName + "' bound.");
                    }
                } else {
                    removeAlias(beanName);
                }
            }
            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);
        } else if (isConfigurationFrozen()) {
            clearByTypeCache();
        }
    }

    ;
}

Bean은 Map<String, BeanDefinition>에 저장된다. 저장되는 타입이 Bean instance가 아닌 BeanDefinition 인 이유가 있다.
BeanDefinition을 우선 저장 하고 getBean()이 호출 될때 Scope를 판단해 Singleton이면 항상 같은 객체를 반환하고, prototype이면 항상 새로운 객체를 생성해서 반환 하기 위해서 이다.

'IT > Spring' 카테고리의 다른 글

Interceptor를 사용하여 Request/Reponse Logging  (0) 2023.09.20
Private Method Test 하기  (0) 2023.09.14
security 6.1 마이그레이션  (1) 2023.05.19
Base64Utils Deprecated  (0) 2023.02.28
static 으로 Bean 주입  (0) 2023.02.28

댓글