我的博客
spring的属性注入属于spring bean的生命周期一部分,bean的生命周期首先记住两个概念:
所谓的bean的生命周期就是磁盘上的类通过spring扫描,然后实例化,属性注入,跟着初始化,继而放到容器当中的大概过程
通过finishBeanFactoryInitialization初始化我们的单例非懒加载的Bean,创建一个Z.java来看看
首先实例化ApplicationContext容器对象
会调用构造方法里的refresh来初始化容器
执行完前后可以看到,在方法finishBeanFactoryInitialization里,Spring已经帮我们创建好了Bean,也就是bean的生命周期都在此方法内
通过此方法内的注释可以知道,接着会执行finishBeanFactoryInitialization#beanFactory.preInstantiateSingletons().doGetBean(beanName);方法
doGetBean方法内容有点多,这个方法非常重要,整个spring bean生命周期中这个方法有着举足轻重的地位,开始分析doGetBean的执行流程
getSingleton(beanName) 是实现自动注入最主要的方法,spring初始化bean的时候先判断bean是否在容器当中,如果存在,直接返回,如不存在则生产。同时,这个方法也是API getBean(beanName) 的底层实现。
spring在初始化的时候容器当中肯定是没有Z的,为什么还需要去判断一下呢?因为一个bean被put到单例池(容器)的渠道有很多;除了spring容器初始化—扫描类----实例化-----put到容器这条线之外还有很多方法可以把一个对象put到单例池,比如:
这里是第一次从单例池中拿Z,肯定是null,所以 singletonObject == null 成立
判断Z是否在正在创建的集合里,第一次肯定是不在的,isSingletonCurrentlyInCreation == false 不成立
以上判断完成,会继续往下面走,继续实例化,然后下面又调用了一次getSingleton,上面也调用了一次getSingleton,两个getSingleton方法并不是同一个方法,这是方法重载,第二次getSingleton就会把bean创建出来
第二次getSingleton就会把我们bean创建出来,换言之整个bean如何被初始化的都是在这个方法里面
当spring觉得可以着手来创建bean的时候首先便是调用beforeSingletonCreation(beanName); 判断当前正在实例化的bean是否存在正在创建的集合当中
当代码运行完this.singletonsCurrentlyInCreation.add(beanName)之后可以看到singletonsCurrentlyInCreation集合当中只存在一个Z,并且没有执行z的构造方法,说明spring仅仅是把Z添加到正在创建的集合当中,但是并没有完成bean的创建(因为连构造方法都没调用)
当运行完this.singletonsCurrentlyInCreation.add(beanName) 之后结果大概如下图这样
之后就会走到singletonObject = singletonFactory.getObject();这个调用的是createBean(beanName, mbd, args) 方法;把创建好的spring bean返回出来;至此第二次getSingleton方法结束
createBean()方法中调用了doCreateBean方法创建spring bean
运行完createBeanInstance之后控制台打印了Z的构造方法的内容,说明Z对象已经被创建了,但是这个时候的Z不是spring bean,因为spring bean的生命周期才刚刚开始
把前面知识串起来,画一下当前代码的语境
这个createBeanInstance方法是如何把对象创建出来的呢?大致流程是:
实例化完成后,开始执行方法populateBean完成属性自动注入,我们在Z里面添加自动注入X
首先看一下方法执行前的情况
没有执行populateBean之前只实例化了Z,X并没实例化;接下来看看执行完这行代码之后的情况
Z 填充 X (简称 zpx)首先肯定需要获取X,调用getBean(x),getBean的本质上文已经分析过,就是调用getSingleton(beanName),进入到第一次调用getSingleton。第一次getSingleton会从单例池获取一下X,如果X没有存在单例池则开始创建X,创建X的流程和创建Z一模一样,都会走bean的生命周期;比如把X添加到正在创建的bean的集合当中,推断构造方法,实例化X
{% qnimg Spring自动注入源码/17.png %}
这样就完成了X的自动注入
Z 填充 X,但是X对象里又自动注入Z
@Component
public class Z {
@Autowired
private X x;
public Z() {
System.out.println("z create");
}
}
@Component
public class X {
@Autowired
private Z z;
public X() {
System.out.println("x create");
}
}
是否能够获取到Z呢?首先我们想如果获取失败则又要创建z—>实例化z—填充属性----获取x----就无限循环了;所以结果是完成了循环依赖,所以这里肯定能够获取到Z,为什么呢?
联系上文第一次调用getSingleton是无法获取到Z的?因为上面说过第一次调用getSingleton是从单例池当中获取一个bean,但是Z显然没有完成生命周期(Z只走到了填充X,还有很多生命周期没走完)
所以应该是获取不到的?为了搞清楚这个原因得去查看第一次getSingleton的源码
总结:
首先spring从单例池当中获取Z,前面说过获取不到,然后判断是否在正在创建bean的集合当中,前面分析过这个集合现在存在Z和X;所以if成立进入分支;进入分支,spring直接从三级缓存中获取Z,根据前面的分析三级缓存当中现在什么都没有,故而返回null
进入下一个if分支,从二级缓存中获取一个ObjectFactory工厂对象;二级缓存中存在Z和X,故而可以获取到;跟着调用singletonFactory.getObject();拿到一个半成品的bean Z对象;然后把Z对象放到三级缓存,同时把二级缓存中Z清除(此时二级缓存中只存在一个X了,而三级缓存中多了一个Z)
问题:
InitializingBean 对应生命周期的初始化阶段,实例化和属性赋值都是Spring帮助我们做的,能够自己实现的有初始化和销毁两个生命周期阶段.Aware方法都是执行在初始化方法之前,所以可以在初始化方法中放心大胆的使用Aware接口获取的资源,这也是我们自定义扩展Spring的常用方式
这几种定义方式的调用顺序没有必要记.因为这几个方法对应的都是同一个生命周期,只是实现方式不同
参考:
博客