Spring源碼分析(十)注冊解析的BeanDefinition


摘要:本文結合《Spring源碼深度解析》來分析Spring 5.0.6版本的源代碼。若有描述錯誤之處,歡迎指正。

 

對配置文件解析完成后,獲取的beanDefiniton已經可以進行使用了,剩下的唯一工作就是注冊了,也就是processBeanDefinition方法中的BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry())代碼的解析了。

/**
 * 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.
    // 使用beanName做唯一標識注冊
    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);
        }
    }
}

從上面的代碼可以看出,解析的beanDefinition都會被注冊到BeanDefinitionRegistry類型的實例中,而對於beanDefinition的注冊分成了兩部分:通過beanName的注冊以及通過別名的注冊。

1. 通過beanName注冊BeanDefinition

對於beanDefinition的注冊,是將beanDefinition直接放入了map中,以beanName為key。但不僅僅如此,代碼如下:

@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 {
            /**
             * 注冊前的最后一次校驗,這里的校驗不同於之前的XML文件校驗,
             * 主要是對於AbstractBeanDefinition屬性中的methodOverrides校驗,
             * 校驗methodOverrides是否與工廠方法並存或者methodOverrides對應的方法根本不存在
             */
            ((AbstractBeanDefinition) beanDefinition).validate();
        }
        catch (BeanDefinitionValidationException ex) {
            throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                    "Validation of bean definition failed", ex);
        }
    }

    BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
    // 處理注冊已經注冊的beanName情況
    if (existingDefinition != null) {
        // 如果對應的beanName已經注冊且在配置中配置了bean不允許被覆蓋,則拋出異常。
        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)
            // 因為beanDefinitionMap是全局變量,這里定會存在並發訪問的情況
            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;
                if (this.manualSingletonNames.contains(beanName)) {
                    Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
                    updatedSingletons.remove(beanName);
                    this.manualSingletonNames = updatedSingletons;
                }
            }
        }
        else {
            // Still in startup registration phase
            // 注冊beanDefinition
            this.beanDefinitionMap.put(beanName, beanDefinition);
            // 記錄beanName
            this.beanDefinitionNames.add(beanName);
            this.manualSingletonNames.remove(beanName);
        }
        this.frozenBeanDefinitionNames = null;
    }

    if (existingDefinition != null || containsSingleton(beanName)) {
        // 重置所有beanName對應的緩存
        resetBeanDefinition(beanName);
    }
}

上面的代碼中我們看到,在對於bean的注冊處理方式上,主要進行了幾個步驟:

(1) 對AbstractBeanDefinition的校驗。在解析XML文件的時候我們提過校驗,但是此校驗非彼校驗,之前的校驗是針對XML格式的校驗,而此時的校驗是針對AbstractBeanDefinition的methodOverrides屬性的。

(2)對beanName已經注冊的情況的處理。如果設置了不允許bean的覆蓋,則需要拋出異常,否則直接覆蓋。

(3)加入map緩存。

(4)清楚解析之前留下的對應beanName的緩存。

2. 通過別名注冊BeanDefinition

在理解了注冊bean的原理后,理解注冊別名的原理就容易多了。

@Override
public void registerAlias(String name, String alias) {
    Assert.hasText(name, "'name' must not be empty");
    Assert.hasText(alias, "'alias' must not be empty");
    synchronized (this.aliasMap) {
        // 如果beanName與alias相同的話不記錄alias,並刪除對應的alias
        if (alias.equals(name)) {
            this.aliasMap.remove(alias);
            if (logger.isDebugEnabled()) {
                logger.debug("Alias definition '" + alias + "' ignored since it points to same name");
            }
        }
        else {
            // 如果alias不允許被覆蓋則拋出異常
            String registeredName = this.aliasMap.get(alias);
            if (registeredName != null) {
                if (registeredName.equals(name)) {
                    // An existing alias - no need to re-register
                    return;
                }
                if (!allowAliasOverriding()) {
                    throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" +
                            name + "': It is already registered for name '" + registeredName + "'.");
                }
                if (logger.isDebugEnabled()) {
                    logger.debug("Overriding alias '" + alias + "' definition for registered name '" +
                            registeredName + "' with new target name '" + name + "'");
                }
            }
            // 當A -> B存在時,若再次出現 A -> C -> B時候則會拋出異常
            checkForAliasCircle(name, alias);
            this.aliasMap.put(alias, name);
            if (logger.isTraceEnabled()) {
                logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'");
            }
        }
    }
}

由以上代碼中可以得知注冊alias的步驟如下:

(1)beanName與alias相同情況處理。若alias和beanName名稱相同則不需要處理並刪除原有的alias。

(2)alias覆蓋處理。若aliasName已經使用並已經指向了另一beanName則需要用戶的設置進行處理。

(3)alias循環檢查。當A -> B存在時,若再次出現 A -> C -> B時候則會拋出異常。

(4)注冊alias。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM