今天讲解下beanFactory.freezeConfiguration();也就是常说的冻结Bean定义。
首先我们来看下一个有趣的现象:
第一个类UserTest:
没什么特别的只是有个构造方法,方便我们看它实例化
第二个类:
这个类就有点意思了,首先我获取了UserTest的BD,然后调用beanFactory.freezeConfiguration();
最后设置下懒依赖为true,楼主目的很简单,就是为了测试我们在冻结后,是否可以更改BeanDefinition的属性;我们来看结果:
结果发现:即使设置了懒依赖,仍然进行了实例化操作,这里楼主但是就推断BeanDefinition确实被冻结了。
而后当楼主在测试其他属性时就发现不对劲了,看代码:
这里楼主想将Scope设置为原型模式,按照上面理解,应该也不生效,但是结果如下:
我们发现多例模式竟然生效了,这就非常奇怪,为什么设置懒加载不生效,但是多例生效。
下面进入源码环节:
首先直接跳到
refresh()方法下的invokeBeanFactoryPostProcessors(beanFactory);来进行查看
这里楼主分两步来进行调试,第一步我们看一下我们设置Bean定义为懒加载时为什么不生效,断点位置如下:
此时我们查看两个BeanFactory的属性
第一个就是BeanDefinitionMap,我们发现经过invokeBeanFactoryPostProcessors(beanFactory);方法后,我们即使冻结Bean定义,我们的懒加载也被正确设置为了true,那么按理说应该不会被实例化啊,与我们结果不符合啊,别急我们再看另外一个属性:
看我们的mergedBeanDefinitions的属性,发现它的懒加载为null,这才是我们想要的效果,没错懒加载为null,这个就是我们为什么第一个测试,懒加载设置后,并没有生效,因为spring是按照这个来进行实例化的,请注意绿色框框,后面有用,翻译过来就是是否过期,后面会解释,大家大概记住,如果为true,这个合并Bean定义代表已经过时,spring会重新合并。
下面对比实验开始,我来看相同断点下,Scope的情况:
同样于懒加载一样,我们的Scope也正确被修改,看另外一个mergedBeanDefinitions
可以发现毫无区别,那么是什么导致后面差异,一个生效一个未生效
继续调试:下面截图不太好截图,大概给大家说下,大家最好鼠标跟着点进去,就是refresh()方法内的实例化方法,遍历调用getBean
我这里直接说了,到我们红框这一步,RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);这一步我们实际获取的BD都是合并后的,也就是我们未修改的(我们修改的BeanDefinitionMap里面的),原因如下:
大家看到了吧,只要stale属性为false,我们都会获取合并后的,所以我们修改的BD时无效的,这里可以看出我们冻结时有效果的,继续看:
注意看绿色框处,bd定义不能为抽象的,bd定义必须是单例,bd定义不是懒加载,才可以进入getBean(),注意了,我们此时的BD是合并后的哦,也就是没被修改的,所以我们设置的懒加载无效的,可以进入(这里就是为什么懒加载设置后无作用的原因,本质上我们只是改了BD而不是合并后的BD,所以改了没作用),同样我们修改成多例也是无效的,所以两个步骤都可以同时进入getBean()流程,这里可能有人要问了,那么为什么从结果看我们修改的多例生效了呢,下面就是解释:
在doGetBean()我们发现了多例生效的玄机:
没错代码到红框处,获取出来的居然是被更改为多例的BD,这里有人就蒙蔽了,为什么前面getMergedLocalBeanDefinition(beanName)获取的是未修改的,这里获取的是修改过的呢?答案在绿框处,来看绿框做了啥
令人震惊有没有,它将我们合并Bean定义设置为过期了,所以在下面我们getMergedLocalBeanDefinition(beanName)获取的实际上通过BeanDefinitionMap里面被修改过的重新生成的,这里大家就发现了,我们设置的多例生效了,实际上我们懒加载也生效了,但是由于跳过了判断懒加载的条件,所以即使懒加载生效了,也仍然不起作用。到这里流程解释得差不多了,但是统观全局,我们发现stale这个属性非常重要,就应因为他为false时,我们得懒加载和多例在geetBean()之前,都是未被修改的,总结下冻结确实有作用,当仅限于getBean之前。
而我们如果不加beanFactory.freezeConfiguration();那么在invokeBeanFactoryPostProcessors(beanFactory);之后该属性实际上是true,原因就在invokeBeanFactoryPostProcessors(beanFactory);方法里面的最后一行
我们看下冻结的代码:
全文完~~~