IOC流程解析-实例化和初始化
本次内容为解析核心方法剩下的全部内容,重点是bean 的实例化和初始化部分。对于其余IOC 部分内容感兴趣的小伙伴可以根据文章最后的导航栏直接跳跃。
源码分析
接着上篇文章(上篇文章是:[IOC流程解析-BeanFactoyPostProcessor和BeanPostProcessor](https://liwqtm.github.io/2022/03/14/Spring IOC-3/#more)),我们还是继续看IOC 的核心方法refresh 方法的内容。这次会提到两个面试常问的问题:BeanFactory 和FactoryBean 的区别、FactoryBean 和普通Bean 的区别,这个文章的最后我会细说一下,有需要面试的小伙伴可以直接跳到文章最后查看。
初始化消息源,国际化-initMessageSource方法
从上篇文章讲的registerBeanPostProcessors 方法后,这里就来到initMessageSource 方法。
initMessageSource 方法不是我们的重点,我们这里就简单描述一下其主要作用:初始化消息源,也就是国际化,目的就是为了多语言的切换,而initMessageSource 方法的执行结果就是向BeanFactory 中的单例池singletonObjects 集合添加了一个bean,一个完整的bean 对象messageSource 对象。
初始化事件广播器-initApplicationEventMulticaster方法
核心方法接着往下走,看到initApplicationEventMulticaster 方法,这个方法也同样简单,还是向BeanFactory 中的单例池singletonObjects 集合添加了一个完整的bean 对象,如果在没有配置事件广播器的情况下,默认创建的是SimpleApplicationEventMulticaster 对象。
Spring事件传播机制
这个还需要知道一个知识点:Spring事件传播机制。首先我们要知道Spring事件传播机制是参考监听者模式改造而来,正常的监听者模式为:被监听者和监听者,当被监听者发生改变时,会将改变推送给监听者。spring 在这个基础上加了一个事件广播器,由这个事件广播器来维护监听者和被监听者之间的关系,也是就将监听者放进集合列表中,然后和被监听者关系对应起来。
那么这里就有三个角色:
事件监听器(ApplicationListener)
事件发布者(ApplicationEventPublisher)因为ApplicationContext 实现了 ApplicationEventPublisher,所以事件发布可以直接使用ApplicationContext
事件广播器(ApplicationEventMulticaster),这里系统默认创建的是SimpleApplicationEventMulticaster
除此之外还有一个就是事件本身(ApplicationEvent),当事件发布者对事件广播器发布事件的时候,其实就是传输了一个ApplicationEvent 事件对象,然后由事件广播器再次传输给监听列表中的每一个事件监听器这个ApplicationEvent事件对象。
事件传播机制先说到这里。继续说核心代码。
拓展方法-onRefresh方法
onRefresh方法这个没有说的,这个就是一个空方法,可以用来自己实现一些业务逻辑的。
注册监听器-registerListeners方法
这里就是上面说的事件传播机制的监听器注册,还是向BeanFactory 中的单例池singletonObjects 集合添加了一个完整的bean 对象,同时也会获取到所有实现了ApplicationListener 接口的对象,然后添加的广播器维护的监听列表集合中,等待后续的调用。
实例化和初始化方法-finishBeanFactoryInitialization方法
终于到本次的重点了,走完了前面一系列的操作,然后到了IOC 流程图中的重点实例化和初始化bean,先回顾一下这部分的流程图。这也是IOC 流程的最后一步了。
我们跟进finishBeanFactoryInitialization 方法,其余的代码可以不看,这里都是设置一些加载器之类的,重点看这个方法的最后一句。
1 | // 5.实例化所有剩余(非懒加载)单例对象 重要===》 |
获取到BeanDefinition并调用getBean方法-preInstantiateSingletons方法
跟进preInstantiateSingletons 方法,首先获取的就是所有BeanDefinition 名称的集合,注意我们目前执行的代码就是在之前创建的BeanFactroy,所以这个名称集合是可以直接获取的。
1 | // 1.创建beanDefinitionNames的副本beanNames用于后续的遍历,以允许init等方法注册新的bean定义。 |
然后遍历这个集合,用名字获取到具体的对象。
1 | // 2.遍历beanNames,触发所有非懒加载单例bean的初始化 |
接下就要判断是否要将对象交给spring 管理,也就是要不要将对象添加的BeanFcatory 的单例池属性中,其依据有三个:BeanDefinition 对应的Bean实例是否是抽象类、是否是单例、是否懒加载,只要其中一个条件不满足,那么当前就不会注册spring 单例池,也就说必须是非抽象类、非懒加载的单例对象spring 在启动的时候才会去注册。
1 | // 4.bd对应的Bean实例:不是抽象类 && 是单例 && 不是懒加载 |
接下来就是将这些非抽象类、非懒加载的单例对象再次分类、判断该对象是否实现了FactoryBean 接口,FactoryBean这里会单独处理一下对象名称,然后再注册其对象,注意这里不是注册FactoryBean 的getObjeck 方法返回的对象。
FactoryBean 接口注册和普通注册的方法都是同一个,主要就是beanName 做了处理,所以我们重点看getBean 方法就行。
1 | // 5.判断beanName对应的bean是否为FactoryBean |
getBean->doGetBean->createBean->doCreateBean
spring 关于getBean 方法往后的调用都比较复杂,但是只跟着主流程,也就是getBean->doGetBean->createBean->doCreateBean 这一段方法逻辑走,一定不会跟丢,我们这里就简单的介绍一下前三个方法,正在的构建bean 和注册单例池还是在doCreateBean 方法里面。
首先是getBean 方法的调用,这里还是挺简单的,就是直接调用doGetBean 方法。
1 |
|
然后是doGetBean 方法,这里代码就比较多了,我们要知道的是在这个方法里面大体做了些什么事情就行了。其主要目的就是调用createBean 方法。
- 判断当前对象是否已经注册到spring 的单例池中了,是则直接返回,这也是为什么单例池也叫做一级缓存的原因。
- 解决循环依赖,这里我们后面说循环依赖的时候再回头来看。
- 然后判断如果存在副容器的话,还要再去副容器中判断获取是否存在当前对象实例。
- 最后获取到具体的BeanDefinition 对象,然后判断该对象是否是单例,因为这里单例是一段逻辑,多例又是另一端段逻辑,不过最后都是调用createBean 方法。
接着是createBean 方法,这里的内容比较简单,最主要的就是调用doCreateBean 方法,不是我敷衍啊,真是我能力有限,我们还是看最主要的方法就行了。
真正获取完整对象的内容-doCreateBean方法
终于来到了本次的重中之重,这里也是IOC 的核心,理清楚这个流程基本上IOC 的面试就不是问题了。老规矩还是看重点,一点一点的分析。
实例化
首先就是就是bean 的实例化,这里调用的是createBeanInstance 方法。这里个方法里面的代码没有什么可以深究的,就是通过反射获取到对应的实例对象,注意第一重点:这里实例出的对象是没有任何属性值的,就只是反射实例了一个对象而已。
1 | if (instanceWrapper == null) { |
初始化
然后看第二个重点:bean 的初始化。这里是两段代码调用,首先就是属性的注入,然后是bean 的初始化,主要属性注入是属于初始化的流程中,不是实例化的,这里区分开是为了后面对于循环依赖的理解和解决。
1 | // 9.【属性注入】对bean进行属性填充;其中,可能存在依赖于其他bean的属性,则会递归初始化依赖的bean实例 |
属性注入
首先跟进populateBean 方法。
这里我们直接看重点,方法中的最后一句话。上面全部都是一些校验之类的,我们暂时不关注。
1 | // 真正的注入在这里!!将属性注入到bean实例当中,这才是真正的属性赋值的地方【关键点】 |
继续跟进applyPropertyValues 方法。
这里我们找到resolveValueIfNecessary 方法的调用,上面的内容都是获取当前对象需要赋值的属性和为了构建valueResolver 解析器对象,判断了一些是否是注解方式注入、set注入之类的,然后构建出具体的解析器对象,我们重点看resolveValueIfNecessary 方法。
1 | /* |
继续跟进resolveValueIfNecessary 方法。
到了这里就是判断当前需要注入的属性,属于哪个类型,然后再调用具体的赋值方法,这里就不重点看了,后面将循环依赖的时候,我们再回头来看其中对于对象注入的具体方法。
1 | // 7. 解析List |
激活所有Aware方法
然后跟进initializeBean 方法。
这里可以看到首先就是激活所有Aware 方法,这里就是判断当前对象是否实现了BeanNameAware、BeanClassLoaderAware、BeanFactoryAware 接口,然后将对应的对应的setBeanName、setBeanClassLoader、setBeanFactory 方法里面塞值。
1 | protected Object initializeBean(String beanName, Object bean, { RootBeanDefinition mbd) |
加载的所有Bean的后置处理器的Before方法
接下来就是调用我们之前加载的所有Bean 的后置处理器的Before 方法,这里我是上篇文章说过的IOC流程解析-BeanFactoyPostProcessor和BeanPostProcessor,这里的调用也很简单,就说在BeanFactory 中获取BeanPostProcessor 集合,然后迭代调用即可。
1 | Object wrappedBean = bean; |
1 |
|
执行InitializingBean接口初始化方法和自定义初始化方法
继续就到了invokeInitMethods 初始化方法,这一步就是判断bean 配置信息标签中是否设置了init-method 属性,如果是则执行设置的自定义初始化方法,或者判断bean 是都实现了InitializingBean 接口,如果是则调用其afterPropertiesSet 方法。注意是走的InitializingBean 接口,然后再走的自定义方法。
1 |
|
调用所有BeanPostProcessor后置处理器的After方法
然后就是最后调用所有BeanPostProcessor 后置处理器的After 方法,这里依然是迭代调用,跟上面的Before 方法没有区别。最后返回初始化完成结果。
1 | if (mbd == null || !mbd.isSynthetic()) { |
这个后面的方法就可以不看了,自行了解一下就行。这个方法执行完成之后,我们就得到了一个完整的bean 对象,然后我们回到doGetBean 方法,这里有一个很有意思的地方,刚刚忘记说了,createBean 方法本身不是在doGetBean 方法中调用的,我们再看下具体代码。
完整对象注入单例池
注意这里的意思是:调用getSingleton 方法,并传入一个beanName和一个函数。也就是说doGetBean 方法调用的是getSingleton 方法,而不是doGetBean 方法,当然最后还是调用doGetBean 方法,我们上面的思路是没有错的,就是在中间加一步。
1 | // 9.1 scope为singleton的bean创建(新建了一个ObjectFactory,并且重写了getObject方法) |
我们这里跟进getSingleton 方法,首先加锁判避免重复创建,然后判断是否存在一级缓存中,也就是单例池中,然后做一些准备工作,最后调用我们刚刚传入函数的getObject 方法,这里回头再看其实入参就是ObjectFactory 类型对象,而我们刚刚看的函数就是getObject 方法的重写。
这里还有一个需要注意的就是在调用doCreateBean 方法之前,这里会添加一个在singletonsCurrentlyInCreation 集合属性,这里记录的就是当前Bean 正在被创建。
1 | // 5.创建单例前的操作 |
函数接口-ObjectFactory接口
这里又有一个小知识点,我自己本身也没有怎么看,用推测的吧,首先上面提到的ObjectFactory 接口,本身只有一个方法就是getObject,但是有一个由java 提供的注解@FunctionalInterface,字面理解是功能接口,我理解是函数接口,可以接收一个函数式的方法,只跟上面的实现一样。
1 |
|
那么上一步走完后,接下来就是addSingleton 方法的调用,这一步是为了将刚刚创建的对象存入单例池中。
1 | // 8.如果是新的单例对象,将beanName和对应的bean实例添加到缓存中(singletonObjects、registeredSingletons) |
1 | protected void addSingleton(String beanName, Object singletonObject) { |
到这里为止IOC 实例化、初始化、注册单例池的内容基本就结束了。
发布事件与清除上下文环境-finishRefresh方法
回到refresh 核心方法,我们还有最后一步,就是发布事件并清除上下文环境,这里也很好理解,我们上面不是构建了事件广播器之类的对象嘛,这里就是用来发布事件到广播器,然后由广播器推送到监听列表中的各个监听器的,至于上下文环境之类的我们后面MVC 的内容再说。
1 | protected void finishRefresh() { |
面试点:BeanFactory和FactoryBean的区别与FactoryBean和普通Bean的区别
BeanFactory和FactoryBean区别
我们先说BeanFactory 和FactoryBean 区别,这个比较简单,通过这一些的文章我们已经很了解BeanFactory 了,那我们现在只要知道FactoryBean 就行了。
FactoryBean 就是一个单纯的接口,其中有三个方法需要实现,最主要的就是getObject,可以这么说如果有对象实现了FactoryBean 接口,那么bean 获取的时候,就不再获取本身对象实例,也就是说如果A 实现了FactoryBean 接口,那么applicationContext.getBean(A) 的时候,获取到的就是可能不是A,而是FactoryBean 的getObject 方法返回的对象。
1 | public interface FactoryBean<T> { |
对了这里我们还可以简单看下FactoryBean 是怎么获取到对象的。还是以applicationContext.getBean(A) 来跟进调用。
跟进后发现有回到了上面说的doGetBean 方法的调用。
1 | public Object getBean(String name) throws BeansException { |
其实上面注册过之后,我们就可以在单例池中拿到对应的对象实例,注意这里的实例还是A 对象的实例,然后就可以进入我们上面没有进入的判断,调用getObjectForBeanInstance 方法。
1 | // 2.尝试从缓存中获取beanName对应的实例, 循环依赖闭环可以拿到 |
这里的方法更简单。如果不是FactoryBean 实现,直接返回,也就说普通对象,不走下面的流程,如果是则将传入的实例对象强转为FactoryBean 对象,然后getObjectFromFactoryBean 方法获取真正的返回实例对象,这个方法就是调用其getObject 方法。不过正在的调用还在里面的一层doGetObjectFromFactoryBean 方法中。
1 | if (!(beanInstance instanceof FactoryBean)) { |
总结一下:BeanFactory 是Bean 的工厂,其中有单例池、BeanDefinition、BeanPostProcessor等等,而FactoryBean 是接口,只要实现了该接口那么获取其对象的时候,就是获取getObject 方法返回的对象,而不是获取对象本身实例。
FactoryBean和普通Bean的区别
其实这点刚刚也说了,它们之剑的区别就是获取对象实例的时候不一样,普通Bean 在单例池中获取到之后就返回了,而实现了FactoryBean 接口的对象还要继续走,直到最后调用getObject 方法返回才行。
总结
全文总结一下,本次说完了核心方法的全部调用,主要重点是bean 的实例化、初始化、注册等,还有一些beanPostProcessor 的两个方法调用之类的内容,再有的话就是spring 的事件传播机制了,最后也聊了一下常见的两个面试点。后续文章还是聊一下面试点:循环依赖的解决。
附录Spring 源码分析系列文章
IOC
时间 | 文章 |
---|---|
2022-03-09 | Spring的基本概念和IOC流程的简述 |
2022-03-11 | IOC流程解析-BeanFactory的创建 |
2022-03-14 | IOC流程解析-BeanFactoyPostProcessor和BeanPostProcessor |
2022-03-15 | IOC流程解析-实例化和初始化 |
2022-03-17 | IOC流程解析-循环依赖 |
AOP
时间 | 文章 |
---|---|
2022-03-19 | AOP流程源码分析-配置信息解析和代理对象创建 |
2022-03-20 | AOP流程源码分析-请求调用全流程 |