上一篇【spring源码-7】bean的实例化(4)后置处理
应用示例:
定义类实现 FactoryBean 接口:
测试结果:
现象:
通过 “myFactoryBean” 获取到的并非是 MyFactoryBean 实例,而是 Student 实例。
通过 “&myFactoryBean” 获取到了 MyFactoryBean 实例。
spring容器加载完成后,实例化了 MyFactoryBean,没有实例化 Student,获取的时候才完成了实例化。
源码解析
877 行:实例化bean时会先判断当前bean是否实现了 FactoryBean 接口。
878 行:FactoryBean 的 getBean();
getBean(FACTORY_BEAN_PREFIX + beanName)
246 行:转换 beanName。调用到本类方法如下:
1. 转换 beanName
说白了就是 “&beanName” 的 “&” 去掉,返回beanName。
2. 从一级缓存中获取
250 行:先从一级缓存中获取实例。
前提条件:此时上下文对象已经初始化完成,一级缓存中是存在 “MyFactoryBean” 实例的。 beanName 又是转换过后的名称,肯定没有 “&”,此时都能获取到 MyFactoryBean 实例。
- applicationContext.getBean("myFactoryBean");
可以拿到 MyFactoryBean 实例。sharedInstance 就是 MyFactoryBean 的实例。- applicationContext.getBean("&myFactoryBean");
可以拿到实例,sharedInstance 也是 MyFactoryBean 的实例。
3. getObjectForBeanInstance(sharedInstance, name, beanName, null)
此时:name 可能有 “&”,beanName 一定没有“&”(前面已经去掉了)。
- applicationContext.getBean("myFactoryBean");
入参:sharedInstance 是 MyFactoryBean 的实例,name = " myFactoryBean ",beanName = "myFactoryBean",mbd = null- applicationContext.getBean("&myFactoryBean");
入参:sharedInstance 是 MyFactoryBean 的实例,name = " &myFactoryBean ",beanName = "myFactoryBean",mbd = null
1792 行:如下图所示:判断当前bean是否是“&”,如果有就说明当前获取bean时传的是 "&myFactoryBean"。
1796 行:再检查下当前bean是否实现了 FactoryBean 接口,如果没有就抛出异常,因为当前就是 FactoryBean 的逻辑。
1802 行:直接返回 MyFactoryBean 实例。
总结:
1792 - 1803 行:applicationContext.getBean("&myFactoryBean")进入 if 判断,最终返回从一级缓存中获取到的 MyFactoryBean 实例
方法走下来,是 applicationContext.getBean("myFactoryBean")的逻辑。
1808 - 1810 行:如果 beanInstance(刚才从一级缓存中获取到实例) 不是 FactoryBean 类型的,就返回。
1817 行:从 factoryBeanObjectCache
缓存中获取实例。
注意:此时的缓存并非是spring容器的一级缓存。
factoryBeanObjectCache
缓存是存储 FactoryBean 类的 getObject() 返回的实例。
1827 行:缓存拿不到就创建。
169 行:调用到 FactoryBean 的 getObject();创建Student对象并添加到factoryBeanObjectCache
缓存中。
总结:
applicationContext.getBean("")时,传 "&factoryBean" 获取 FactoryBean 实例,传 "factoryBean" 获取 getObject() 返回的对象。
FactoryBean 类会随着IOC容器的创建 完成实例化,但是 getObject() 中的对象不会,只有当获取时才会完成实例化。
getObject() 中的对象也是由IOC完成创建的,但是并没有放在一级缓存中管理,而是放在了
factoryBeanObjectCache
缓存中。
注:本文通过源码 + 行说明的方式进行描述,若不好理解可留言。本文仅为个人学习记录,有错误的地方,请大佬们指正。