Spring BeanName 的自動(dòng)生成原理示例詳解
?? 一、默認(rèn) name 生成原理
在 Spring 中,提供了 BeanNameGenerator 用來(lái)生成 BeanName:
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);
}
- DefaultBeanNameGenerator:XML 配置中,默認(rèn)的 BeanName 就是在這個(gè)中自動(dòng)生成的
- AnnotationBeanNameGenerator:Java 配置中,如果使用了 @Component 等注解標(biāo)記的 Bean,沒(méi)有設(shè)置默認(rèn)的名稱(chēng),則通過(guò)這個(gè)來(lái)生成默認(rèn)的 BeanName
public class DefaultBeanNameGenerator implements BeanNameGenerator {
/**
* A convenient constant for a default {@code DefaultBeanNameGenerator} instance,
* as used for {@link AbstractBeanDefinitionReader} setup.
* @since 5.2
*/
public static final DefaultBeanNameGenerator INSTANCE = new DefaultBeanNameGenerator();
@Override
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
return BeanDefinitionReaderUtils.generateBeanName(definition, registry);
}
}可以看到,generateBeanName 這個(gè)方法實(shí)際上代理了 BeanDefinitionReaderUtils.generateBeanName 方法的執(zhí)行,真正的 BeanName 的生成是在這個(gè)方法中完成的

public static String generateBeanName(
BeanDefinition definition, BeanDefinitionRegistry registry, boolean isInnerBean)
throws BeanDefinitionStoreException {
// 這里就是獲取到 XML 中 bean 標(biāo)簽里邊配置的 class 屬性的值
String generatedBeanName = definition.getBeanClassName();
// 判斷是否有 class 這個(gè)屬性值,如果沒(méi)有的話,則在 parnetName 存在的情況下,
// 使用 parentName+$child 來(lái)作為 生成的 beanName
if (generatedBeanName == null) {
if (definition.getParentName() != null) {
generatedBeanName = definition.getParentName() + "$child";
}
// 如果沒(méi)有 parentName,則嘗試使用 factoryBeanName
else if (definition.getFactoryBeanName() != null) {
generatedBeanName = definition.getFactoryBeanName() + "$created";
}
}
// 如果經(jīng)過(guò)上面的處理,還是沒(méi)有 generatedBeanName,那么就要拋異常了
if (!StringUtils.hasText(generatedBeanName)) {
throw new BeanDefinitionStoreException("Unnamed bean definition specifies neither " +
"'class' nor 'parent' nor 'factory-bean' - can't generate bean name");
}
if (isInnerBean) {
// Inner bean: generate identity hashcode suffix.
return generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + ObjectUtils.getIdentityHexString(definition);
}
// Top-level bean: use plain class name with unique suffix if necessary.
// 我們的默認(rèn) BeanName,實(shí)際上是在這個(gè)方法中生成的
return uniqueBeanName(generatedBeanName, registry);
}
public static String uniqueBeanName(String beanName, BeanDefinitionRegistry registry) {
String id = beanName;
int counter = -1;
// Increase counter until the id is unique.
//GENERATED_BEAN_NAME_SEPARATOR 實(shí)際上就是 #
// 所有這里是把類(lèi)的全路徑和 # 拼在一起
String prefix = beanName + GENERATED_BEAN_NAME_SEPARATOR;
// 后面的判斷表示這個(gè) id 是否已經(jīng)被注冊(cè)了,如果已經(jīng)被注冊(cè),則繼續(xù)生成新的 id
while (counter == -1 || registry.containsBeanDefinition(id)) {
counter++;
id = prefix + counter;
}
//最終生成的 id 就是 org.javaboy.bean.User#0
return id;
}由此可以看到,默認(rèn)的 BeanName 就是類(lèi)的全路徑+ # +序列號(hào),如 com.dong.Cat#0 、 com.dong.Cat#1 。對(duì)于序列號(hào)為 0 的 BeanName,還有一個(gè)默認(rèn)的名稱(chēng),就是類(lèi)的全路徑,不加任何序列號(hào)上面這個(gè)生成 BeanName 的方法是在 BeanDefinitionParserDelegate#parseBeanDefinitionElement 方法中執(zhí)行的,具體的邏輯如下:
if (beanDefinition != null) {
// 當(dāng)前沒(méi)有配置 BeanName,即 bean 標(biāo)簽中沒(méi)有 id 或者 name 屬性
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
//這個(gè)地方,最終會(huì)調(diào)用到上面的邏輯去生成 BeanName
//com.dong.Cat#0
beanName = this.readerContext.generateBeanName(beanDefinition);
// Register an alias for the plain bean class name, if still possible,
// if the generator returned the class name plus a suffix.
// This is expected for Spring 1.2/2.0 backwards compatibility.
// 獲取一個(gè)類(lèi)的全路徑 com.dong.Cat
//!this.readerContext.getRegistry().isBeanNameInUse(beanClassName) 表示 beanClassName 還沒(méi)有作為一個(gè) BeanName 注冊(cè)到 Spring 容器中
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
//將之添加別名中,相當(dāng)于類(lèi)的全路徑本身,成為了 Bean 的一個(gè)別名
aliases.add(beanClassName);
}
}
if (logger.isTraceEnabled()) {
logger.trace("Neither XML 'id' nor 'name' specified - " +
"using generated bean name [" + beanName + "]");
}
}
catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}這就是為什么默認(rèn)生成的 BeanName 中,#0可有可無(wú)的原因
?? 二、id 和 name 屬性處理原理
id 和 name 屬性的處理其實(shí)也是在 BeanDefinitionParserDelegate#parseBeanDefinitionElement 方法中:
// 獲取 bean 標(biāo)簽中的 id 屬性值,user
String id = ele.getAttribute(ID_ATTRIBUTE);
// 獲取 bean 標(biāo)簽中 name 屬性值,user;user2;user3
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
List<String> aliases = new ArrayList<>();
if (StringUtils.hasLength(nameAttr)) {
//MULTI_VALUE_ATTRIBUTE_DELIMITERS 變量實(shí)際上就是 ;,空格
// 所以這個(gè)方法實(shí)際上就是根據(jù) ; , 以及 空格 去拆分 nameAttr,將之拆分為一個(gè)數(shù)組
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
// name 拆出來(lái)的屬性將作為這個(gè) bean 的別名
aliases.addAll(Arrays.asList(nameArr));
}
//使用 id 作為 beanName
String beanName = id;
//這里相當(dāng)于判斷這個(gè) bean 標(biāo)簽沒(méi)有 id 屬性,但是有 name 屬性
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
//將 name 拆出來(lái)的集合中的第一項(xiàng)作為 beanName
beanName = aliases.remove(0);
if (logger.isTraceEnabled()) {
logger.trace("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}但是,經(jīng)過(guò)上面的處理,beanName 還是有可能為空。如果還為空,則進(jìn)入到上面的邏輯中,自動(dòng)生成 BeanName
到此這篇關(guān)于Spring BeanName 的自動(dòng)生成原理的文章就介紹到這了,更多相關(guān)Spring BeanName自動(dòng)生成原理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot中數(shù)據(jù)傳輸對(duì)象(DTO)的實(shí)現(xiàn)
本文主要介紹了SpringBoot中數(shù)據(jù)傳輸對(duì)象(DTO)的實(shí)現(xiàn),包括了手動(dòng)創(chuàng)建DTO、使用ModelMapper和Lombok創(chuàng)建DTO的示例,具有一定的參考價(jià)值,感興趣的可以了解一下2024-07-07
Java數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)折半查找的算法過(guò)程解析
這篇文章主要介紹了Java數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)折半查找的算法過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03
Java多線程中的CountDownLatch詳細(xì)解讀
這篇文章主要介紹了Java多線程中的CountDownLatch詳細(xì)解讀,一個(gè)同步輔助類(lèi),在完成一組正在其他線程中執(zhí)行的操作之前,它允許一個(gè)或多個(gè)線程一直等待,用給定的計(jì)數(shù) 初始化 CountDownLatch,需要的朋友可以參考下2023-11-11
Java Spring框架簡(jiǎn)介與Spring IOC詳解
Spring 框架是一個(gè)輕量級(jí)的解決方案,可以一站式地構(gòu)建企業(yè)級(jí)應(yīng)用。它是為了解決 企業(yè)應(yīng)用開(kāi)發(fā)的復(fù)雜性而創(chuàng)建的。Spring 使用基本的 JavaBean 來(lái)完成以前只可能由 EJB 完成的事情。IOC 是 Inversion of Control 的縮寫(xiě),多數(shù)書(shū)籍翻譯成控制反轉(zhuǎn)2021-09-09
詳解Java集合中的基本數(shù)據(jù)結(jié)構(gòu)
總有小伙伴讓我總結(jié)一下Java集合中的基本數(shù)據(jù)結(jié)構(gòu)的相關(guān)知識(shí),今天特地整理了本篇文章,文中有非常詳細(xì)的介紹,需要的朋友可以參考下2021-06-06
Java位運(yùn)算和邏輯運(yùn)算的區(qū)別實(shí)例
Java位運(yùn)算和邏輯運(yùn)算的區(qū)別實(shí)例,請(qǐng)參考下面代碼,希望對(duì)你有所幫助2013-02-02
解決IDEA使用maven創(chuàng)建Web項(xiàng)目,出現(xiàn)500錯(cuò)誤的問(wèn)題
本文主要介紹了在使用Maven創(chuàng)建項(xiàng)目并導(dǎo)入依賴(lài)寫(xiě)完測(cè)試代碼后運(yùn)行出現(xiàn)500錯(cuò)誤的解決步驟,這種問(wèn)題的根本原因是Tomcat啟動(dòng)后缺少某些支持的jar包,導(dǎo)致運(yùn)行出錯(cuò),解決方法是在項(xiàng)目結(jié)構(gòu)中找到Artifacts,點(diǎn)擊要編輯的項(xiàng)目2024-10-10

