一、前言
在 Spring 容器中每个 bean 对象都有一个唯一的名字 (beanName) 和 0 个或者多个别名 (aliases)
如果我们想从 IOC 容器中获取 bean 对象,那么我们可以通过 beanName 获取,也可以通过别名获取
beanFactory.getBean("beanName or alias");
下面我们就从源码的角度看一下我们平常在 bean 标签中配置的 id、name ,以及我们上面说到的 beanName、aliases 它们这些到底是什么,具体有什么作用
我们这里参照的源码是( 4.3.11.RELEASE这个版本的 )
由于Spring IOC 中的容器类和各自的方法众多,我这里只说一下对应的类、方法、以及代码的行号数
二、Spring 配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="watermelon" class="com.xiaomaomao.entity.Watermelon">
<property name="name" value="西瓜"></property>
<property name="color" value="原谅色"></property>
<property name="price" value="3.0"></property>
</bean>
<bean class="com.xiaomaomao.entity.Banana">
<property name="name" value="香蕉"></property>
<property name="color" value="黄色"></property>
<property name="price" value="4.0"></property>
</bean>
<bean name="a1,a2,a3" class="com.xiaomaomao.entity.Apple">
<property name="name" value="苹果"></property>
<property name="color" value="红色"></property>
<property name="price" value="5.0"></property>
</bean>
<bean id="mango" name="m1,m2,m3" class="com.xiaomaomao.entity.Mango">
<property name="name" value="芒果"></property>
<property name="color" value="黄色"></property>
<property name="price" value="6.0"></property>
</bean>
</beans>
三、源码分析
Spring IOC 启动到解析 bean 标签前面的代码太多了,我这里就不贴了,直接从解析 XML 这里开始吧
// DefaultBeanDefinitionDocumentReader 类中的方法, 代码行号: 161
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
// 判断是否是根节点下面默认的命名空间,我们这里默认的命名空间 就是 xmlns="http://www.springframework.org/schema/beans"
// 也就是没有 xnlns:前缀的这个命名空间
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
// 解析默认的命名空间下面默认的元素
parseDefaultElement(ele, delegate);
}
else {
// 解析默认的命名空间下面非默认的元素
delegate.parseCustomElement(ele);
}
}
}
}
else {
// 非默认的命名空间下面的元素解析方式
delegate.parseCustomElement(root);
}
}
关于什么是 XML 名称空间、默认的名称空间等等是什么意思,大家可以参考这篇博客:
我们这里默认的名称空间是 xmlns="http://www.springframework.org/schema/beans" ,这个默认的名称空间里面有几个默认值,分别是 bean、alias、beans、description、import,这些标签会进入到 parseDefaultElement(ele, delegate) 这个分支中进行解析.
除了这些默认值之外,我们还经常会在 beans 标签中定义 <mvc: />、<context: />、<aop: />等标签,这些标签就是默认名称空间( http://www.springframework.org/schema/beans )里面非默认的元素值,这些标签会进到 delegate.parseCustomElement(ele) 这个分支中进行解析.
言归正传,我们这里要分析的是 <bean /> 标签的解析过程,所以毫无疑问,应该进入到 parseDefaultElement(ele, delegate) 这个分支中,点进去
// DefaultBeanDefinitionDocumentReader 类中的方法, 代码行号: 182
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
// 解析 import 标签
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
// 解析 alias 标签
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
// 解析 bean 标签
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
// 解析 beans 标签
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// 如果是 <beans> 里面,嵌套 <beans> 标签,需要递归处理
doRegisterBeanDefinitions(ele);
}
}
这里面是对各个默认的标签进行解析,找到 bean 标签对应的解析逻辑,点进去
// DefaultBeanDefinitionDocumentReader 类中的方法,代码行号: 298
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 将<bean.../> 节点中的配置信息提取出来,然后封装到 BeanDefinitionHolder 对象中
// 这个对象封装的信息包括 BeanDefinition 对象、beanName、aliases(别名数组)
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 注册最终封装的实例对象
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
// 异常处理
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// 发送注册时间
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
首先看一下 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele) 这个方法,看看它是如何从 bean 标签中提取出配置信息的
在解读这个方法之前,我们需要看一下 BeanDefinitionHolder 对象,了解一下它里面具体包含的信息是什么.
public class BeanDefinitionHolder implements BeanMetadataElement {
// BeanDefinition 对象,这个很重要,我们平常所说的 bean 其实可以看做是一个 BeanDefinition 对象实例
private final BeanDefinition beanDefinition;
// bean 的名称
private final String beanName;
// bean 的别名
private final String[] aliases;
...
}
这里的 BeanDefinition 很重要,我们可以看一下
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
// Spring 默认的 scope 的值有 singleton 和 prototype
// 其实还有 request、session、globalSession 等,不过这些都是 web 的扩展
String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
// 不重要
int ROLE_APPLICATION = 0;
int ROLE_SUPPORT = 1;
int ROLE_INFRASTRUCTURE = 2;
// 设置 parent bean, 这里的 parent bean 和我们平常说的继承关系的父类是不同的
// 这里设置 parent bean,仅仅是为了继承 parent bean 里面的皮脂信息而已
// 关于 parent bean ,读者可以参考博客:
void setParentName(String parentName);
// 获取 parent bean name
String getParentName();
// 设置 bean 的类名称,将来是要通过反射来生成实例的
void setBeanClassName(String beanClassName);
// 获取 bean 的类名称
String getBeanClassName();
// 设置 bean 的 scope
void setScope(String scope);
String getScope();
// 设置是否懒加载
void setLazyInit(boolean lazyInit);
// 是否懒加载初始化
boolean isLazyInit();
// 设置该 bean 依赖的所有的 bean, 注意,这里的依赖不是指的属性依赖( 如 @ Autowire 标记的),是 depends-on 属性设置的值
void setDependsOn(String... dependsOn);
String[] getDependsOn();
// 设置该 bean 是否能注入到其它的 bean 中
void setAutowireCandidate(boolean autowireCandidate);
boolean isAutowireCandidate();
同一接口的多个实现,如果不指定名字的话,Spring 会优先选择设置 primary 为 true 的 bean
// primary:默认值为false,同一个接口的多个实现类对象,如果不指定名字的话, Spring 会优先选择将 primary 设置为true 的 bean
void setPrimary(boolean primary);
boolean isPrimary();
// 如果该 bean 采用工厂方法生成,指定工厂的名称
// Spring 中并不是所有的 bean 实例对象都是通过反射生成的,它们也可以通过工厂模式来生成
void setFactoryBeanName(String factoryBeanName);
String getFactoryBeanName();
// 工厂类中的工厂方法
void setFactoryMethodName(String factoryMethodName);
String getFactoryMethodName();
// 构造方法参数值
ConstructorArgumentValues getConstructorArgumentValues();
// bean 的属性值,为 bean 做属性注入的时候会用到
MutablePropertyValues getPropertyValues();
// 是否是单例对象
boolean isSingleton();
// 是否是多例对象
boolean isPrototype();
// 如果某个 bean 被设置为 abstract,那么它是不能被实例化的它的作用仅仅是为了被其它的 bean 继承用
// 可以参考博客中对 abstract 的介绍
boolean isAbstract();
int getRole();
String getDescription();
String getResourceDescription();
BeanDefinition getOriginatingBeanDefinition();
}
我们从上面的介绍中可以知道 BeanDefinitonHolder 对象中包含三个属性,BeanDefinition对象、beanName、aliases(别名数组),而最重要的是 BeanDefiniton 对象,这个对象中包含了 <bean.../> 标签中所有能配置的属性,我们所谓的 bean ,其实就可以看做是一个 BeanDefinition 对象的实例.有了对这些知识的了解之后呢,我们继续接着上面的代码来看,点开 parseBeanDefinitionElement(ele) 这个方法
// BeanDefinitionParserDelegate 类中的方法,代码行号: 437
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
// 获取 bean 标签中的 id、name 属性的值,如果它们没有配置,则它们的值为 ""
String id = ele.getAttribute(ID_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
// 定义一个别名集合 aliases
List<String> aliases = new ArrayList<String>();
// 如果 bean 里面配置了 name 属性
if (StringUtils.hasLength(nameAttr)) {
// 将 nameAttr 这个字符进行分割,转换成字符串数组(例如: nameArr="m1,m2,m3" ====> {m1,m2,m3}
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
// 将数组转换成集合,并且添加到 aliases 这个 List 集合中
aliases.addAll(Arrays.asList(nameArr));
}
// 这里的 id 就是我们 Bean 标签中配置的 id 属性,在 Spring 中 id 属性代表的也就是 beanName
String beanName = id;
// 如果 bean 标签中只配置了 name 属性,没有配置 id 属性,那么用别名列表的第一个名字作为 beanName
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
// 将 name 属性中的第一个值作为 beanName,剩下的作为别名(alias)注意这里是 remove(0) 它会改变集合的长度的
// 例如 List <String> aliases = {a1,a2,a3} 经过 remove(0) 之后就变成了 {a2,a3}了,此时 beanName 为 a1,别名数组为 {a2,a3}
beanName = aliases.remove(0);
// 如果日志级别是 debug 级别的,会输出下面这段日志,但是 Spring 默认的日志级别是 info的
if (logger.isDebugEnabled()) {
logger.debug("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}
// containingBean 是前面的方法传过来的参数,值是 null
if (containingBean == null) {
// 校验名字的唯一性(校验现在使用的 beanName 是不是已经被加载过的 bean 使用了
checkNameUniqueness(beanName, aliases, ele);
}
// 根据 <bean....>....</bean> 标签中的配置创建一个对应的 BeanDefinition 对象,
// 然后把配置中的数据都设置到 BeanDefinition 实例中去
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
// 如果 BeanDefiniton 实例对象不为空
if (beanDefinition != null) {
// 这里如果 beanName 为空或者为 "",(也就是我们的 bean 标签中既没有配置 id 也没有配置 name )
// 这里 Spring 会按照框架定义的某种规则生成 beanName 和 aliases
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
// 如果我们不定义 id 和 name 属性,Spring 会按照一定的规则生成 beanName 和 beanClassName,
// 如果 Spring 的配置文件中只配合了一个 <bean class="com.xiaomaomao.spring.Watermelon"> 这样的标签
// 那么生成的 beanName 和 beanClassName 的值如下:
// 1、beanName 为:com.xiaomaomao.spring.Watermelon#0
// 2、beanClassName 为:com.xiaomaomao.spring.Watermelon
beanName = this.readerContext.generateBeanName(beanDefinition);
// 获取 beanClassName 的值
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
// 将 beanClassName 的值设置为别名,也就是 com.xiaomaomao.spring.Watermelon
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;
}
}
// 将存放别名的 List<String> 集合,转成别名数组
String[] aliasesArray = StringUtils.toStringArray(aliases);
// 将 BeanDefinition、beanName、aliasesArray 赋值给 BeanDefinitionHolder 对象
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
我们可以具体的看一下是怎么解析 <bean>...</bean>标签的,并如何将 bean 标签中的的数据封装到 BeanDefinition 对象中的(当然这个不是我们的重点)
// BeanDefinitionParserDelegate 类中的方法, 代码行号: 522
public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, BeanDefinition containingBean) {
// 把 beanName 的值赋值给本类的 ParseState 对象
this.parseState.push(new BeanEntry(beanName));
String className = null;
// 如果 bean 标签中配置了 class 属性,把 class 属性配置的全包类名赋值给 className 这个变量
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
try {
String parent = null;
// 如果 bean 里面配置了 parent 属性,将 parent 属性对应的值赋值给 parent 这个变量
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
// 创建 BeanDefinition 对象,并设置相应的属性,里面有设置 BeanClassName
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// 设置 BeanDefinition 中定义的属性,这些属性定义在 AbstractBeanDefinition 中
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
// 下面是解析 bean 标签中的子标签
// 解析 <meta /> 标签
parseMetaElements(ele, bd);
// 解析 <lookup-method /> 标签
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
// 解析 <replaced-method /> 标签
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
// 解析 <constructor-arg /> 标签
parseConstructorArgElements(ele, bd);
// 解析 <property /> 标签
parsePropertyElements(ele, bd);
// 解析 <qualifier /> 标签
parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
}
catch (ClassNotFoundException ex) {
error("Bean class [" + className + "] not found", ele, ex);
}
catch (NoClassDefFoundError err) {
error("Class that bean class [" + className + "] depends on not found", ele, err);
}
catch (Throwable ex) {
error("Unexpected failure during bean definition parsing", ele, ex);
}
finally {
this.parseState.pop();
}
return null;
}
上面有说到如果我们没有配置 id 和 name 属性,那么 Spring 框架帮我们生成的 beanName 和 aliases 分别是什么呢?
// BeanDefinitionReaderUtils 工具类中的方法, 代码行号: 102
public static String generateBeanName(
BeanDefinition definition, BeanDefinitionRegistry registry, boolean isInnerBean)
throws BeanDefinitionStoreException {
// 获取 beanClassName ,前面执行 AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// 的时候有设置 beanClassName
String generatedBeanName = definition.getBeanClassName();
// 如果存在父 Bean ,那么 generatedBeanName 的值为父 bean 的名称拼接 $child
// tips: 父 bean,不是我们说的继承中的父子 bean,这里指的是 bean 标签中配置的 parent 属性对应的 bean
if (generatedBeanName == null) {
if (definition.getParentName() != null) {
generatedBeanName = definition.getParentName() + "$child";
}
// 如果是 FactoryBean ,那么 generatedBeanName 的值为 FactoryBean 的名称拼接 $created
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;
// 判断是否是内部的 bean
if (isInnerBean) {
// 内部 bean 的 beanName 生成规则
id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + ObjectUtils.getIdentityHexString(definition);
}
else {
int counter = -1;
// 如果 counter = -1 或者是容器中已经存在了该 beanName 则一直执行循环
// 例如 Spring 的配置文件中配置了一个 <bean class="com.xiaomaomao.entity.Banana">
// 那么这里的 id 值为 com.xiaomaomao.entity.Banana#0
// 如果配置文件中配置了两个相同的 <bean class="com.xiaomaomao.entity.Banana">
// 那么第一个 beanName 为com.xiaomaomao.entity.Banana#0 ,第二个为 com.xiaomaomao.entity.Banana#1
while (counter == -1 || registry.containsBeanDefinition(id)) {
counter++;
id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + counter;
}
}
return id;
}
那么接着就是将上面代码里生成的 id 赋值给了 beanName,前面设置的 beanClassName 赋值给了 aliases,通过上面的代码,我们最终将 beanName、aliases、BeanDefinition 对象使用 BeanDefinitionHolder 这个对象承载了全部的数据.
紧接着回到代码的入口位置
// DefaultBeanDefinitionDocumentReader 类中的方法,代码行号: 298
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 将<bean.../> 节点中的配置信息提取出来,然后封装到 BeanDefinitionHolder 对象中
// 这个对象封装的信息包括 BeanDefinition 对象、beanName、aliases(别名数组)
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
// 如果有自定义的属性,进行相应的解析
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 注册最终封装的实例对象
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
// 异常处理
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// 发送注册时间
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
经过上面的分析,BeanDefinitionHolder 对象我们就创建出来了,并且该对象的三个属性, BeanDefinition 对象、beanName、aliaese我们都分别给其赋了值.(注意,这里的一个 bean 对应的是一个 BeanDefinitionHolder 对象,如果配置文件中存在多个 bean 标签,这里会生成多个 BeanDefinitonHolder对象)
接着我们看一下 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());这个方法,看看是如何注册 bean 的吧.
好了,我们回到 解析 bean 标签的入口方法中,看一下怎么注册 bean 的吧
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// 注册 bean
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// 注册别名
// 如果还有别名的话,也要根据别名全部注册一遍,不然根据别名就会找不到 Bean 了
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
// alias -> beanName 保存它们的别名信息,这个很简单,用一个 map 保存一下就可以了,
// 获取的时候,会先将 alias 转换为 beanName,然后再查找
registry.registerAlias(beanName, alias);
}
}
}
看一下怎么注册 bean 的吧
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
// 校验
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
// oldBeanDefinition ,这里涉及 allowBeanDefinitionOverriding 这个属性,
// 如果两个 bean 标签配置了相同的 id 或者是 name ,那么 Spring 中默认的就是允许后面注册的 bean 覆盖前面注册的 bean
BeanDefinition oldBeanDefinition;
// 所有的 bean 的注册最终都是放到 Map<String, BeanDefinition> beanDefinitionMap 这个 Map 集合中
oldBeanDefinition = this.beanDefinitionMap.get(beanName);
// 如果存在重复名称的 bean 的情况
if (oldBeanDefinition != null) {
// 如果 AllowBeanDefinitionOverriding 属性的值是 false 的情况下会抛异常
// 大致的内容信息可以参考这篇博客
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
"': There is already [" + oldBeanDefinition + "] bound.");
}
// 用 spring 定义的 bean 去覆盖用户自定义的 bean
else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if (this.logger.isWarnEnabled()) {
this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
oldBeanDefinition + "] with [" + beanDefinition + "]");
}
}
// 如果两个先后注册的 bean 不同,那么用后注册的 bean 覆盖前面注册的 bean
else if (!beanDefinition.equals(oldBeanDefinition)) {
if (this.logger.isInfoEnabled()) {
this.logger.info("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + oldBeanDefinition +
"] with [" + beanDefinition + "]");
}
}
// 如果前后注册的两个 bean 内容相同
else {
// 如果日志级别是 debug 级别的情况下,打印如下的日志
if (this.logger.isDebugEnabled()) {
this.logger.debug("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + oldBeanDefinition +
"] with [" + beanDefinition + "]");
}
}
// 使用后注册的 bean 覆盖前面注册的 bean
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
// 判断是否已经有其它的 bean 开始初始化了
// 注意,注册 bean 这个动作结束之后,bean 依然没有被初始化,真正的初始化操作还在后面
// 在 spring 容器启动的最后,会预初始化所有的单例 bean
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
synchronized (this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
if (this.manualSingletonNames.contains(beanName)) {
Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames);
updatedSingletons.remove(beanName);
this.manualSingletonNames = updatedSingletons;
}
}
}
else {
// 将 BeanDefinition 放到这个 map 中,这个 map 保存了所有的 beanDefinition 实例对象
this.beanDefinitionMap.put(beanName, beanDefinition);
// beanDefinitionNames 是一个 List<String> 集合,里面会按照 bean 标签的配置顺序保存所有的 beanName,
this.beanDefinitionNames.add(beanName);
// 这是个 LinkedHashSet,代表的是手动注册的 singleton bean,
// manualSingletonNames 是一个 Set<String> ,代表的是手动注册的 singleton bean
// 注意这里的是 remove() 方法,到这里的 bean 当然不是手动注册的
// 手动注册指的是通过调用 registerSingleton(String beanName, Object singletonObject)方法注册的 bean
// spring 会在后面手动注册一些 bean ,例如 environment、systemProperties 等 bean,当然我们自己也可以在运行时注册
// bean 到 spring 容器中
this.manualSingletonNames.remove(beanName);
}
this.frozenBeanDefinitionNames = null;
}
if (oldBeanDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
}
再看一下怎么注册 aliases(别名)
public void registerAlias(String name, String alias) {
Assert.hasText(name, "'name' must not be empty");
Assert.hasText(alias, "'alias' must not be empty");
// 判断 alias 和 beanName 是否相等,相等的话代表的是同一个,则移除重复的
// <bean id="watermelon" name="watermelon,w1,w2" class="com.xiaomaomao.entity.Watermelon">
// 那么别名只有 w1 和 w2,虽然也配置了 watermelon 为别名,但是重复的会移除
if (alias.equals(name)) {
this.aliasMap.remove(alias);
}
else {
// 从 aliasMap 这个 Map 集合中获取 别名所对应的值
// 这个 Map 的数据结构是 Map< alias,beanName>
String registeredName = this.aliasMap.get(alias);
if (registeredName != null) {
if (registeredName.equals(name)) {
// 如果已经存在了该别名,直接返回,不需要再次注册
return;
}
// 重复覆盖
if (!allowAliasOverriding()) {
throw new IllegalStateException("Cannot register alias '" + alias + "' for name '" +
name + "': It is already registered for name '" + registeredName + "'.");
}
}
// 校验别名循环
checkForAliasCircle(name, alias);
// 以别名为 key ,beanName 的值为 value ,存入 Map 集合
this.aliasMap.put(alias, name);
}
}
四、总结
<bean id="watermelon" class="com.xiaomaomao.entity.Watermelon"> </bean>
上面的配置代表的意思是 beanName 为 watermelon , 不存在别名
<bean class="com.xiaomaomao.entity.Banana"> </bean>
上面的配置代表的意思是 beanName 为 com.xiaomaomao.entity.Banana#0, 别名为 com.xiaomaomao.entity.Banana
<bean name="a1,a2,a3" class="com.xiaomaomao.entity.Apple"> </bean>
上面的配置代表的意思是 beanName 为 a1, 别名为 a2、a3
<bean id="mango" name="m1,m2,m3" class="com.xiaomaomao.entity.Mango"> </bean>
上面的配置代表的意思是 beanName 为 mango, 别名为 m1、m2、m3
当我们解析完了 XML 配置文件之后,可以在 DefaultListBeanFactory 这个类中找到 我们注册的所有 beanNames 和 aliases
如下是所有的 beanName 的集合
如下是 alias 的集合,所有的别名都存储在 aliasMap 这个 Map<alias,beanName> 中我们可以看到
beanName 为 mango 的 bean 对应的别名有 m1、m2、m3
beanName 为 a1 的 bean 对应的别名有 a2、a3
beanName 为 com.xiaomaomao.entity.Banana#0 对应的别名有 com.xiaomaomao.entity.Banana
有一种情况需要注意,name 属性可以配置多个,用逗号分割,但是 id 属性是不能配置多个的,例如:
<bean id="watermelon,w1,w2,w3" class="com.xiaomaomao.entity.Watermelon"> </bean>
这种情况下 id 属性不会和 name 属性一样进行分割,这里的 id 属性只有一个,beanName 就是 watermelon,w1,w2,w3
这样的结果也正好印证了我们的配置.至此 bean 标签的 Id 和 name 属性的源码分析就到这里结束了.