BeanDefinition注册到spring容器

parseBeanDefinitionElement 后返回AbstractBeanDefinition对象
notion image
跳回调用parseBeanDefinitionElement 方法的上一级parseBeanDefinitionElement
notion image
可以看到,前面解析bean标签下的各种属性和标签都是在方法parseBeanDefinitionElement中完成了,然后得到GenericBeanDefinition类型的beanDefinition。
最后,我们可以看到parseBeanDefinitionElement方法其实就是将beanDefinition,连带着解析得到的别名aliasesArray一起封装到了BeanDefinitionHolder中,BeanDefinitionHolder我们可以理解为是持有BeanDefinition的一个对象而已。
跳到上一层processBeanDefinition

BeanDefinitionReaderUtils

notion image
调用BeanDefinitionReaderUtils 的方法registerBeanDefinition()
notion image
BeanDefinitionRegistryregisterBeanDefinition 去注册, 这里的BeanDefinitionRegistry 其实是XmlBeanFactory,根据类图关系,最终调用的是父类DefaultListableBeanFactoryregisterBeanDefinition 方法
notion image
notion image

DefaultListableBeanFactory

notion image
方法中的代码量比较多,我们依次来看下,首先,beanDefinition肯定是AbstractBeanDefinition的实例,所以调用方法validate进行最后一次校验。

validate

notion image
可以看到,在方法validate中不允许methodOverrides属性和factoryMethodName属性同时设置,这是为什么呢?
不知道大家还记得,在上一节在解析lookup-method标签和replaced-method标签时,会将这两个标签需要覆盖的方法名设置到MethodOverrides中,一旦MethodOverrides不为空,这就意味着Spring创建出来bean还要重新覆写这些方法。
而factoryMethodName属性也就是工厂方法的名称,了解过工厂设计模式的同学应该都知道,通过工厂方法也可以创建一个bean出来,但是这相比于Spring默认的创建方式而言,算是一种不允许外界覆盖bean中方法的创建方式了。
也就是说要么你通过工厂方法创建bean,要么就按Spring普通的方式来创建bean,两者选其一,当然这些后面我们在bean的加载环节会详细讲解,暂时只需要知道这两种创建bean的方式是不一样的,我们这里只能存在其中一种。
接下来,方法prepareMethodOverrides就是对MethodOverrides的一些准备工作了,我们可以简单进去看下:
notion image
notion image
可以看到,在prepareMethodOverrides方法中如果存在MethodOverrides属性的话,就会通过方法prepareMethodOverride依次预处理这些需要覆盖的方法。
预处理的方式也比较简单,就是在方法prepareMethodOverride中判断一下,如果lookup-method标签或replaced-method标签中配置了bean中需要覆盖的方法,就将MethodOverride中的overloaded属性值设置为false。
为什么要这样设置overloaded属性值为false呢?当然是为了提高性能,也就是告诉Spring MethodOverride中记录的那些需要覆盖的方法,在bean中是没有重载方法的,这样的话,Spring就不需要额外根据参数去检查bean中是否存在其他重载方法了,避免了一定的性能损耗
 

beanDefinitionMap

/** Map of bean definition objects, keyed by bean name. */ private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
注册前的validate完成后DefaultListableBeanFactory 从beanDefinitionMap中获取beanName的值,
beanDefinitionMap就是一个ConcurrentHashMap类型的Map,我们之前或多或少都听说过Spring容器就是一个Map,确实,beanDefinitionMap就是大名鼎鼎Spring容器,Spring容器从本质上来说就是一个Map。
因为beanDefinitionMap是成员变量,难免会有并发安全问题,所以这里使用多线程安全的ConcurrentHashMap作为Spring的容器。
notion image
在if分支上有一个方法isAllowBeanDefinitionOverriding在约束着,通过方法的名称我们可以知道,它是用来判断beanDefinitionMap中的元素是否可以被覆盖的。
如果方法isAllowBeanDefinitionOverriding返回结果为true,也就是允许相同名称BeanDefinition覆盖Spring容器的Map,可以看到就会将当前bean的名称beanName1,以及相应的BeanDefinition设置到beanDefinitionMap中了。
成员变量allowBeanDefinitionOverriding的默认值为true。
也就是说在默认情况下,如果出现相同名称的多个bean来注册,在Spring容器对应的beanDefinitionMap中是允许被覆盖的,所以这也在暗示我们在配置bean的时候,尽量不要出现相同名称的bean,否则会被覆盖

resetBeanDefinition

notion image
如果发现当前不是这个名称beanName第一次来注册,或者当前beanName还没有创建相应的单例时,将会调用方法resetBeanDefinition重置和beanName相关的一系列缓存

BeanDefinition别名alias的注册

最后,我们再回到上一个方法中看下:
notion image
可以看到,现在就剩最后一个环节了也就是注册bean的别名,也就是从BeanDefinitionHolder中获取之前解析到的别名,然后依次遍历注册它们。
我们到方法registerAlias中看下:
 
notion image
可以看到,如果需要注册的别名alias,和bean的名称name相同,这就没必要注册了,并且会从aliasMap中删除该别名。
其中,aliasMap的key为bean的别名,value为bean在Spring容器中的实际名称,也就是我们之前看到的beanName。
如果alias和name不相同,此时会先从aliasMap中,根据alias获取registeredName,如果registeredName和name相同,这就意味着名称为name的别名已经被其他的bean注册了,就不需要重复注册了。
notion image
如果registeredName和name不相同,而且因为根据别名alias从aliasMap成功获取了一个非空的bean的名称registeredName了,这就意味着别名alias已经注册了一个bean名称为registeredName了。
此时,如果方法allowAliasOverriding返回false,也就是不允许别名被覆盖,也就是说一个别名alias只能注册一个bean的名称不能注册多个bean的名称,事与愿违,这个时候直接就会抛异常了。
另外,我们可以看到还有一个方法checkForAliasCircle:
notion image
notion image
它主要是为了检查是否存在别名循环的问题,比如,别名alias1对应bean的名称为name1,但是,如果同时还存在别名alias1对应bean的名称为name2,而name2又是bean name1的别名。
相当于同时存在:alias1到name1,alias1到name2,name2再到name1这两个关系,出现了两个起点和重点都相同的循环了,这样就造成了别名循环的问题就会抛异常。
返回再上一层DefaultBeanDefinitionDocumentReaderprocessBeanDefinition 方法
notion image
最后,当BeanDefinition注册到Spring容器之后会调用fireComponentRegistered方法,发布一个BeanDefinition已经被注册的事件通知而已,当然,一般我们都不需要监听这方面的事件。