Spring BeanName 的自動生成原理示例詳解
?? 一、默認 name 生成原理
在 Spring 中,提供了 BeanNameGenerator 用來生成 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 配置中,默認的 BeanName 就是在這個中自動生成的
- AnnotationBeanNameGenerator:Java 配置中,如果使用了 @Component 等注解標記的 Bean,沒有設置默認的名稱,則通過這個來生成默認的 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 這個方法實際上代理了 BeanDefinitionReaderUtils.generateBeanName 方法的執(zhí)行,真正的 BeanName 的生成是在這個方法中完成的
public static String generateBeanName( BeanDefinition definition, BeanDefinitionRegistry registry, boolean isInnerBean) throws BeanDefinitionStoreException { // 這里就是獲取到 XML 中 bean 標簽里邊配置的 class 屬性的值 String generatedBeanName = definition.getBeanClassName(); // 判斷是否有 class 這個屬性值,如果沒有的話,則在 parnetName 存在的情況下, // 使用 parentName+$child 來作為 生成的 beanName if (generatedBeanName == null) { if (definition.getParentName() != null) { generatedBeanName = definition.getParentName() + "$child"; } // 如果沒有 parentName,則嘗試使用 factoryBeanName else if (definition.getFactoryBeanName() != null) { generatedBeanName = definition.getFactoryBeanName() + "$created"; } } // 如果經(jīng)過上面的處理,還是沒有 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. // 我們的默認 BeanName,實際上是在這個方法中生成的 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 實際上就是 # // 所有這里是把類的全路徑和 # 拼在一起 String prefix = beanName + GENERATED_BEAN_NAME_SEPARATOR; // 后面的判斷表示這個 id 是否已經(jīng)被注冊了,如果已經(jīng)被注冊,則繼續(xù)生成新的 id while (counter == -1 || registry.containsBeanDefinition(id)) { counter++; id = prefix + counter; } //最終生成的 id 就是 org.javaboy.bean.User#0 return id; }
由此可以看到,默認的 BeanName
就是類的全路徑+ #
+序列號,如 com.dong.Cat#0
、 com.dong.Cat#1
。對于序列號為 0 的 BeanName,還有一個默認的名稱,就是類的全路徑,不加任何序列號上面這個生成 BeanName 的方法是在 BeanDefinitionParserDelegate#parseBeanDefinitionElement 方法中執(zhí)行的,具體的邏輯如下:
if (beanDefinition != null) { // 當前沒有配置 BeanName,即 bean 標簽中沒有 id 或者 name 屬性 if (!StringUtils.hasText(beanName)) { try { if (containingBean != null) { beanName = BeanDefinitionReaderUtils.generateBeanName( beanDefinition, this.readerContext.getRegistry(), true); } else { //這個地方,最終會調用到上面的邏輯去生成 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. // 獲取一個類的全路徑 com.dong.Cat //!this.readerContext.getRegistry().isBeanNameInUse(beanClassName) 表示 beanClassName 還沒有作為一個 BeanName 注冊到 Spring 容器中 String beanClassName = beanDefinition.getBeanClassName(); if (beanClassName != null && beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) { //將之添加別名中,相當于類的全路徑本身,成為了 Bean 的一個別名 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); }
這就是為什么默認生成的 BeanName 中,#0可有可無的原因
?? 二、id 和 name 屬性處理原理
id 和 name 屬性的處理其實也是在 BeanDefinitionParserDelegate#parseBeanDefinitionElement 方法中:
// 獲取 bean 標簽中的 id 屬性值,user String id = ele.getAttribute(ID_ATTRIBUTE); // 獲取 bean 標簽中 name 屬性值,user;user2;user3 String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); List<String> aliases = new ArrayList<>(); if (StringUtils.hasLength(nameAttr)) { //MULTI_VALUE_ATTRIBUTE_DELIMITERS 變量實際上就是 ;,空格 // 所以這個方法實際上就是根據(jù) ; , 以及 空格 去拆分 nameAttr,將之拆分為一個數(shù)組 String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS); // name 拆出來的屬性將作為這個 bean 的別名 aliases.addAll(Arrays.asList(nameArr)); } //使用 id 作為 beanName String beanName = id; //這里相當于判斷這個 bean 標簽沒有 id 屬性,但是有 name 屬性 if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) { //將 name 拆出來的集合中的第一項作為 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)過上面的處理,beanName 還是有可能為空。如果還為空,則進入到上面的邏輯中,自動生成 BeanName
到此這篇關于Spring BeanName 的自動生成原理的文章就介紹到這了,更多相關Spring BeanName自動生成原理內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
解決Java中SimpleDateFormat線程不安全的五種方案
SimpleDateFormat 就是一個典型的線程不安全事例,本文主要介紹了解決Java中SimpleDateFormat線程不安全的五種方案,需要的朋友們下面隨著小編來一起學習學習吧2021-05-05Java并發(fā)編程ArrayBlockingQueue的使用
ArrayBlockingQueue是一個備受矚目的有界阻塞隊列,本文將全面深入地介紹ArrayBlockingQueue的內部機制、使用場景以及最佳實踐,感興趣的可以了解一下2024-08-08