前言
上文讲到了整体流程、以及配置类的BeanDefinition注册到ioc容器中。
其实bean的实例化是一件非常复杂的事情,因为不同的bean实例化的方式都是有一些差别的。而且bean在实例化过后会经历很多后置的处理(BeanPostProcess),所以本文我会核心讲如何实例化我们的单例bean。
本文暂时不会涉及到很多有关于后置处理器的核心点(后续可能会讲到),本文暂时着重bean是如何实例化的。
1.refresh之实例化单实例bean
前文中refresh刷新上下文中,我标记了有一个重点的方法,初始化所有单实例bean
// Instantiate all remaining (non-lazy-init) singletons.
//重点:完成所有非延迟加载bean的初始化
finishBeanFactoryInitialization(beanFactory);
这一个方法里中的完成做了很多事情。
暂时先看看我debug之后整理出来的实例化URL。(中间已经省略了很多细节,一张UML图还是放不下,放了两张UML图,核心只在实例化bean)。
是不是感觉很杂、很多?已经觉得有点难度了。不,这个还只是我省略了很多实现细节的UML时序图。当你看着源码,跟着我的时许图深入到这一步时,你就会发现,spring的ioc容器底层原理真的就只是反射而已。
但如果ioc只有反射,是不是太单薄了。当然ioc在实例化单例bean的过程中,还做了很多事情。比如依赖注入、属性填充、解决循环依赖等,这些事情都是在我画的时序图某一步中完成的,所以还需要继续进行深入源码,实例化过程就只通过时序图表示(源码太多,影响阅读,有兴趣的可以跟着我的思路跟踪一下源码)。
好了,这就是整个单例bean的实例化过程,有兴趣的小伙伴可以跟踪一下源码。代码重点的地方是在太多了,我只能从常见的经典场景拆分进行逐一说明了(不贴点源码,总觉内容很空翻,哈哈)。
2.解决循环依赖
经典的场景 类A依赖类B,类B依赖类C,类C依赖类A。即 A->B,B->C,C->A。
通常而言有几种情况下的循环依赖是解决不了的。
比如构造器循环依赖(spring会抛出BeanCurrentlyInCreationException异常)、原型循环依赖。
先看看什么是循环依赖:
但是通过setter方法的单例模式创建bean却可以解决循环依赖,先一睹为快,看看我的代码及运行结果
public class MyApplication {
public static void main(String[] args) {
//spring上下文,启动一个spring应用程序。
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyApp.class);
TestA testA =(TestA) context.getBean("testA");
System.out.println(testA.getTestB());
}
}
运行结果:
com.upanda.app.test.TestB@1b0375b3
Process finished with exit code 0
ok,是不是有点疑惑为什么spring可以解决这种看似死循环的循环依赖呢?那就跟踪一下源码看看,spring是如何解决循环依赖的。
直接进入源码,在上述UML时序图11步骤里的 **T doGetBean()**方法中开始(注意:我的set方法上标注了@Resource注解)。这个里面涉及到了DI依赖注入,属性填充等等操作。
在 步骤2.populateBean(testA) 之前,做了一个很关键的操作,代码如下
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
//如有必要,添加给定的单例工厂以构建指定的单例.被要求进行单例者的急切注册,例如能够解析循环引用
//ObjectFactory很关键,当填充属性时就是填充的它
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
getEarlyBeanReference()方法:
//获取用于早期访问指定bean的依赖,通常是为了解决循环依赖
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
ok,真正的关键前期操作就是前两段源码,在A->B->C->A的过程中,
A依赖B,尝试getSingleton(B),get不到B,实例化B;
B依赖C,尝试getSingleton(C),get不到C,实例化C;
C又依赖了A,先会去尝试getSingleton(),咦,这个时候竟然get到了A。这个A是啥呢?
在循环依赖C->A时 ,在上述UML时序图11步骤里的 **T doGetBean()**中的第一个getSingleton()方法中,就拿到了单例对象,终结了循环依赖。
!
在 步骤2.populateBean属性填充之前,允许循环依赖的bean A,通过ObjectFactory A,提前暴露了exposedObject 的bean A对象。(注意:此时Bean A并没有完成属性填充,所以依赖的类B并没有注入进来。)
ok,有了循环终结条件,那么当C依赖A完成了bean的创建,随之B依赖C也完成了bean的创建。,A依赖B也完成了bean的创建。
bean的创建顺序就是: C->B->A
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
//循环依赖 A->B->C->A,C依赖A,A正在创建,进入其中
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//在循环依赖中,实际返回的A为:singletonFactory.getObject()
singletonObject = singletonFactory.getObject();
//早起的单例对象earlySingletonObjects
this.earlySingletonObjects.put(beanName, singletonObject);
//除此
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
3.依赖注入
在来看看依赖注入的核心代码,第4步的InjectedElement#inject()方法(后续可能会总结所有依赖注入的方式)。
protected void inject(Object target, @Nullable String requestingBeanName, @Nullable PropertyValues pvs)
throws Throwable {
if (this.isField) {
Field field = (Field) this.member;
ReflectionUtils.makeAccessible(field);
field.set(target, getResourceToInject(target, requestingBeanName));
}
else {
if (checkPropertySkipping(pvs)) {
return;
}
try {
Method method = (Method) this.member;
ReflectionUtils.makeAccessible(method);
//反射完成依赖注入
method.invoke(target, getResourceToInject(target, requestingBeanName));
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
}
证明一下类C注入的类A,类A并没有完成testB的注入。
现在讲的只是属于spring 的容器ioc范畴,本文和上文只是讲解了普通bean的注册以及实例化。
还有一些@Configuration类,@Component,@import的注解的类、FactoryBean类的BeanDefinition的注册以及实例化,以及容器的扩展点BeanFactory的后置处理器、BeanPostProcess后置处理器等等,这些 其实都很复杂,简简单单一个其实可以牵扯出很多代码设计的思想,而且很多开源的整合了spring的框架也是基于ioc去扩展的,时常用到的spring-mybatis就是基于此与aop完成的。
如果感兴趣的话,尽情期带我的下一篇文章把~
上一篇:1、spring源码解析之概况流程
下一篇:3、spring核心源码解析之@Configuration注解详解