美文网首页Java Worldspring boot相关程序员
Spring Boot是如何调用到initializeBean(

Spring Boot是如何调用到initializeBean(

作者: 大道至简_Andy | 来源:发表于2017-01-10 22:45 被阅读3481次

    写在最前

    《Spring是如何回调Aware系列接口?》这篇文章的最后留了个小尾巴,下面我们就围绕着这个“小尾巴”进行分析,来说说initializeBean()是如何被调用到的。

    正文

    《Spring Boot创建Beans的过程分析》的最后说道:beans的创建是AbstractAutowireCapableBeanFactory类的createBean(String beanName, RootBeanDefinition mbd, Object[] args)方法中,如下图:

    AbstractAutowireCapableBeanFactory

    那现在,我们接着上次的文章分析,分析下 - Spring Boot是如何调用到initializeBean()方法的?

    还记得initializeBean()方法做些什么吗?简单回顾下:

    initializeBean()

    initializeBean()中主要做的是初始化了部分beans、应用了spring的beans生命周期中的一些回调方法。
    如:BeanNameAware、BeanClassLoaderAware、BeanFactoryAware。。。详细的内容请看《Spring是如何回调Aware系列接口?》这篇文章。

    1. AbstractAutowireCapableBeanFactory的createBean(xxx) 方法
    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) 方法
    doCreateBean(xxx)方法

    看上图中的注释,也可以说明beanName,mdbToUse和args这三个参数的含义。这个方法比较长,其中有一行关键的代码是我们要讨论的:


    doCreateBean(xxx)

    看到这里,终于发现我们的initializeBean()方法被调用了。下面我们详细分析下initializeBean()方法里面到底做了些什么.

    initializeBean()

    我们以图中的序列号为单位,进行逐一分析:
    1.invokeAwareMethods()方法

    invokeAwareMethods()方法

    此方法的入参参数是String 和Object类型。为什么这里要设置成Object类型的呢?因为我们的bean对象可能实现了BeanNameAware、BeanClassLoaderAware、BeanFactoryAware中的任意几个,为了兼容所有类型,方便向下转型,这里就用Object了。该方法以回调的方式调用Aware接口的实现,把spring内部的对象传递到外部供开发者使用,同时也支持了用户以回调的方式自定义了spring内部的一些对象或配置。

    2.applyBeanPostProcessorsBeforeInitialization()方法

    applyBeanPostProcessorsBeforeInitialization()

    此方法获取了所有的BeanPostProcessor对象。然后,循环调用了所有BeanPostProcessor的postProcessBeforeInitialization方法。这里我们就不说BeanPostProcessor是如何被设置到Spring里的了。我们看看官方是如何定义BeanPostProcessor的:Factory hook that allows for custom modification of new bean instances,也就是说你可以在这里实现初始化bean前的一些自定义。从此方法的签名,我们也可以看出来,此方法会在bean初始化前调用。什么!!!等等,难道这时候bean还没有被初始化???很奇怪是吧,我们先留下这个问题,稍后再讨论。

    3.invokeInitMethods()方法

    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()方法

    applyBeanPostProcessorsAfterInitialization()方法

    这个方法和applyBeanPostProcessorsBeforeInitialization()方法类似,就没什么好说的了。

    写在最后

    分析过程中,可选择的路径可能比较多,我只挑选了其中的一条分析路径。
    就写到这里吧,该下班了!晚安!

    相关文章

      网友评论

        本文标题:Spring Boot是如何调用到initializeBean(

        本文链接:https://www.haomeiwen.com/subject/ybhlbttx.html