spring系列——BeanNameGenerator接口(bean名字生成器)

目錄

1、BeanNameGenerator 接口

2、AnnotationBeanNameGenerator 類

3、DefaultBeanNameGenerator 類 


spring容器是通過bean Name去管理着大量的bean,而且不出錯。

首先看看spring是如何爲每個bean生成名字的,BeanNameGenerator接口是bean名字生成器的入口,下面是類圖:

1、BeanNameGenerator 接口

就一個方法,爲某個bean生成名字,參數爲BeanDefinition 和 BeanDefinitionRegistry 類型。此處可以反映出spring需要的bean結構是BeanDefinition。

public interface BeanNameGenerator {
    String generateBeanName(BeanDefinition var1, BeanDefinitionRegistry var2);
}

2、AnnotationBeanNameGenerator 類

從這個類名基本可以知道,這個類是爲那些用註解方式的bean生成名字。暫時不作介紹。

public class AnnotationBeanNameGenerator implements BeanNameGenerator {
    //@Component註解(這個功能類)的類名
    private static final String COMPONENT_ANNOTATION_CLASSNAME = "org.springframework.stereotype.Component";
    public AnnotationBeanNameGenerator() { }
    //生成bean的名字,實現了接口中的方法
    public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
        if (definition instanceof AnnotatedBeanDefinition) {
            String beanName = this.determineBeanNameFromAnnotation((AnnotatedBeanDefinition)definition);
            if (StringUtils.hasText(beanName)) {
                return beanName;
            }
        }

        return this.buildDefaultBeanName(definition, registry);
    }

    @Nullable
    protected String determineBeanNameFromAnnotation(AnnotatedBeanDefinition annotatedDef) {
        AnnotationMetadata amd = annotatedDef.getMetadata();
        Set<String> types = amd.getAnnotationTypes();
        String beanName = null;
        Iterator var5 = types.iterator();

        while(var5.hasNext()) {
            String type = (String)var5.next();
            AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(amd, type);
            if (attributes != null && this.isStereotypeWithNameValue(type, amd.getMetaAnnotationTypes(type), attributes)) {
                Object value = attributes.get("value");
                if (value instanceof String) {
                    String strVal = (String)value;
                    if (StringUtils.hasLength(strVal)) {
                        if (beanName != null && !strVal.equals(beanName)) {
                            throw new IllegalStateException("Stereotype annotations suggest inconsistent component names: '" + beanName + "' versus '" + strVal + "'");
                        }

                        beanName = strVal;
                    }
                }
            }
        }

        return beanName;
    }

    protected boolean isStereotypeWithNameValue(String annotationType, Set<String> metaAnnotationTypes, @Nullable Map<String, Object> attributes) {
        boolean isStereotype = annotationType.equals("org.springframework.stereotype.Component") || metaAnnotationTypes.contains("org.springframework.stereotype.Component") || annotationType.equals("javax.annotation.ManagedBean") || annotationType.equals("javax.inject.Named");
        return isStereotype && attributes != null && attributes.containsKey("value");
    }

    protected String buildDefaultBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
        return this.buildDefaultBeanName(definition);
    }

    protected String buildDefaultBeanName(BeanDefinition definition) {
        String beanClassName = definition.getBeanClassName();
        Assert.state(beanClassName != null, "No bean class name set");
        String shortClassName = ClassUtils.getShortName(beanClassName);
        return Introspector.decapitalize(shortClassName);
    }
}

3、DefaultBeanNameGenerator 類 

下面是源碼,就一個方法實現。

public class DefaultBeanNameGenerator implements BeanNameGenerator {
    public DefaultBeanNameGenerator() {}
    //實現接口方法,爲某個bean生成名字
    public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
        return BeanDefinitionReaderUtils.generateBeanName(definition, registry);
    }
}

接着追蹤,看看BeanDefinitionReaderUtils 的 generateBeanName()方法

public static String generateBeanName(BeanDefinition beanDefinition, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException {
        return generateBeanName(beanDefinition, registry, false);
}
//此方法被上面方法調用
public static String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry, boolean isInnerBean) throws BeanDefinitionStoreException {
        //獲取bean的類名(全路徑類名)
        String generatedBeanName = definition.getBeanClassName();
        //如果類名爲空時
        if (generatedBeanName == null) {
            //bean的父bean名字不爲空時
            if (definition.getParentName() != null) {
                //此bean的名字就爲:父bean名+$child
                generatedBeanName = definition.getParentName() + "$child";
            } 
            //bean的工廠bean名字不爲空時
            else if (definition.getFactoryBeanName() != null) {
                //此bean的名字就爲:生產它的工廠bean名+$created
                generatedBeanName = definition.getFactoryBeanName() + "$created";
            }
        }
        //bean的名字爲null、“”、“ ”時,拋出異常
        if (!StringUtils.hasText(generatedBeanName)) {
            throw new BeanDefinitionStoreException("Unnamed bean definition specifies neither 'class' nor 'parent' nor 'factory-bean' - can't generate bean name");
        } 
        //此處isInnerBean 爲false
        else if (isInnerBean) {
            String id = generatedBeanName + "#" + ObjectUtils.getIdentityHexString(definition);
            return id;
        } else {
            //返回bean的名字
            return uniqueBeanName(generatedBeanName, registry);
        }
}

繼續追蹤,看看uniqueBeanName()方法:

這個方法的意思是:如果generateBeanName()方法生成的bean名字,已經被註冊了(即已經存在了),那麼就在名字後加上#和數字,比如hehe名字已經存在了,那麼此時如果generateBeanName()方法生成的名字還是hehe,那麼此時就在hehe後面加上#0(hehe#0),就是#後的數字遞增,來區分,因爲bean的名字必須唯一。

public static String uniqueBeanName(String beanName, BeanDefinitionRegistry registry) {
        String id = beanName;
        for(int counter = -1; counter == -1 || registry.containsBeanDefinition(id); id = beanName + "#" + counter) {
            ++counter;
        }
        return id;
}

bean名字生成的流程總結:bean將自己的全路徑類名作爲自己的bean名字,如果沒有類名,那就看是否有父bean,如果有,假設父bean名字爲hehe,那麼就用hehe$child作爲此子bean的名字,如果沒有父bean,那就看bean的工廠bean的名字,如果有,假設工廠bean名字爲haha,那麼bean的名字就是haha$created,如果沒有工廠,那就報錯“既沒有自己的類名、也沒有父bean類名、也沒有工廠bean類名”。不管最終用的是哪一個的名字,對這個名字進行唯一性檢查,如果名字重複了(已經有這個名字存在了),那就在名字後面+#+數字,這樣,每個bean的名字就是唯一的了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章