- preInstantiateSingletons初始化所有剩下的单实例bean
- getBean创建对象
- 拿到我们的bean的名字
- 根据我们bean的名字尝试获取缓存中保存的单实例bean
- getSingleton
- singletonObjects属性
- 缓存获取不到的后续处理
- 判断父子容器
- 标记markBeanAsCreated
- 获取我们bean的定义信息
- 获取我们当前bean所依赖的其他bean
- 判断我们的bean是不是单实例的
- createBean
- resolveBeforeInstantiation
- InstantiationAwareBeanPostProcessor
- doCreateBean==进入
- createBeanInstance
- synchronized(mbd.postProcessingLock)
- populateBean
- initializeBean
- 初始化完成之后获取我们单实例bean
- registerDisposableBeanIfNecessary注册bean的销毁方法
- doCreateBean==出来
- createBean出来到doGetBean
- 回到doGetBean
- doGetBean出来到this.getBean(beanName);调用处
按下F5快捷键进入finishBeanFactoryInitialization方法里面,如下图所示,可以看到一开始有跟类型转换组件有关的东东,这个嘛玩意我们也勿须深究,故略过。
继续按下F6快捷键让程序往下运行,这不是来判断beanFactory是否有值解析器的吗?这一块我们也没必要深究,也略过。
继续按下F6快捷键让程序往下运行,可以看到有跟AspectJ Weaver有关的东东,这些东东我们也没必要深究,也略过。
当程序运行至finishBeanFactoryInitialization方法的最后一行代码时,可以很清楚地从该行代码上的注释看出,这儿是来初始化所有剩下的单实例bean的。
preInstantiateSingletons初始化所有剩下的单实例bean
获取容器中所有的bean,然后依次进行初始化和创建对象
按下F5快捷键进入preInstantiateSingletons方法里面,如下图所示,可以看到一开始会先获取容器中所有bean的名字。当程序运行至如下这行代码处时,我们不妨Inspect一下beanNames变量的值,可以看到容器中现在有好多bean,有我们自己编写的组件,有Spring默认内置的一些组件。
对于容器中现在所有的这些bean来说,有些bean可能已经在之前的步骤中创建以及初始化完成了。因此,preInstantiateSingletons方法就是来初始化所有剩下的bean的。你能很明显地看到,这就有一个for循环,该for循环是来遍历容器中所有的bean,然后依次触发它们的整个初始化逻辑的。
获取bean的定义注册信息
进入for循环中之后,会获取到每一个遍历出来的bean的定义注册信息。我们要知道bean的定义注册信息是需要用RootBeanDefinition这种类型来进行封装的。
根据bean的定义注册信息判断bean是否是抽象的、单实例的、懒加载的
接下来,会根据bean的定义注册信息来判断bean是否是抽象的、单实例的、懒加载的。如果该bean既不是抽象的也不是懒加载的(我们之前就说过懒加载,它就是用到的时候再创建对象,与@Lazy注解有关),并且还是单实例的,那么这个时候程序就会进入到最外面的if判断语句中,即从第740行代码开始,到第763行代码结束,如下图所示。
接下来,我们就来好好分析一下该if判断语句里面的整个逻辑。
判断当前bean是不是FactoryBean的
factoryBean和beanFactory,factoryBean是给容器注册组件的另一种方式,参见第11讲的内容
按下F6快捷键让程序继续往下运行,你会发现还有一个判断,它是来判断当前bean是不是FactoryBean的,若是则进入到if判断语句中,若不是则进入到else分支语句中。
我们不妨点进isFactoryBean方法里面去看一看,如下图所示,可以很清楚地看到该方法就是来判断当前bean是不是属于FactoryBean接口的。
经过判断,如果我们的bean确实实现了FactoryBean接口,那么Spring就会调用FactoryBean接口里面的getObject方法来帮我们创建对象,查看FactoryBean接口的源码,你会发现它里面定义了一个getObject方法,这个我们之前是不是已经说过了😂。
好,那我们来看看第一个bean究竟是不是属于FactoryBean接口的
那么它是不是实现了FactoryBean接口呢?我们可以按下F6快捷键让程序继续往下运行,发现并没有,因为此时程序来到了下面的else分支语句中,如下图所示。
也就是说,如果我们的bean并没有实现FactoryBean接口,那么就会利用getBean方法来创建对象。
为了能够继续跟踪Spring源码的执行过程,我们可以在getBean方法处打上一个断点,如下图所示。
然后,我们就需要给程序不断地放行了,一直放行到我们自己编写的bean中,例如,我们之前在讲解Spring其他的扩展原理时,编写了一个如下的配置类。
从该配置类的代码中,我们可以看到还会向容器中注册一个我们自己编写的Blue组件。同样地,为了方便继续跟踪Spring源码的执行过程,我们也可以在下图所示的地方打上一个断点。
OK,打上以上两个断点之后,我们来按下F8快捷键让程序运行到下一个断点,可以看到现在是来创建第二个bean的对象。
继续不停地按下F8快捷键让程序运行到下一个断点,直至放行到Blue对象的创建为止,如下图所示,在这一过程中,我们可以依次看到每一个bean的创建。
程序运行至此,我们可以知道Blue对象是得通过getBean方法来创建的。于是,接下来,我们就来看看这个Blue对象它到底是怎么创建的。
其实,我们早已用过这个方法了,查阅之前我们自己编写的单元测试类,例如IOCTest_AOP,你就能看到我们确实是调用了IOC容器的getBean方法,如下图所示。
getBean创建对象
但是,现在我们要从源码的角度来分析该方法了,看看它里面都做了哪些事。
我们按下F5快捷键进入getBean方法里面去看一看,如下图所示,可以看到它里面又调用了一个叫doGetBean的方法。
拿到我们的bean的名字
继续按下F5快捷键进入doGetBean方法里面去看一看,如下图所示,可以看到一开始会拿到我们的bean的名字。
根据我们bean的名字尝试获取缓存中保存的单实例bean
然后,根据我们bean的名字尝试获取缓存中保存的单实例bean。你可以看到这儿调用的是getSingleton方法,而且从缓存中获取到了之后会赋值给一个叫sharedInstance的变量,它翻译过来就是共享的bean。
为什么这儿会先尝试从缓存中获取我们单实例bean呢?这是因为以前有一些单实例bean已经被创建好了,而且这些单实例bean也已经被缓存起来了,通俗一点说就是,所有创建过的单实例bean都会被缓存起来,所以这儿会调用getSingleton方法先从缓存中获取。如果能获取到,那么说明这个单实例bean之前已经被创建过了。
getSingleton
为了看得更加清楚,我们不妨按下F5快捷键进入getSingleton方法里面去看一看,如下图所示,发现它里面是下面这个样子的,好像是又调用了一个重载的getSingleton方法。
再继续按下F5快捷键进入以上getSingleton方法里面去看一看,如下图所示,可以看到从缓存中获取其实就是从singletonObjects属性里面来获取。
singletonObjects属性
我得说明一下,singletonObjects是DefaultSingletonBeanRegistry类里面的一个属性,点它,你就能看到了,如下图所示。
可以看到singletonObjects属性就是一个Map集合,该Map集合里面缓存的就是所有的单实例bean,而且还是按照bean的名字和其实例对象缓存起来的,这可以从该属性上面的注释中看出来。
还是回到getSingleton方法处,Inspect一下singletonObjects属性的值,发现不仅能看到一些已经创建好的bean,而且还能看到一些其他的属性信息以及环境变量等等。
我们按下F6快捷键让程序继续往下运行,运行一步即可,此时Inspect一下singletonObject变量的值,发现是null,如下图所示,这说明名字为blue的bean从缓存中是获取不到的。
接着,我们继续按下F6快捷键让程序往下运行,直至运行到下面这行代码处,这儿有一个判断,好像是来判断我们的bean是否是原型创建,这是嘛玩意啊,不懂😭
继续按下F6快捷键让程序往下运行,此时程序并没有进入到以上if判断语句中,而是来到了下面这行代码处。
程序运行至这里,我先做一个小结吧!如果从缓存中获取不到我们的bean,那么我们自然就得来创建了,走的便是下面else分支语句里面的逻辑。
缓存获取不到的后续处理
判断父子容器
好了,现在是该开始创建我们bean的对象了,那么这个创建对象的流程又是怎样的呢?从上图中可以看到,首先会来获取一个(父)BeanFactory,因为我们后来也是用它来创建对象的。然后,立马会有一个判断,即判断是不是能获取到(父)BeanFactory。这儿为什么会强调要获取 (父) BeanFactory呢?我想这是因为跟Spring MVC与Spring的整合有关,它俩整合起来以后,就会有父子容器了。嗯,我想就是这样的😊
按下F6快捷键让程序继续往下运行,我们会发现程序并没有进入到if判断语句中,而是来到了下面这行代码处,这说明并没有获取到(父)BeanFactory。
标记markBeanAsCreated
可以看到这儿又有一个判断,而且程序能够进入到该if判断语句中,如下图所示。
那么,markBeanAsCreated方法主要是来做什么的呢?它是在我们的bean被创建之前,先来标记其为已创建,相当于做了一个小标记,这主要是为了防止多个线程同时来创建同一个bean,从而保证了bean的单实例特性。这样看起来,Spring的源码写的还是蛮严谨的。
获取我们bean的定义信息
按下F6快捷键让程序继续往下运行,当程序运行至下面这行代码处时,可以看到这是来获取我们bean的定义信息的。
获取我们当前bean所依赖的其他bean
我们继续按下F6快捷键让程序往下运行,直至运行到下面这行代码处。
可以看到这儿调用了bean定义信息对象的一个getDependsOn方法,它是来获取我们当前bean所依赖的其他bean的。
还记得我们之前在编写Spring的XML配置文件时,使用
<bean id="person" class="com.meimeixia.bean.Person">
<property name="age" value="18"></property>
<property name="name" value="liayun"></property>
<property name="nickName" value="${person.nickName}"></property>
</bean>
其实,我们还可以在
<bean id="person" class="com.meimeixia.bean.Person" depends-on="book,user">
<property name="age" value="18"></property>
<property name="name" value="liayun"></property>
<property name="nickName" value="${person.nickName}"></property>
</bean>
添加上depends-on=”book,user”这样一个属性之后,那么在创建名字为person的bean之前,得先把名字为book和user的bean给创建出来。也就是说,depends-on属性决定了bean的创建顺序。
回到主题,可以看到,depends-on属性也在Spring的源码中得到了体现,这可以参考上图。我们可以看到,会先获取我们当前bean所依赖的其他bean,如果我们要创建的bean确实有依赖其他bean的话,那么还是会调用getBean方法把所依赖的bean都创建出来,用于规定创建顺序
你有没有发现我们一直在研究这个getBean方法啊?研究到这里,我们又会发现使用它来创建我们的bean之前,它做的一件大事,就是把我们要创建的bean所依赖的bean先创建出来,当然了,前提是我们要创建的bean是确实是真的有依赖其他bean。
判断我们的bean是不是单实例的
我们继续按下F6快捷键让程序往下运行,会发现程序并没有进入到if判断语句中,而是来到了下面这行代码处。
在这会做一个判断,即判断我们的bean是不是单实例的,由于我们的bean就是单实例的,所以程序会进入到if判断语句中,来启动单实例bean的创建流程。
那么是怎么来启动我们单实例bean的创建流程的呢?我们可以看到,现在是调用了一个叫getSingleton的方法,而且在调用该方法时,还传入了两个参数,第一个参数是咱们单实例bean的名字,第二个参数是ObjectFactory(是不是可以叫它Object工厂呢?)对象。
哎,你有没有看到ObjectFactory对象里面还有一个getObject方法呢?其实,Spring就是利用它来创建我们单实例bean的对象的。哎,它里面怎么还调用了一个createBean方法呢,莫非该方法就是来帮我们真正开始创建bean对象的?确实是这样的哟😊
createBean
所以,为了方便继续跟踪Spring源码的执行过程,我们不妨在createBean方法处打上一个断点,如下图所示。
于是,我们按下F8快捷键让程序直接运行到下一个断点,此时程序来到了createBean方法处,现在要调用该方法来帮我们创建bean的对象了哟~
为了搞清楚单实例bean的创建流程,我们不妨按下F5快捷键进入到createBean方法里面去看一看,如下图所示。
当程序往下运行时,可以看到会先拿到我们bean的定义信息,然后再来解析我们要创建的bean的类型。
继续让程序往下运行,直至运行到下面这行代码处为止。
resolveBeforeInstantiation
InstantiationAwareBeanPostProcessor
可以看到,在创建我们bean的对象之前,会调用了一个resolveBeforeInstantiation方法。那该方法是干嘛的呢?看该方法上的注释,它说是给BeanPostProcessor一个机会来提前返回我们bean的代理对象,这主要是为了解决依赖注入问题。也就是说,这是让BeanPostProcessor先拦截并返回代理对象。
但是,这究竟是哪个BeanPostProcessor在工作呢?我们之前就说过,BeanPostProcessor是有非常多的,一般而言,BeanPostProcessor都是在创建完bean对象初始化前后进行拦截的。而现在我们还没创建对象呢,因为我们是调用createBean方法来创建对象的,还记得吗?这也就是说,我们bean的对象还未创建之前,就已经有了一个BeanPostProcessor,那么这个BeanPostProcessor究竟是谁呢?
我们不妨按下F5快捷键进入resolveBeforeInstantiation方法里面去看一看,如下图所示,当程序运行到下面这行代码处时,我们才知道原来是InstantiationAwareBeanPostProcessor这种类型的BeanPostProcessor。
可以看到这儿是来判断是否有InstantiationAwareBeanPostProcessor这种类型的后置处理器的。如果有,那么就会来执行InstantiationAwareBeanPostProcessor这种类型的后置处理器。那么,其执行逻辑又是怎么样的呢?
哎,看到那个applyBeanPostProcessorsBeforeInstantiation方法了没有,我们直接点进去看下,如下图所示。
你是不是看到了这样的逻辑?在该方法中,会先判断遍历出的每一个BeanPostProcessor是不是InstantiationAwareBeanPostProcessor这种类型的,如果是,那么便来触发其postProcessBeforeInstantiation方法,该方法定义在InstantiationAwareBeanPostProcessor接口中。
如果applyBeanPostProcessorsBeforeInstantiation方法执行完之后返回了一个对象,并且还不为null,那么紧接着就会来执行后面的applyBeanPostProcessorsAfterInitialization方法。
我们不妨直接点进applyBeanPostProcessorsAfterInitialization方法里面去看一下,如下图所示。
可以看到它里面是来执行每一个BeanPostProcessor的postProcessAfterInitialization方法的。注意,postProcessAfterInitialization方法是定义在BeanPostProcessor接口中的,只不过是InstantiationAwareBeanPostProcessor接口继承过来了而已。
也就是说,如果有InstantiationAwareBeanPostProcessor这种类型的后置处理器,那么会先执行其postProcessBeforeInstantiation方法,并看该方法有没有返回值(即创建的代理对象),若有则再执行其postProcessAfterInitialization方法。现在,你该知道InstantiationAwareBeanPostProcessor这种类型的后置处理器中两个方法的执行时机了吧😊!
我们按下F6快捷键让程序继续往下运行,直至运行到下面这行代码处,看来我们确实是有InstantiationAwareBeanPostProcessor这种类型的后置处理器。
然后,按下F5快捷键进入applyBeanPostProcessorsBeforeInstantiation方法里面去瞧一瞧,如下图所示,可以看到它里面会遍历获取到的所有的BeanPostProcessor,接着再来判断遍历出的每一个BeanPostProcessor是不是InstantiationAwareBeanPostProcessor这种类型的。很明显,遍历出的第一个BeanPostProcessor并不是InstantiationAwareBeanPostProcessor这种类型的,所以程序并没有进入到最外面的if判断语句中。
继续让程序往下运行,发现这时遍历出的第二个BeanPostProcessor是ConfigurationClassPostProcessor,而且它还是InstantiationAwareBeanPostProcessor这种类型的,于是,程序自然就进入到了最外面的if判断语句中,如下图所示。
这里我稍微提一嘴,ConfigurationClassPostProcessor这种后置处理器是来解析标准了@Configuration注解的配置类的。
紧接着便会来执行ConfigurationClassPostProcessor这种后置处理器的postProcessBeforeInstantiation方法了,但是该方法的返回值为null。于是,我们继续让程序往下运行,直至遍历完所有的BeanPostProcessor,返回到下面这行代码处。
此时,applyBeanPostProcessorsBeforeInstantiation方法便执行完了,我们也知道它里面做了些啥,只不过它并没有返回创建的代理对象,因此,程序继续往下运行,并不会进入到下面的if判断语句中,而是来到了下面这行代码处。
好了,我们单实例bean的创建流程就分析到这里算了,还没有分析完哟😥,下一讲我们接着再接再厉地分析完,敬请期待哟~~~
于是,我们继续按下F6快捷键让程序往下运行,直至运行到下面这行代码处为止。
这时,resolveBeforeInstantiation方法总算是执行完了。你还记得该方法是干嘛的吗?它是在创建我们单实例bean之前,先来给BeanPostProcessor一个返回其代理对象的机会。但是,此刻是没有返回我们单实例bean的代理对象的,不信你看。
如果InstantiationAwareBeanPostProcessor这种类型的后置处理器并没有返回我们bean的代理对象,那么接下来该怎么办呢?
doCreateBean==进入
那我们只好继续按下F6快捷键让程序往下运行了,继续执行下面的流程,当程序运行到下面这行代码处时,发现调用了一个叫doCreateBean的方法,顾名思义,该方法就是来创建我们bean的实例的。
那么这个创建bean的流程又是怎样的呢?下面我就来为大家揭晓答案。
按下F5快捷键进入doCreateBean方法里面去看一下,如下图所示,可以看到会用BeanWrapper接口来接收我们创建的bean。
继续按下F6快捷键让程序往下运行,直至运行到下面这行代码处为止,可以看到这儿调用的是一个叫createBeanInstance的方法,顾名思义,它是来创建bean实例的。
也就是说,创建bean的流程的第一步就是先来创建bean实例。
createBeanInstance
当执行完createBeanInstance方法之后,我们bean的对象就创建出来了。那么,我们bean实例的创建流程又是怎样的呢?我们不妨按下F5快捷键进入createBeanInstance方法里面去看一下,如下图所示,可以看到一开始就要来解析一下我们要创建的bean的类型。
于是,我们继续按下F6快捷键让程序往下运行,由于解析出来的类型为null,所以程序并不会进入到下面的if判断语句中,而是来到了下面这行代码处。
这块是来干嘛呢?我们可以来详细说一下。首先,在if判断语句中的条件表达式中,你可以看到调用了bean定义信息对象的一个getFactoryMethodName方法,该方法是来获取工厂方法的名字的。我们不妨Inspect一下mbd.getFactoryMethodName()表达式的值,发现其值就是blue,如下图所示。
为什么叫工厂方法呢?还记得咱们自己编写的Blue对象是如何注册到IOC容器中的吗?如下图所示,我们是使用标注了@Bean注解的blue方法来创建Blue对象并将其注册到IOC容器中的。
也就是说,以上blue方法就相当于Blue对象的工厂方法。
还是回到Spring的源码中来,现在程序是停留在了if判断语句块内,不难猜测此时就是来执行Blue对象的工厂方法(即blue方法)来创建Blue对象的。我们不妨按下F5快捷键进入instantiateUsingFactoryMethod方法里面去看一下,如下图所示,这块好像是来拿什么构造器解析的,咱也不太懂,哈哈哈😡
我们就直接按下F6快捷键继续让程序往下运行,运行一步,发现程序来到了ExtConfig配置类的blue方法中,如下图所示。
继续让程序往下运行,这时可以从Eclipse控制台中看到打印了如下内容,即调用了Blue类的无参构造器创建出了Blue对象。
是不是可以这样说呢?这儿就是利用工厂方法或对象的构造器创建出bean实例呢?当我们这个bean实例(也即Blue对象)创建出来以后,继续按下F6快捷键让程序往下运行,直至运行到下面这行代码处为止。
让程序继续往下运行,这时你得不停地按F6快捷键,这一过程中好像是利用反射来创建对象,咱也不用看,直接放过(再说了,你也看不过来😥),直至运行到下面这行代码处为止。
程序运行至此,咱们这个bean实例(即Blue对象)就创建出来了,只不过该Blue对象刚刚创建出来,空空如也,什么都没有。
接着,让程序继续往下运行,直至再次运行回doCreateBean方法中,如下图所示。
这时,以上createBeanInstance方法就算是执行完了,也就是说,创建出了我们的bean实例(即Blue对象)。
synchronized(mbd.postProcessingLock)
最后,让程序继续往下运行,直至运行到下面这行代码处为止,从这行代码上面的注释中,我们可以看到这块允许后置处理器来修改咱们这个bean的定义信息。
很明显,我们的bean实例创建完了以后,接下来就得来调用这个applyMergedBeanDefinitionPostProcessors方法了。
那么,该方法又是来执行哪些后置处理器的呢?我们直接点击该方法进去它里面看一下,如下图所示,可以看到先是来获取到所有的后置处理器,然后再来遍历它们,如果是MergedBeanDefinitionPostProcessor这种类型的,那么就调用其postProcessMergedBeanDefinition方法。
从这儿也能看到,每一个后置处理器(或者说它里面的方法)的执行时机都是不一样的,比如我们在上一讲中所讲述的InstantiationAwareBeanPostProcessor这种类型的后置处理器中的两个方法的执行时机是在创建bean实例之前,而现在MergedBeanDefinitionPostProcessor这种类型的后置处理器,是在创建完bean实例以后,来执行它里面的postProcessMergedBeanDefinition方法的。
所以说,如果大家要用到各种类型的后置处理器,那么你得清楚知道它们里面的方法是何时执行的,可一定要做到心中有数哟😁
接着,让程序继续往下运行,直至运行到下面这行代码处为止。
尼玛的,这儿又是来干嘛的啊😡?咱也不知道,咱也不敢问!不过从earlySingletonExposure变量的名字中,我们是不是能猜到这儿是来拿到什么单实例的要暴露的bean的呢?😔,我也不知道,只是猜测。
程序继续往下运行的过程中,我们发现其会进入到if判断语句中,来到addSingletonFactory方法处,如下图所示,调用该方法好像是来添加一些缓存的,咱也不必深究,直接略过。
populateBean
于是,我们让程序继续往下运行,直至运行到下面这行代码处为止,很明显,populateBean方法是来为bean的属性赋值的。
也就是说,创建完bean实例以后,首先就是来为bean实例的属性赋值。
你可不要忘了程序现在依然还在doCreateBean方法内运行哟!在该方法内,首先会创建出我们的bean实例,然后再执行MergedBeanDefinitionPostProcessor这种类型的后置处理器,接着,创建完bean实例之后就得为其属性赋值了。
你有没有想过是如何为bean的属性赋值这一问题的呢?不急,我们不妨按下F5快捷键进入populateBean方法里面去看一下,如下图所示,可以看到一开始会拿到赋给所有属性的属性值。
只不过,我们现在没啥要赋的属性值,不信你看,但是如果有的话,那么就会拿到很多。
于是,我们让程序继续往下运行,直至运行到下面这行代码处为止,此时,大家可要注意了,现在依然还没有为bean的属性赋值哟!
可以看到,接下来会遍历获取到的所有后置处理器,如果是InstantiationAwareBeanPostProcessor这种类型的,那么就调用其postProcessAfterInstantiation方法。
而且,以上所有的这些操作均是在为bean的属性赋值之前做的哟~~~
我们继续按下F6快捷键让程序往下运行,当程序运行到下面这行代码处时,好像是来拿到ResolvedAutowireMode这个东东的,咱也不知道这玩意是个嘛,咱也不敢问😄!只是在这记录一下。
继续让程序往下运行,你会发现程序并没有进入到if判断语句中,而是来到了下面这行代码处。
可以看到这儿调用了一个hasInstantiationAwareBeanPostProcessors方法,它是来判断是否有InstantiationAwareBeanPostProcessor这种类型的后置处理器的。
继续让程序往下运行,你会发现程序进入到了下面的if判断语句中,来到了下面这行代码处,这说明是有InstantiationAwareBeanPostProcessor这种类型的后置处理器的。
接着,继续让程序往下运行,很显然程序会再进入到下面的if判断语句中,因为确实是有InstantiationAwareBeanPostProcessor这种类型的后置处理器。
现在,你该对接下来所要做的事情了然于胸了吧!其实,就是遍历获取到的所有后置处理器,如果是InstantiationAwareBeanPostProcessor这种类型的,那么就调用其postProcessPropertyValues方法。
大家可要注意哟,即使是执行了InstantiationAwareBeanPostProcessor这种类型的后置处理器中的postProcessAfterInstantiation和postProcessPropertyValues这俩方法,咱们bean实例的属性依然还没有被赋值。那么,到底是啥时候来赋值的呢?
我们不妨让程序继续往下运行,直至遍历完所有的后置处理器。在这一过程中,我再说一遍啊,唉,都说的口都快干了,如果遍历出的后置处理器是InstantiationAwareBeanPostProcessor这种类型,那么就会调用其postProcessPropertyValues方法。
唉,对了,我们可以点进去InstantiationAwareBeanPostProcessor接口里面看一看它的源码哟,如下图所示,你可以看到它里面定义了一个postProcessPropertyValues方法,该方法会返回一个PropertyValues对象,它就是我们bean实例属性要被赋予的属性值,最终这些属性值会被赋值给bean的属性。我是这样理解的啦😂,也不知道对不对。
好像更加专业的说法是这样的,Spring获取bean的实例时,需要把配置的属性值解析到PropertyValues中,然后再填充入BeanWrapper。我在这里先记录一下。
当程序运行到下面这行代码处时,你会发现这儿调用了一个applyPropertyValues方法,这里才是正式开始为bean的属性赋值。
大家一定要注意啊,现在调用applyPropertyValues方法才是开始为bean的属性赋值,在为bean的属性赋值之前,我们知道会执行InstantiationAwareBeanPostProcessor这种类型的后置处理器中的postProcessAfterInstantiation和postProcessPropertyValues这俩方法。
其实,为bean的属性赋值,说到底就是利用setter方法为bean的属性进行赋值。这里,我们就不再进入applyPropertyValues方法去一探究竟了,它里面无非就是利用反射调setter方法之类的。
接下来,继续让程序往下运行,直至运行到下面这行代码处为止,此时,populateBean方法就执行完了,也就是说,已经为我们bean的属性赋完值了。
接着继续让程序往下运行,运行一步即可,这时程序来到了下面这行代码处。
可以看到,这儿调用了一个initializeBean方法,你是不是很熟悉它,因为我们之前就已经研究过它了,它就是来初始化bean的,下面我们再来详详细细地研究一下它。
至此,我们就知道了这样一个结论,为bean的属性赋完值之后,接着便要来初始化bean了。
initializeBean
初始化bean
至此,我们要不先梳理一下吧😊,你看我总结的可还行?
1)在创建bean实例之前,会执行InstantiationAwareBeanPostProcessor这种类型的后置处理器中的两个方法,即postProcessBeforeInstantiation方法和postProcessAfterInstantiation方法
2)创建bean实例
3)为bean实例的属性赋值。在赋值的过程中,会依次执行InstantiationAwareBeanPostProcessor这种类型的后置处理器中的两个方法,即postProcessAfterInstantiation方法和postProcessPropertyValues方法
4)初始化bean
那么,你知不知道是如何来初始化bean的呢?我想你肯定不知道,不知道没关系,我们可以按下F5快捷键进入initializeBean方法中去一探究竟,如下图所示,进来之后,我们不妨让程序运行到下面这行代码处。
可以看到,这儿调用了一个invokeAwareMethods方法,顾名思义,它是来执行XxxAware接口中的方法的。
执行XxxAware接口中的方法
对于XxxAware接口,不知你还有没有一些印象,我们以前就用过,比如我们之前曾经编写过这样一个Dog组件,如下所示,在该组件里面如果想要用IOC容器,那么就得让其实现ApplicationContextAware接口,这样,Spring就会在setApplicationContext方法中把IOC容器给传过来。
package com.meimeixia.bean;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* ApplicationContextAwareProcessor这个类的作用是可以帮我们在组件里面注入IOC容器,
* 怎么注入呢?我们想要IOC容器的话,比如我们这个Dog组件,只需要实现ApplicationContextAware接口就行
*
* @author liayun
*
*/
@Component
public class Dog implements ApplicationContextAware {
private ApplicationContext applicationContext;
public Dog() {
System.out.println("dog constructor...");
}
// 在对象创建完成并且属性赋值完成之后调用
@PostConstruct
public void init() { // 在这儿打个断点调试一下
System.out.println("dog...@PostConstruct...");
}
// 在容器销毁(移除)对象之前调用
@PreDestroy
public void destory() {
System.out.println("dog...@PreDestroy...");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { // 在这儿打个断点调试一下
// TODO Auto-generated method stub
this.applicationContext = applicationContext;
}
}
那么,在invokeAwareMethods方法中是怎么来执行XxxAware接口的方法的呢?我们不妨按下F5快捷键进入该方法里面去看一下,如下图所示。
可以看到,它就是来判断我们的bean是不是实现了BeanNameAware、BeanClassLoaderAware、BeanFactoryAware这些Aware接口的,若是则回调接口中对应的方法。
当然了,现在我们的bean(即Blue对象)是没有实现以上这些Aware接口的,所以,我们直接让程序继续往下运行,直至运行到下面这行代码处为止。
执行postProcessBeforeInitialization
执行完XxxAware接口中的方法之后,可以看到会再来调用一个applyBeanPostProcessorsBeforeInitialization方法,该方法我们之前也研究过,不是吗?
那么,你知道applyBeanPostProcessorsBeforeInitialization方法中具体执行了哪些逻辑吗?我们不妨按下F5快捷键进入该方法里面去看一下,如下图所示。
可以看到,在applyBeanPostProcessorsBeforeInitialization方法中,会遍历所有的后置处理器,然后依次执行所有后置处理器的postProcessBeforeInitialization方法,一旦后置处理器的postProcessBeforeInitialization方法返回了null以后,则后面的后置处理器便不再执行了,而是直接退出for循环。
然后,我们让程序继续往下运行,一直运行到下面这行代码处为止。
现在,你该知道后置处理器初始化之前的方法(即postProcessBeforeInitialization方法)的调用时机了吧😁
执行初始化方法
执行完后置处理器的postProcessBeforeInitialization方法之后,可以看到现在又调用了一个invokeInitMethods方法,其作用就是执行初始化方法。
哎呀,初始化方法究竟是指哪些方法呢?我们不妨来回顾一下,是不是包括了一个实现InitializingBean接口的方法啊?还记得我们之前曾经编写过如下所示这样一个Cat组件吗?
package com.meimeixia.bean;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
// @Scope("prototype")
@Component
public class Cat implements InitializingBean, DisposableBean {
public Cat() {
System.out.println("cat constructor...");
}
/**
* 会在容器关闭的时候进行调用
*/
@Override
public void destroy() throws Exception {
// TODO Auto-generated method stub
System.out.println("cat destroy...");
}
/**
* 会在bean创建完成,并且属性都赋好值以后进行调用
*/
@Override
public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub
System.out.println("cat afterPropertiesSet...");
}
}
可以看到,以上Cat组件实现了一个InitializingBean接口,而该接口中定义了一个afterPropertiesSet方法,必然在Cat组件内就会实现该方法,这样,该方法就是Cat组件的初始化方法了。
当然了,我们除了通过以上方式来指定初始化方法之外,还可以在@Bean注解中使用initMehod属性来指定初始化方法,就像下面这样。
package com.meimeixia.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import com.meimeixia.bean.Car;
/**
*
* @author liayun
*
*/
@ComponentScan("com.meimeixia.bean")
@Configuration
public class MainConfigOfLifeCycle {
// @Scope("prototype")
@Bean(initMethod="init", destroyMethod="destroy")
public Car car() {
return new Car();
}
}
从以上配置类的代码中可以看出,我们指定了Car对象中的init方法为初始化方法,destroy方法为销毁方法,而Car类的代码如下所示。
package com.meimeixia.bean;
import org.springframework.stereotype.Component;
@Component
public class Car {
public Car() {
System.out.println("car constructor...");
}
public void init() {
System.out.println("car ... init...");
}
public void destroy() {
System.out.println("car ... destroy...");
}
}
好,回顾完了,我们现在将记忆拉到现实,来看一下究竟是如何来执行初始化方法的。于是,我们按下F5快捷键进入invokeInitMethods方法里面去看一下,如下图所示。
可以看到,一开始就会来判断我们的bean是否是InitializingBean接口的实现,若是则执行该接口中定义的初始化方法。
如果不是的话呢?我们不妨继续让程序往下运行,可以发现程序并没有进入到下面的if判断语句中,而是来到了下面这行代码处,这是因为我们的bean并没有实现InitializingBean接口。
这时,是来看我们的bean是否自定义了初始化方法,如果是的话,那么就来执行初始化方法。
但是,现在我们的bean是没有自定义初始化方法的,因此在程序继续往下运行的过程中,程序并不会进入到下面的if判断语句中,而是来到了下面这行代码处。
此时,invokeInitMethods方法便执行完了。这其实就是说,Spring会帮我们把我们bean中的初始化方法回调一下。
执行postProcessAfterInitialization
初始化方法执行完了以后,下一步就是来调用applyBeanPostProcessorsAfterInitialization方法,难道不是这样吗?
那么,你知道applyBeanPostProcessorsAfterInitialization方法中具体执行了哪些逻辑吗?我们不妨按下F5快捷键进入该方法里面去看一下,如下图所示。
可以看到,在applyBeanPostProcessorsAfterInitialization方法中,会遍历所有的后置处理器,然后依次执行所有后置处理器的postProcessAfterInitialization方法,一旦后置处理器的postProcessAfterInitialization方法返回了null以后,则后面的后置处理器便不再执行了,而是直接退出for循环。
然后,我们让程序继续往下运行,一直运行到下面这行代码处为止,可以看到我们的bean已经初始化完了。
以上就是我们bean的整个初始化逻辑。
初始化完成之后获取我们单实例bean
我们的bean初始化完了以后,继续让程序往下运行,直至运行到下面这行代码处为止,很显然,这儿是来获取我们单实例bean的,因为我们单实例bean都已经创建好了。
不错,这儿确实是来获取我们单实例bean的,只不过是先从缓存中来获取,但缓存中还没有我们bean呢。妈的,老子Inspect了一下bean变量的值,如下图所示,发现我们的bean确实是已经创建好了。
好吧,从缓存中获取不到就获取不到吧,我们就让程序继续往下运行,很显然程序是不会进入到下面的if判断语句中的,而是来到了这行代码处。
可以看到这儿调用了一个registerDisposableBeanIfNecessary方法,那么该方法又是来干嘛的呢?
registerDisposableBeanIfNecessary注册bean的销毁方法
现在程序是不是又回到了doCreateBean方法中了呢?确实是哟!那么,registerDisposableBeanIfNecessary方法是来干嘛的呢?它是来注册我们bean的销毁方法的。注意,这里只是注册而不是调用哟😊
我们知道,销毁方法是在IOC容器关闭以后才被调用的。还记得,我们之前是如何指定销毁方法的吗?聪明的同学一定知道,因为上面的那个Cat组件就实现了一个DisposableBean接口,因此该组件里面的destroy方法就是销毁方法,它会在容器关闭的时候进行调用。
我们不妨按下F5快捷键进入registerDisposableBeanIfNecessary方法里面去看一下,如下图所示,看一下而已,没必要深究,因为这一块呢,只是提前把bean的销毁方法注册了一下。
doCreateBean==出来
然后,继续让程序往下运行,直至运行到下面这行代码处为止,此时,doCreateBean方法就算是返回了一个bean实例。
我们经过以上这么多的步骤,终于终于将bean实例创建出来了,是不是很不容易啊!你可能要问了,经过了哪些步骤啊?能不能给总结一下呢?其实,这些步骤都包含在以上doCreateBean方法中,我不妨总结一下。
- 1)在创建bean实例之前,会执行InstantiationAwareBeanPostProcessor这种类型的后置处理器中的两个方法,即postProcessBeforeInstantiation方法和postProcessBeforeInstantiation方法
- 2)创建bean实例
- 3)为bean实例的属性赋值。在赋值的过程中,会依次执行InstantiationAwareBeanPostProcessor这种类型的后置处理器中的两个方法,即postProcessAfterInstantiation方法和postProcessPropertyValues方法
- 4)初始化bean,而且在初始化前后会执行后置处理器中的两个方法,即postProcessBeforeInitialization方法和postProcessAfterInitialization方法
- 5)注册bean的销毁方法
createBean出来到doGetBean
if (mbd.isSingleton()) {
sharedInstance = this.getSingleton(beanName, () -> {
try {
return this.createBean(beanName, mbd, args);
} catch (BeansException var5) {
this.destroySingleton(beanName);
throw var5;
}
});
bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
这时,createBean方法就算是彻底执行完了,而且它会返回创建好的bean实例哟~
回到doGetBean
AbstractBeanFactory :
if (mbd.isSingleton()) {
sharedInstance = this.getSingleton(beanName, () -> {
try {
return this.createBean(beanName, mbd, args);
} catch (BeansException var5) {
this.destroySingleton(beanName);
throw var5;
}
});
bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
lambda表达式进去this.getSingleton看看发现是可以获取到创建出的bean实例了。
因为:
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory ;
public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanRegistry { … }
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
synchronized(this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
if (this.singletonsCurrentlyInDestruction) {
throw new BeanCreationNotAllowedException(beanName, "Singleton bean creation not allowed while singletons of this factory are in destruction (Do not request a bean from a BeanFactory in a destroy method implementation!)");
}
if (this.logger.isDebugEnabled()) {
this.logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
}
this.beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = this.suppressedExceptions == null;
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet();
}
try {
singletonObject = singletonFactory.getObject();
newSingleton = true;
} catch (IllegalStateException var16) {
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
throw var16;
}
} catch (BeanCreationException var17) {
BeanCreationException ex = var17;
if (recordSuppressedExceptions) {
Iterator var8 = this.suppressedExceptions.iterator();
while(var8.hasNext()) {
Exception suppressedException = (Exception)var8.next();
ex.addRelatedCause(suppressedException);
}
}
throw ex;
} finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
this.afterSingletonCreation(beanName);
}
if (newSingleton) {
this.addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
DefaultSingletonBeanRegistry
可以从singletonFactory当中取出bean实例
然后,再运行到下面这行代码处,这时,就能获取到创建出的bean实例了。
真的是这样吗?我们不妨Inspect一下singletonObject变量的值,如下图所示,发现我们的bean实例(即Blue对象)确实是被获取到了。
接着,让程序运行到下面这行代码处,可以看到,这儿会调用一个addSingleton方法,那么,该方法又干了些啥呢?
将创建出的单实例bean添加到缓存中
当我们的单实例bean创建出来之后,接下来就得调用一个addSingleton方法了,该方法的作用就是将创建的单实例bean添加到缓存中。
现在,你是不是能得出这样一个结论了,只要单实例bean创建出来了,那么就会将其添加到缓存中。
我们可以按下F5快捷键进入addSingleton方法里面去看一下,如下图所示,你还记得singletonObjects吗?
笨蛋,上一讲我们就已讲解过了,singletonObjects是DefaultSingletonBeanRegistry类里面的一个属性,点它,你就能看到了,如下图所示。
可以看到singletonObjects属性就是一个Map集合,该Map集合里面缓存的就是所有的单实例bean,而且还是按照bean的名字和其实例对象缓存起来的,这可以从该属性上面的注释中看出来。
哦,原来缓存就是一个Map集合啊😀,那么,将创建的单实例bean添加到缓存中指的不就是将创建的单实例bean添加到singletonObjects指代的Map集合中吗?第一次获取单实例bean,就是从singletonObjects里面来获取的,你还记得吗?
将创建的单实例bean添加到singletonObjects指代的Map集合中,好处是以后我们就可以直接从这个Map集合里面来获取了。
现在,你该知道什么叫IOC容器了吧!所谓的IOC容器就是指各种各样的Map集合,而且这些Map集合有很多哟😋,如下图所示,这些Map集合里面保存了我们创建的所有组件以及IOC容器的其他信息。
也就是说,以上所有这些Map集合就构成了Spring的IOC容器,容器里面保存了所有的组件。以后,我们从容器中来获取组件,其实就是从这些Map集合里面来获取组件。
好了,单实例bean的创建流程我们就算是完完整整地过了一遍了。
接下来,我们继续让程序往下运行,直至运行到下面这行代码处为止,至此,我们的单实例bean就获取到了。
if (mbd.isSingleton()) {
sharedInstance = this.getSingleton(beanName, () -> {
try {
return this.createBean(beanName, mbd, args);
} catch (BeansException var5) {
this.destroySingleton(beanName);
throw var5;
}
});
bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
然后,让程序往下运行到下面这行代码处,可以看到这儿将会返回我们获取到的单实例bean。
doGetBean出来到this.getBean(beanName);调用处
接着,让程序往下运行到下面这行代码处,可以看到getBean方法总算是执行完了,这时,也就获取到了我们的单实例bean。
此时,程序停留在了第764行代码处!