写在最前
《Spring是如何回调Aware系列接口?》这篇文章的最后留了个小尾巴,下面我们就围绕着这个“小尾巴”进行分析,来说说initializeBean()是如何被调用到的。
正文
《Spring Boot创建Beans的过程分析》的最后说道:beans的创建是AbstractAutowireCapableBeanFactory类的createBean(String beanName, RootBeanDefinition mbd, Object[] args)方法中,如下图:
那现在,我们接着上次的文章分析,分析下 - Spring Boot是如何调用到initializeBean()方法的?
还记得initializeBean()方法做些什么吗?简单回顾下:
initializeBean()中主要做的是初始化了部分beans、应用了spring的beans生命周期中的一些回调方法。
如:BeanNameAware、BeanClassLoaderAware、BeanFactoryAware。。。详细的内容请看《Spring是如何回调Aware系列接口?》这篇文章。
1. AbstractAutowireCapableBeanFactory的createBean(xxx) 方法
从上图中我们看出,在调用createBean(xxx)方法的过程中,获取到了beanName,mdbToUse(BeanDefinition)和args三个参数,这个三个参数很关键。beanName定义了该bean的名字,通常我们使用Resource和Qualifier注解的时候会用到;mdbToUse是一个BeanDefinition,也就是我们通常在xml文件中定义的<bean xxx/>元素的对象表示;args是spring框架的xml解析器解析到的传入参数,用来调用构造函数或工厂方法来创建Bean对象的。然后,调用了doCreateBean()方法。
2. AbstractAutowireCapableBeanFactory的doCreateBean(xxx) 方法
看上图中的注释,也可以说明beanName,mdbToUse和args这三个参数的含义。这个方法比较长,其中有一行关键的代码是我们要讨论的:
看到这里,终于发现我们的initializeBean()方法被调用了。下面我们详细分析下initializeBean()方法里面到底做了些什么.
我们以图中的序列号为单位,进行逐一分析:
1.invokeAwareMethods()方法
此方法的入参参数是String 和Object类型。为什么这里要设置成Object类型的呢?因为我们的bean对象可能实现了BeanNameAware、BeanClassLoaderAware、BeanFactoryAware中的任意几个,为了兼容所有类型,方便向下转型,这里就用Object了。该方法以回调的方式调用Aware接口的实现,把spring内部的对象传递到外部供开发者使用,同时也支持了用户以回调的方式自定义了spring内部的一些对象或配置。
2.applyBeanPostProcessorsBeforeInitialization()方法
此方法获取了所有的BeanPostProcessor对象。然后,循环调用了所有BeanPostProcessor的postProcessBeforeInitialization方法。这里我们就不说BeanPostProcessor是如何被设置到Spring里的了。我们看看官方是如何定义BeanPostProcessor的:Factory hook that allows for custom modification of new bean instances
,也就是说你可以在这里实现初始化bean前的一些自定义。从此方法的签名,我们也可以看出来,此方法会在bean初始化前调用。什么!!!等等,难道这时候bean还没有被初始化???很奇怪是吧,我们先留下这个问题,稍后再讨论。
3.invokeInitMethods()方法
从上图的注释中,我们得知:invokeInitMethods方法是给了bean一个使用设置好的properties的机会,也给了一个了解自己所在的beanFactory的机会,同时检查该bean有没有实现InitializingBean接口或者是在xml配置文件中自定义了bean的init-method方法,以便回调它们。
从上面的话,我们可以推断出:该bean已经被前面的某个步骤给实例化了,并且设置好了它的properties,这样我们才可以使用这些properties做些事情。这样也就解释了,前面的疑问 - bean还没被初始化???其实,bean已经被实例化, 这里的初始化是指调用InitializingBean的afterPropertiesSet()或开发者自定义的init-method,并不等同于实例化,请不要混淆了!至于什么时候实例化和设置properties的,我们以后在分析。
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
上面的这行代码有点晦涩难懂,意思是:
a. 必须实现了InitializingBean接口,否则退出这个if语句。
b. 实现了InitializingBean接口还不行,还必须mbd == null或者mbd不为null时但必须不包含afterPropertiesSet() 方法。
我觉得我说了等于没说!那这个到底是什么意思呢?好了,我不卖关子了。既然,spring是在初始化bean,那你想:bean会被多次初始化吗?肯定不是的吧,在整个生命周期过程中只应该被初始化一次。mbd是个BeanDefinition对象,对应着xml配置文件中<bean xxx>元素的定义。如果该bean的实例已经存在了,那么mbd将会为空。看这个方法上的注释也是这么说明的:can also be null, if given an existing bean instance
。
bean已经存在了,mbd就会为空?那这是为什么呢?我自己是这样解释:通常我们获取bean的时候,是通过getBean方法。当bean不存在时,spring需要从已经解析过的众多BeanDefinition中找到我们需要的,然后设置给mbd;如果在根据beanName获取bean的时候,发现该bean已经存在了,那么mbd就不要再次获取了,所以就为空了(猜想,需要证明)。
那既然实例已经bean存在了,直接调用afterPropertiesSet()方法,也就没什么问题了。那如果mbd !=null也就是说,bean是第一次初始化,如果发现该bean对应的BeanDefinition对象mbd中也包含了一个afterPropertiesSet方法,那么就不执行if语句块了。
String initMethodName = mbd.getInitMethodName();
if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
invokeCustomInitMethod(beanName, bean, mbd); // 通过反射调用
}
而这段代码,是spring通过反射调用了init-method方式设置的方法,调用的时机是mbd!=null,也就是第一次初始化的时候。具体代码我就不说了,比较简单。
4.applyBeanPostProcessorsAfterInitialization()方法
这个方法和applyBeanPostProcessorsBeforeInitialization()方法类似,就没什么好说的了。
写在最后
分析过程中,可选择的路径可能比较多,我只挑选了其中的一条分析路径。
就写到这里吧,该下班了!晚安!