一、前言
从上文的分析中我们知道了:RootBeanDefinition 这是一个最常用的实现类,Spirng 去创建 Bean 时是基于RootBeanDefinition去创建的!
在之前的文章中14(2)-Spring源码-BeanDefinition详解, 我们对BeanDefinition的体系进行了较为深入的介绍, 并且提出了一个**父子bean**的概念, 一 个BeanDefinition是可以设置父类BeanDefinition的, 仅仅需要调用其setParentName即可, 之所以出现父子bean是因为Spring允许将相同bean的定义给抽出来, 成为一个父BeanDefinition, 这样其它的BeanDefinition 就能共用这些公共的数据了, 并且提出了RootBeanDefinition和ChildBeanDefinition之间的关系, 以及他们 的缺点, 从而Spring在之后的版本中引入了GenericBeanDefinition, 在Spring的生命周期中, 如果要实例化 一个bean则需要先将bean进行合并, 这样拿到的BeanDefinition才是信息完整的, 本篇文章就来聊聊Spring是 如何合并BeanDefinition的, 从而为后面的内容打下基础
二、getMergedLocalBeanDefinition入口
protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {// Quick check on the concurrent map first, with minimal locking.RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);if (mbd != null && !mbd.stale) {return mbd;}return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));}
分析:
在获取一个合并后的BeanDifinition的时候, Spring都会调用这个getMergedLocalBeanDifinition方法, 可以看到, Spring会先从mergedBeanDefinitions中获取, 从而可以很好的联想到, 在对一个BeanDifinition进行合并完成后, Spring都会将合并后的BeanDifinition放入到这个Map中, 如果在缓存中没有拿到, 则先 调用getBeanDefinition方法从beanDefintionsMap中获取原始的BeanDefinition, 如果对Spring扫描bean 的代码有所了解的话, 就会明白当一个bean被扫描出来变成一个beanDefintion后就会放入到beanDefintionsMap 中。
三、getMergedBeanDefinition[没有父类BeanDefinition的情况]
if (bd.getParentName() == null) {// Use copy of given root bean definition.if (bd instanceof RootBeanDefinition) {mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();}else {mbd = new RootBeanDefinition(bd);}}
分析:
如果一个BeanDefinition没有设置parentName, 表示该beanDefinition是没父BeanDefinition的
- 如果这个
BeanDefinition是RootBeanDefinition的实例或子类实例(例如ConfigurationClassBeanDefinition), Spring就会直接克隆一个一模一样的BeanDefinition,cloneBeanDefinition的代码很简单, 即new RootBeanDefinition(this) - 如果不是
RootBeanDefinition类型, 则创建一个RootBeanDefinition并将当前bd合并进去, 这时候肯定会有疑问, - 两个合并的代码不是一模一样吗, 其实不是, 如果一个
BeanDefinition是RootBeanDefinition的子类, 那么 调用克隆方法的时候, 就会new一个该子类, 然后开始克隆, 比如new ConfigurationClassBeanDefinition(this)
四、getMergedBeanDefinition(有父类BeanDefinition的情况)
else {// Child bean definition: needs to be merged with parent.BeanDefinition pbd;String parentBeanName = transformedBeanName(bd.getParentName());if (!beanName.equals(parentBeanName)) {pbd = getMergedBeanDefinition(parentBeanName);}else {BeanFactory parent = getParentBeanFactory();if (parent instanceof ConfigurableBeanFactory) {pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName);}}// Deep copy with overridden values.mbd = new RootBeanDefinition(pbd);mbd.overrideFrom(bd);}
分析:
- 如果一个
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中缓存起来
