Spring Boot是如何调用到initializeBean()的?

写在最前

《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()方法类似,就没什么好说的了。

写在最后

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容

  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,733评论 6 342
  • Spring容器高层视图 Spring 启动时读取应用程序提供的Bean配置信息,并在Spring容器中生成一份相...
    Theriseof阅读 2,795评论 1 24
  • 女儿,当本科的录取通知书送达我们家的时候,我和你妈妈的心情无比激动,因为你已成功地完成了爸妈的心愿,成为一名...
    东江之子阅读 1,421评论 6 8
  • 今天是马克读书训练营第四天,七月四号,发神经一般的天气,完美诠释了阴晴不定,大起大落。前一篇读书感我才说到作为红色...
    兰浥尘阅读 286评论 0 0
  • 十月间隙 那天下了一整天的雨 上山途中沿路而行,有山,有水,有溪流,有小瀑布,还有路边的野梨,树上的板栗,还有一排...
    我不爱起名字阅读 2,329评论 0 0