• 【Spring】BeanName 的自动生成原理


    🎈博客主页:🌈我的主页🌈
    🎈欢迎点赞 👍 收藏 🌟留言 📝 欢迎讨论!👏
    🎈本文由 【泠青沼~】 原创,首发于 CSDN🚩🚩🚩
    🎈由于博主是在学小白一枚,难免会有错误,有任何问题欢迎评论区留言指出,感激不尽!🌠个人主页



    🌟 一、默认 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);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在这里插入图片描述

    • 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);
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    可以看到,generateBeanName 这个方法实际上代理了 BeanDefinitionReaderUtils.generateBeanName 方法的执行,真正的 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";
    		}
    	}
        // 如果经过上面的处理,还是没有 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 是否已经被注册了,如果已经被注册,则继续生成新的 id
    	while (counter == -1 || registry.containsBeanDefinition(id)) {
    		counter++;
    		id = prefix + counter;
    	}
        //最终生成的 id 就是 org.javaboy.bean.User#0
    	return id;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    由此可以看到,默认的 BeanName 就是类的全路径+#+序列号,如 com.dong.Cat#0com.dong.Cat#1
    对于序列号为 0 的 BeanName,还有一个默认的名称,就是类的全路径,不加任何序列号
    上面这个生成 BeanName 的方法是在 BeanDefinitionParserDelegate#parseBeanDefinitionElement 方法中执行的,具体的逻辑如下:

    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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    这就是为什么默认生成的 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 变量实际上就是 ;,空格
        // 所以这个方法实际上就是根据 ; , 以及 空格 去拆分 nameAttr,将之拆分为一个数组
    	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");
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    但是,经过上面的处理,beanName 还是有可能为空。如果还为空,则进入到上面的逻辑中,自动生成 BeanName

  • 相关阅读:
    flink1.15 异步维表Join 用于外部数据访问的异步 I/O scala版本
    使用无服务器功能的云计算成新趋势?无服务器功能的隐藏挑战
    为什么Dapr是比SpringCloud和Istio更优雅的微服务框架?
    Java根据Freemarker模板生成Word文件
    Git的常见操作
    实战回忆录:从Webshell开始突破边界
    无法加载文件xxx,此系统上禁止运行脚本
    梯度下降——机器学习
    一个斜杠引发的CDN资源回源请求量飙升
    【Linux】进程概念
  • 原文地址:https://blog.csdn.net/m0_46635265/article/details/132941123