谈谈spring中bean的名字

谈谈spring中bean的名字

     提到bean的名字,就要从声明bean的地方说起。在应用spring时,有两个地方可以声明一个bean,一个是在spring的配置文件中,一个是在代码中通过Component等标注声明。

     代码中可以通过标注的方式来表示这个类是属于spring管理的类,这类标注有Component、Repository、Service以及Controller。它们默认没有直接指定bean的名字,所以bean的名字是spring默认生成的,这些bean的名字生成规则就是bean的类型首字母小写。这个会则会引起一个问题,若不同的包下有两个名字相同的类,而这两个类都声明成spring的bean,这时候就会产成冲突。因为bean的名字就是bean的唯一标示,是不允许重复的。当然我们可以在标注中指定bean的名字就解决了这个问题,比如Controller("bean9527"),这样当前Controller的名字就是"bean9527"了。

      前面在讲依赖注入中涉及到获取依赖bean的时候用到的方法都是getBean(String beanName),可见知道bean的名字就可以找到相应的bean。 那么bean只能有一个名字吗,当然不是,我们可以给bean取多个名字,也就是别名alias。另外,我们在配置文件中声明bean的时候,不仅可以指定bean的名字还可以指定bean的id,那么bean的名字和id之间是什么关系呢?还有一种情况就是即不指定bean的名字,也不指定bean的id,只是单单指定bean的类型,这时候bean的名字又是怎样的呢?下面通过一段配置来看看spring是怎么管理bean的名字的
	<bean class="com.test.service.Service1"/>
	<bean class="com.test.service.Service1"/>
	<bean id="service3" class="com.test.service.Service3" init-method="initMethod">
		<property name="service4" ref="service4"/>
	</bean>
	<bean name="service4" id="service42" class="com.test.service.Service4" >
		<property name="service3" ref="service3"/>
		<property name="service6" ref="service64"/>
	</bean>
	<bean id="service6" name="service61"  class="com.test.service.Service6" autowire="byName"/>
	<alias name="service6" alias="service62"/>
	<alias name="service61" alias="service63"/>
	<alias name="service63" alias="service64"/>
    假设在spring的配置文件中声明如上所示的bean,那么spring会怎么生成和保存这些bean的名字呢?透过现象看本质,先来看看现象是怎样的
                                       
     上图的截图来自beanFactory中的singletonObjects,这个singletonObjects在AbstractBeanFactory的基类DefaultSingletonBeanRegistry中。它是一个map结构,key就是bean的名字,value就是bean本身,它保存了当前beanFactory中注册的所有的单例bean。利用图中的结果我们来一一分析一下bean的名字的由来。

1,只指定bean的类型
     若只指定了bean的类型,spring为bean生成名字是bean类型的全限定名加编号组成。可以看到,我们在配置文件中声明两个com.test.service.Service1类型的bean,而他们对应的名字就是com.test.service.Service1#0和com.test.service.Service1#1。居然有两个类型相同的单例bean,可见spring中单例的概念和传统的单例并不相同。spring中单例不是相对类型而言,而是相对于我们定义的bean。也就是说如果我们定义了一个bean,他叫“张三”,那么在spring中就只有一个张三的实例,不论何时你看到的都是他。但是我们还可以定义一个和张三一模一样的bean,只要他不叫张三就好。

2,只指定bean的id
    在声明bean的时候,可以不指定bean的名字而是指定bean的id,这时它的id就是他的名字。如上图service3

3,同时指定bean的id,名字和类型
    由上面的service4和service6的例子可以看出,只要是指定了bean的id,存储在singletonObjects中的名字就是这个id。那么通过配置中的name以及类的全名就找不到了吗?bean的别名在哪里,怎么利用别名查找bean?接下来通过源码进行分析,看看事情的本质。

一,BeanDefinition解析时bean名字的识别与生成
         要解释上面的现象,首先要从BeanDefinition的解析说起,下面就是处理配置文件中bean标签的方法
	public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
		String id = ele.getAttribute(ID_ATTRIBUTE);
		String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

		List<String> aliases = new ArrayList<String>();
		if (StringUtils.hasLength(nameAttr)) {
			String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
			aliases.addAll(Arrays.asList(nameArr));
		}

		String beanName = id;
		if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
			beanName = aliases.remove(0);
			if (logger.isDebugEnabled()) {
				logger.debug("No XML 'id' specified - using '" + beanName +
						"' as bean name and " + aliases + " as aliases");
			}
		}

		if (containingBean == null) {
			checkNameUniqueness(beanName, aliases, ele);
		}

		AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
		if (beanDefinition != null) {
			if (!StringUtils.hasText(beanName)) {
				try {
					if (containingBean != null) {
						beanName = BeanDefinitionReaderUtils.generateBeanName(
								beanDefinition, this.readerContext.getRegistry(), true);
					}
					else {
						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.
						String beanClassName = beanDefinition.getBeanClassName();
						if (beanClassName != null &&
								beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
								!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
							aliases.add(beanClassName);
						}
					}
					if (logger.isDebugEnabled()) {
						logger.debug("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);
		}

		return null;
	}
   上面的方法定义在BeanDefinitionParserDelegate中,这个方法诠释了bean的名字以及别名的确定过程。首先,配置文件中 name中是可以指定bean的多个名字的,每个名字中间用逗号、分号或者空格隔开。若指定了id,则bean的名字就用它了,完全符合刚才的service4及service6。若没有指定bean的id,则取bean的第一名字作为它真实的名字。若即没有指定id也没有指定那么,那么就根据bean的类型生成一个,就像刚才的service1。这里有个特殊情况,就是第一个声明的bean会使用类型的全限定名作为bean的别名, 刚刚的 com.test.service.Service1#0会使用com.test.service.Service1作为它的别名
      bean的别名不仅可以自动生成,更是可以通过alias标签显示指定,对于alias标签定义的别名的处理在DefaultBeanDefinitionDocumentReader的下面方法中
	protected void processAliasRegistration(Element ele) {
		String name = ele.getAttribute(NAME_ATTRIBUTE);
		String alias = ele.getAttribute(ALIAS_ATTRIBUTE);
		boolean valid = true;
		if (!StringUtils.hasText(name)) {
			getReaderContext().error("Name must not be empty", ele);
			valid = false;
		}
		if (!StringUtils.hasText(alias)) {
			getReaderContext().error("Alias must not be empty", ele);
			valid = false;
		}
		if (valid) {
			try {
				getReaderContext().getRegistry().registerAlias(name, alias);
			}
			catch (Exception ex) {
				getReaderContext().error("Failed to register alias '" + alias +
						"' for bean with name '" + name + "'", ele, ex);
			}
			getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
		}
	}

二,BeanDefinition注册时对bean名字的处理

     回顾一下BeanDefinition在BeanDefinitionReaderUtils中注册时是怎么处理bean的名字的
	public static void registerBeanDefinition(
			BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
			throws BeanDefinitionStoreException {

		// Register bean definition under primary name.
		String beanName = definitionHolder.getBeanName();
		registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

		// Register aliases for bean name, if any.
		String[] aliases = definitionHolder.getAliases();
		if (aliases != null) {
			for (String aliase : aliases) {
				registry.registerAlias(beanName, aliase);
			}
		}
	}
    可以看到BeanDefinition注册时,记录了bean的名字和BeanDefinition的映射关系。bean的别名信息并没有保存在BeanDefinition中,而是单独进行了注册。看一下别名的注册过程,它定义在SimpleAliasRegistry中
	public void registerAlias(String name, String alias) {
		Assert.hasText(name, "'name' must not be empty");
		Assert.hasText(alias, "'alias' must not be empty");
		if (alias.equals(name)) {
			this.aliasMap.remove(alias);
		}
		else {
			if (!allowAliasOverriding()) {
				String registeredName = this.aliasMap.get(alias);
				if (registeredName != null && !registeredName.equals(name)) {
					throw new IllegalStateException("Cannot register alias '" + alias + "' for name '" +
							name + "': It is already registered for name '" + registeredName + "'.");
				}
			}
			checkForAliasCircle(name, alias);
			this.aliasMap.put(alias, name);
		}
	}
     别名的注册其实就是保存了别名到真实名字(可能也是另个名字的别名)的映射关系,这个 SimpleAliasRegistry是DefaultSingletonBeanRegistry的基类,他们都是AbstractBeanFactory的基类。由此可见,我们常用的BeanFactory不仅是BeanDefinition的注册中心,还是单例对象的注册中心以及bean的别名注册中心。

三,利用名字查找bean的过程
    在AbstractBeanFactory中的doGetBean方法的第一行就是关于bean名字的处理final String beanName = transformedBeanName(name);。这里就实现了bean的名字的转换,转换过程在它调用的canonicalName方法中
	public String canonicalName(String name) {
		String canonicalName = name;
		// Handle aliasing...
		String resolvedName;
		do {
			resolvedName = this.aliasMap.get(canonicalName);
			if (resolvedName != null) {
				canonicalName = resolvedName;
			}
		}
		while (resolvedName != null);
		return canonicalName;
	}
   在想要获取一个bean的时候,做的第一件事情就是找到bean的真是名字,因为找到bean的真实名字才能找到对应的BeanDefinition或其单例实例。找到bean真实名字的方法就是根据层层的别名关系,直到找出这样一个名字,这个名字在aliasMap中作为别名已经找不到对应的真实bean名字,也就是说这个名字已经不是别名就是bean的名字。所以,不论根据bean的名字,还是任意一个别名都能从容器中取得到相应的bean。


  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Spring Bean 的生命周期是指从 Bean 实例化开始,到最终销毁的整个过程。它主要包括以下四个阶段:实例化、属性注入、初始化和销毁。首先,Bean 的实例化是指通过反射创建出对象。接着,在属性注入阶段,Spring 会将配置文件定义的属性值注入到 Bean 。然后,初始化阶段会调用 Bean 的初始化方法,这个方法可以由开发者在 Bean 自定义。最后,在销毁阶段,Spring 会在容器关闭时调用 Bean 的销毁方法,以释放资源。 另外,如果 Bean 实现了 BeanFactoryAware 接口,Spring 会在实例化后调用它实现的 setBeanFactory() 方法,将 Spring 工厂注入进来。这样,Bean 就可以获取 Spring 工厂的一些功能,例如获取其他 Bean。 了解 Spring Bean 的生命周期对于深入理解 Spring 框架很有帮助,可以帮助我们更好地管理和控制 Bean 的创建、初始化和销毁过程。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [谈谈我对Spring Bean 生命周期的理解](https://download.csdn.net/download/weixin_38546817/12762245)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [SpringBean的生命周期](https://blog.csdn.net/weixin_71786285/article/details/128274251)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值