下面一起来看下这两个步骤中, bean
是如何进行加载的。
- 前言
- 时序图
- 代码分析
- FactoryBean 的使用
- 从缓存中获取单例 bean
- 从 bean 的实例中获取对象
- 获取单例
- 准备创建
bean
- 处理 Override 属性
- 实例化前的前置处理
- 创建 bean
- 创建 bean 的实例
- 处理循环依赖
- 属性注入
- 初始化 bean
- 注册 disposableBean
- 总结
- 参考资料
时序图
我们的代码分析都是围绕着这个方法,请同学们提前定位好位置:
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
这个 bean
加载的代码量是有点多的,已经超过 100 行,所以整理了时序图,希望能对加载流程有个清晰的概览:
这个时序图介绍了 bean
加载的大体流程,还有很多细节没在图中进行展示。我们先对整体流程有个了解,然后跟着代码一起深入分析吧。
代码分析
再提示一下:由于代码量很多,每次贴大段代码看起来会比较吃力,所以展示的是我认为的关键代码,下载项目看完整注释,跟着源码一起分析~
码云 Gitee 地址
Github 地址
FactoryBean 的使用
在分析加载流程之前,有个前置概念要了解下,在一般情况下, Spring
是通过反射机制利用 bean
的 class
属性指定实现类来实例化 bean
。
引用书本:
在某些情况下,实例化
bean
比较复杂,例如有多个参数的情况下,传统方式需要在配置文件中,写很多配置信息,显得不太灵活。在这种情况下,可以使用Spring
提供的FactoryBean
接口,用户可以通过实现该接口定制实例化bean
的逻辑。
FactoryBean
接口定义了三个方法:
{
T getObject()throwsException;
Class<?> getObjectType();
defaultboolean isSingleton(){
returntrue;
}
}
主要讲下用法吧:
当配置文件中的
<bean>
的class
属性实现类是FactoryBean
时,通过getBean()
方法返回的不是FactoryBean
本身,而是FactoryBean#getObject()
方法返回的对象。
使用 demo
代码请看下图:
扩展 FactoryBean
之后,需要重载图中的两个方法,通过泛型约定返回的类型。在重载的方法中,进行自己个性化的处理。
在启动类 Demo
,通过上下文获取类的方法 context.getBean("beanName")
,使用区别是 beanName
是否使用 & 前缀,如果有没有 & 前缀,识别到的是 FactoryBean.getObject 返回的 car
类型,如果带上 & 前缀,那么将会返回 FactoryBean
类型的类。
验证和学习书中的概念,最快的方式是运行一遍示例代码,看输出结果是否符合预期,所以参考书中的例子,自己手打代码,看最后的输出结果,发现与书中说的一致,同时也加深了对 FactoryBean
的了解。
为什么要先讲 BeanFactory
这个概念呢?
从时序图看,在 1.5 那个步骤,调用了方法:
org.springframework.beans.factory.support.AbstractBeanFactory#getObjectForBeanInstance
在这一步中,会判断 sharedInstance
类型,如果属于 FactoryBean
,将会调用用户自定义 FactoryBean
的 getObject()
方法进行 bean
初始化。
实例化的真正类型是 getObjectType()
方法定义的类型,不是 FactoryBean
原来本身的类型。最终在容器中注册的是 getObject()
返回的 bean
。
提前讲了这个概念,希望大家在最后一步时不会对这个有所迷惑。
从缓存中获取单例 bean
// Eagerly check singleton cache for manually registered singletons.
// 检查缓存中或者实例工厂是否有对应的实例或者从 singletonFactories 中的 ObjectFactory 中获取
Object sharedInstance = getSingleton(beanName);
protectedObject getSingleton(String beanName,boolean allowEarlyReference){
Object singletonObject =this.singletonObjects.get(beanName);
// 检查缓存中是否存在实例
if(singletonObject ==null&& isSingletonCurrentlyInCreation(beanName)){
// 记住,公共变量都需要加锁操作,避免多线程并发修改
synchronized(this.singletonObjects){
// 如果此 bean 正在加载则不处理
singletonObject =this.earlySingletonObjects.get(beanName);
if(singletonObject ==null&& allowEarlyReference){
// 当某些方法需要提前初始化,调用 addSingletonFactory 方法将对应的
// objectFactory 初始化策略存储在 singletonFactories
ObjectFactory<?> singletonFactory =this.singletonFactories.get(beanName);
if(singletonFactory !=null){
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
单例模式在代码设计中经常用到,在 Spring
中,同一个容器的单例只会被创建一次,后续再获取 bean
直接从单例缓存 singletonObjects
中进行获取。
而且因为单例缓存是公共变量,所以对它进行操作的时候,都进行了加锁操作,避免了多线程并发修改或读取的覆盖操作。
还有这里有个 earlySingletonObjects
变量,它也是单例缓存,也是用来保存 beanName
和 创建 bean
实例之间的关系。
与 singletonFactories
不同的是,当一个单例 bean
被放入到这 early
单例缓存后,就要从 singletonFactories
中移除,两者是互斥的,主要用来解决循环依赖的问题。(循环依赖下一篇再详细讲吧)
从 bean 的实例中获取对象
在 getBean
方法中, getObjectForBeanInstance
是个高频方法,在单例缓存中获得 bean
还是 根据不同 scope
策略加载 bean
,都有这个方法的出现,所以结合刚才说的 BeanFactory
概念,一起来看下这个方法做了什么。
org.springframework.beans.factory.support.AbstractBeanFactory#getObjectForBeanInstance
// 返回对应的实例,有时候存在诸如 BeanFactory 的情况并不是直接返回实例本身
// 而是返回指定方法返回的实例
bean = getObjectForBeanInstance(sharedInstance, name, beanName,null);
具体方法实现,搜索 注释 4.6 看代码中的注释吧:
交代一下这个方法的流程:
- 验证
bean
类型:判断是否是工厂bean
- 对非
FactoryBean
不做处理 - 对
bean
进行转换 - 处理
FactoryBean
类型:委托给getObjectFromFactoryBean
方法进行处理。
在这个方法中,对工厂 bean
有特殊处理,处理方法跟上面提到的 FactoryBean
使用一样,最终获取的是 FactoryBean.getObject()
方法返回的类型。