Spring中的beanName

Spring中的beanName

在Spring中每一個注冊到容器中的Bean都有自己的名字(至少一個)挑社,可能不止一個(別名)。對于未明確指定name的Bean,Spring會自動為其生成一個名字。而對于在xml中配置的Bean和使用諸如Service、Component等注解標識的Bean与殃,Spring為其生成名字的方式并不相同单山,下面我們一一分析。

核心接口

核心接口.png

BeanNameGenerator接口定義如下

public interface BeanNameGenerator {

    /**
     * Generate a bean name for the given bean definition.
     * @param definition the bean definition to generate a name for
     * @param registry the bean definition registry that the given definition
     * is supposed to be registered with
     * @return the generated bean name
     */
    String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry);

}

BeanNameGenerator是生成beanName的頂級接口幅疼,而它有兩個實現(xiàn)類米奸,圖中左側的DefaultBeanNameGenerator是給XML配置中Bean使用的,圖中右側的AnnotationBeanNameGenerator則是給通過注解定義的Bean使用的爽篷。

XML配置

在此不贅述XML文件中Bean的解析過程悴晰,直接來看DefaultBeanNameGenerator,其調用鏈路為

DefaultBeanNameGenerator#generateBeanName—>BeanDefinitionReaderUtils#generateBeanName

最后這個方法的定義如下

public static String generateBeanName(
            BeanDefinition definition, BeanDefinitionRegistry registry, boolean isInnerBean)
            throws BeanDefinitionStoreException {
        // 先拿類名賦值
        String generatedBeanName = definition.getBeanClassName();
        if (generatedBeanName == null) {
            if (definition.getParentName() != null) {
                generatedBeanName = definition.getParentName() + "$child";
            }
            else if (definition.getFactoryBeanName() != null) {
                generatedBeanName = definition.getFactoryBeanName() + "$created";
            }
        }
        if (!StringUtils.hasText(generatedBeanName)) {
            throw new BeanDefinitionStoreException("Unnamed bean definition specifies neither " +
                    "'class' nor 'parent' nor 'factory-bean' - can't generate bean name");
        }

        String id = generatedBeanName;
        if (isInnerBean) {
            // 內部bean逐工,在少數(shù)情況下走該分支铡溪,例如使用key-ref等標簽時
            // Inner bean: generate identity hashcode suffix.
            id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + ObjectUtils.getIdentityHexString(definition);
        }
        else {
            // Top-level bean: use plain class name.
            // Increase counter until the id is unique.
            // 為了保證id唯一,在其后加數(shù)字
            int counter = -1;
            while (counter == -1 || registry.containsBeanDefinition(id)) {
                counter++;
                id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + counter;
            }
        }
        return id;
    }

注釋都寫在了上面泪喊,邏輯很簡單:類名+“#”+數(shù)字

注解配置

public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
        if (definition instanceof AnnotatedBeanDefinition) {
            // 如果注解的value指定了beanName棕硫,則使用該值
            String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
            if (StringUtils.hasText(beanName)) {
                // Explicit bean name found.
                return beanName;
            }
        }
        // Fallback: generate a unique default bean name.
        // 如果沒有指定value,則為其生成beanName
        return buildDefaultBeanName(definition, registry);
    }

繼續(xù)追蹤

protected String buildDefaultBeanName(BeanDefinition definition) {
        String shortClassName = ClassUtils.getShortName(definition.getBeanClassName());
        return Introspector.decapitalize(shortClassName);
    }

Introspector.decapitalize的代碼如下

public static String decapitalize(String name) {
        if (name == null || name.length() == 0) {
            return name;
        }
        if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
                        Character.isUpperCase(name.charAt(0))){
            return name;
        }
        char chars[] = name.toCharArray();
        chars[0] = Character.toLowerCase(chars[0]);
        return new String(chars);
    }

通過上面兩段代碼可以看出邏輯如下

  1. 取短類名袒啼,即不包含包路徑的類名哈扮,例如com.test.Student的短類名為Student,這點跟XML配置中取全類名不一樣
  2. 如果短類名長度大于1蚓再,且第一個和第二個字符為大寫滑肉,則直接返回短類名,也就是說假設類為com.test.STudent摘仅,則beanName為STudent
  3. 其他情況下將短類名首字符小寫后返回靶庙,假設類為com.test.Student,則beanName為student

驗證

由于只為了驗證beanName娃属,簡單起見惶洲,Bean類中都為空

People類

@Component
public class Pepole {
}

TNtt類

@Service
public class TNttt {
}

TestPepole類

public class TestPepole {
}

TNTt類

public class TNTt {
}

其中TestPepole和TNTt通過XML配置

<bean class="com.hust.TestPepole"></bean>
    <bean class="com.hust.TNTt"></bean>

測試主類

public class App {
    public static void main(String[] args) throws IOException {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:application.xml");
        System.out.println(new Gson().toJson(BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Pepole.class)));
        System.out.println(new Gson().toJson(BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext,
                TNttt.class)));
        System.out.println(new Gson().toJson(BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext,
                TestPepole.class)));
        System.out.println(new Gson().toJson(BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext,
                TNTt.class)));
    }
}

輸出結果

["pepole"]
["TNttt"]
["com.hust.TestPepole#0"]
["com.hust.TNTt#0"]

總結

  • 在不指定beanName的情況下,Spring會自動為注冊的Bean生成一個唯一的beanName
  • 通過注解注冊的Bean和XML注冊的Bean膳犹,Spring為其生成默認beanName的機制不一樣
  • 不要盲目覺得通過注解注冊的Bean,Spring為其生成beanName就是將短類名的首字母小寫签则,當短類名的首字符和第二個字符均大寫時须床,beanName就是短類名
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市渐裂,隨后出現(xiàn)的幾起案子豺旬,更是在濱河造成了極大的恐慌,老刑警劉巖柒凉,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件族阅,死亡現(xiàn)場離奇詭異,居然都是意外死亡膝捞,警方通過查閱死者的電腦和手機坦刀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人鲤遥,你說我怎么就攤上這事沐寺。” “怎么了盖奈?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵混坞,是天一觀的道長。 經(jīng)常有香客問我钢坦,道長究孕,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任爹凹,我火速辦了婚禮厨诸,結果婚禮上,老公的妹妹穿的比我還像新娘逛万。我一直安慰自己泳猬,他們只是感情好,可當我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布宇植。 她就那樣靜靜地躺著得封,像睡著了一般。 火紅的嫁衣襯著肌膚如雪指郁。 梳的紋絲不亂的頭發(fā)上忙上,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天,我揣著相機與錄音闲坎,去河邊找鬼疫粥。 笑死,一個胖子當著我的面吹牛腰懂,可吹牛的內容都是我干的梗逮。 我是一名探鬼主播,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼绣溜,長吁一口氣:“原來是場噩夢啊……” “哼慷彤!你這毒婦竟也來了?” 一聲冷哼從身側響起怖喻,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤底哗,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后锚沸,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體跋选,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年哗蜈,在試婚紗的時候發(fā)現(xiàn)自己被綠了前标。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片坠韩。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖候生,靈堂內的尸體忽然破棺而出同眯,到底是詐尸還是另有隱情,我是刑警寧澤唯鸭,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布须蜗,位于F島的核電站,受9級特大地震影響目溉,放射性物質發(fā)生泄漏明肮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一缭付、第九天 我趴在偏房一處隱蔽的房頂上張望柿估。 院中可真熱鬧,春花似錦陷猫、人聲如沸秫舌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽足陨。三九已至,卻和暖如春娇未,著一層夾襖步出監(jiān)牢的瞬間墨缘,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工零抬, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留镊讼,地道東北人。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓平夜,卻偏偏與公主長得像蝶棋,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子忽妒,可洞房花燭夜當晚...
    茶點故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內容

  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理玩裙,服務發(fā)現(xiàn),斷路器锰扶,智...
    卡卡羅2017閱讀 134,652評論 18 139
  • Spring Boot 參考指南 介紹 轉載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,806評論 6 342
  • 1.1 spring IoC容器和beans的簡介 Spring 框架的最核心基礎的功能是IoC(控制反轉)容器,...
    simoscode閱讀 6,713評論 2 22
  • Spring Web MVC Spring Web MVC 是包含在 Spring 框架中的 Web 框架寝受,建立于...
    Hsinwong閱讀 22,394評論 1 92
  • 善良的你颜及,過的還好嗎…… 小時候,故事書里的主人公們個個都善良蹂楣,純真俏站,美好,每每讀到她們最終得以幸溉粒快樂地...
    阿宇寶閱讀 285評論 1 0