一、前言

从上文的分析中我们知道了:RootBeanDefinition 这是一个最常用的实现类,Spirng 去创建 Bean 时是基于RootBeanDefinition去创建的!

在之前的文章中14(2)-Spring源码-BeanDefinition详解, 我们对BeanDefinition的体系进行了较为深入的介绍, 并且提出了一个**父子bean**的概念, 一 个BeanDefinition是可以设置父类BeanDefinition的, 仅仅需要调用其setParentName即可, 之所以出现父子bean是因为Spring允许将相同bean的定义给抽出来, 成为一个父BeanDefinition, 这样其它的BeanDefinition 就能共用这些公共的数据了, 并且提出了RootBeanDefinitionChildBeanDefinition之间的关系, 以及他们 的缺点, 从而Spring在之后的版本中引入了GenericBeanDefinition, 在Spring的生命周期中, 如果要实例化 一个bean则需要先将bean进行合并, 这样拿到的BeanDefinition才是信息完整的, 本篇文章就来聊聊Spring是 如何合并BeanDefinition的, 从而为后面的内容打下基础

二、getMergedLocalBeanDefinition入口

  1. protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
  2. // Quick check on the concurrent map first, with minimal locking.
  3. RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
  4. if (mbd != null && !mbd.stale) {
  5. return mbd;
  6. }
  7. return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
  8. }

分析:
在获取一个合并后的BeanDifinition的时候, Spring都会调用这个getMergedLocalBeanDifinition方法, 可以看到, Spring会先从mergedBeanDefinitions中获取, 从而可以很好的联想到, 在对一个BeanDifinition进行合并完成后, Spring都会将合并后的BeanDifinition放入到这个Map中, 如果在缓存中没有拿到, 则先 调用getBeanDefinition方法从beanDefintionsMap中获取原始的BeanDefinition, 如果对Spring扫描bean 的代码有所了解的话, 就会明白当一个bean被扫描出来变成一个beanDefintion后就会放入到beanDefintionsMap 中。

三、getMergedBeanDefinition[没有父类BeanDefinition的情况]

  1. if (bd.getParentName() == null) {
  2. // Use copy of given root bean definition.
  3. if (bd instanceof RootBeanDefinition) {
  4. mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();
  5. }
  6. else {
  7. mbd = new RootBeanDefinition(bd);
  8. }
  9. }

分析:
如果一个BeanDefinition没有设置parentName, 表示该beanDefinition是没父BeanDefinition

  • 如果这个BeanDefinitionRootBeanDefinition的实例或子类实例(例如ConfigurationClassBeanDefinition), Spring就会直接克隆一个一模一样的BeanDefinition, cloneBeanDefinition的代码很简单, 即new RootBeanDefinition(this)
  • 如果不是 RootBeanDefinition类型, 则创建一个RootBeanDefinition并将当前bd合并进去, 这时候肯定会有疑问,
  • 两个合并的代码不是一模一样吗, 其实不是, 如果一个BeanDefinitionRootBeanDefinition的子类, 那么 调用克隆方法的时候, 就会new一个该子类, 然后开始克隆, 比如new ConfigurationClassBeanDefinition(this)

四、getMergedBeanDefinition(有父类BeanDefinition的情况)

  1. else {
  2. // Child bean definition: needs to be merged with parent.
  3. BeanDefinition pbd;
  4. String parentBeanName = transformedBeanName(bd.getParentName());
  5. if (!beanName.equals(parentBeanName)) {
  6. pbd = getMergedBeanDefinition(parentBeanName);
  7. }
  8. else {
  9. BeanFactory parent = getParentBeanFactory();
  10. if (parent instanceof ConfigurableBeanFactory) {
  11. pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName);
  12. }
  13. }
  14. // Deep copy with overridden values.
  15. mbd = new RootBeanDefinition(pbd);
  16. mbd.overrideFrom(bd);
  17. }


分析:

  • 如果一个BeanDefinition有父类, 那么Spring就会获取到父类的beanName(因为可能存在别名, 所以调用了transformedBeanName方法),
  • 如果父类的beanName和当前BeanName不一样, 说明是存在真正的父类的, 这个判断是用来防止由于程序员将当前beanName设置为当前BeanDefinition的父类而导致死循环的,
  • 如果真正存在父类, Spring就会先将父类合并了, 因为父类可能还有父类, 该递归方法结束后就能获取到一个完整的父BeanDefinition了, 然后new了一个RootBeanDefinition, 将完整的父BeanDefinition放入进去, 从而初步完成了合并
  • 上面有一个else判断, 是对存在有父容器情况的处理, 对于Spring的父容器, 笔者也不太清楚, 就不展开讲解
  • 当完成了初步的合并后, Spring调用overrideFrom进行了深入的复制, 这里面的代码也很简单, 大家有兴趣可以看看, 就是大量的mbd.setXXX(bd.getXXX)

    五、getMergedBeanDefinition[结尾

    ```java // Set default singleton scope, if not configured before. if (!StringUtils.hasLength(mbd.getScope())) { mbd.setScope(RootBeanDefinition.SCOPE_SINGLETON); }

// Cache the merged bean definition for the time being // (it might still get re-merged later on in order to pick up metadata changes) if (containingBd == null && isCacheBeanMetadata()) { this.mergedBeanDefinitions.put(beanName, mbd); } ``` 分析:

  • 如果这个新创建的RootBeanDefinition没有被设置Scope的话, 那么就是在合并的时候, 被合并的bd本身没有设置Scope属性, 此时Spring会默认设置其为单例,
  • 再之后, 如果bean工厂允许缓存Bean的元数据的情况下,Spring就会将合并后的BeanDefinition存到mergedBeanDefinitions这个map中, 之后便可以从缓存中取得了,
  • 需要注意的是, 在分析这个getMergedBeanDefinition方法的时候, 笔者跳过了containingBd这个变量的分析, 因为我也搞不懂是干嘛的, 但是通常情况下这个值都是null的

总结

1:对于BeanDefinition的合并,Spring都会创建一个新的RootBeanDefinition来进行接收, 而不是用原来的BeanDefinition

2:如果原始BeanDefinition没有父BeanDefinition了, 那么就直接创建一个RootBeanDefinition, 并将原始BeanDefinition作为参数传入构造方法中,

3:如果原始BeanDefinition存在BeanDefinition, Spring 除了会做上述的操作外, 还会调用overrideFrom方法进行深入的合并, 其实就是一系列的setXXX方法的调用而已, 在合并完成后, 对于合并后的

4:BeanDefinition如果没有作用域, 则设置为单例, 并且将合并的BeanDefinition放入到mergedBeanDefinitions这个map中缓存起来