目录
一、BeanDefinition加载解析及注册(创建BeanFactory)
二、BeanFactory的预准备/处理
三、注册并执行BeanFactory的后置处理器
四、注册Bean的后置处理器
五、初始化/注册其他组件
六、初始化所有剩下的单实例bean(重头戏)
七、完成IOC容器的刷新
八、(附加)循环依赖/三级缓存
8.1 什么是循环依赖?
8.2循环依赖什么情况下可以被解决?
8.3 循环依赖如何解决?
8.3.1 无AOP
8.3.2 有AOP(三级缓存的奥秘之处)
8.4 总结
8.4.1 ”Spring是如何解决的循环依赖?“
8.4.2 ”为什么要使用三级缓存呢?二级缓存能解决循环依赖吗?“
IoC容器是Spring的核⼼模块,是抽象了对象管理、依赖关系管理的框架解决⽅案。Spring 提供了很多 的容器,其中 BeanFactory 是顶层容器(根容器),不能被实例化,它定义了所有 IoC 容器必须遵从的⼀套原则,具体的容器实现可以增加额外的功能,⽐如我们常⽤到的ApplicationContext,其下更具体的实现如 ClassPathXmlApplicationContext 包含了解析 xml 等⼀系列的内容, AnnotationConfigApplicationContext 则是包含了注解解析等⼀系列的内容。
ApplicationContext 除了继承BeanFactory的⼦接⼝, 还继承了ResourceLoader、MessageSource等接⼝,因此其提供的功能也就更丰富了。
通过断点调试发现,IOC容器的创建过程基本都是在AbstractApplicationContext 的refresh()方法中实现的:
public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
//1.预处理
this.prepareRefresh();
//2.获取BeanFactory。
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
//3.BeanFactory的预准备工作
this.prepareBeanFactory(beanFactory);
try {
//4.BeanFactory准备⼯作完成后进⾏的后置处理⼯作(在BeanFactory准备工作完成后做一些定制化的处理,没有具体实现)
this.postProcessBeanFactory(beanFactory);
//5.实例化并调⽤实现了BeanFactoryPostProcessor接⼝的Bean
this.invokeBeanFactoryPostProcessors(beanFactory);
//6.注册BeanPostProcessor(Bean的后置处理器),在创建bean的前后执⾏
this.registerBeanPostProcessors(beanFactory);
//7.初始化MessageSource组件
this.initMessageSource();
//8.初始化事件派发器
this.initApplicationEventMulticaster();
//9.留给子容器(子类),在容器刷新的时候可以⾃定义逻辑
this.onRefresh();
//10.注册应⽤的监听器。就是注册实现了ApplicationListener接⼝的监听器bean
this.registerListeners();
//11.初始化所有剩下的⾮懒加载的单例bean
// 初始化创建⾮懒加载⽅式的单例Bean实例(未设置属性)
// 填充属性
// 初始化⽅法调⽤(⽐如调⽤afterPropertiesSet⽅法、init-method⽅法)
// 调⽤BeanPostProcessor(后置处理器)对实例bean进⾏后置处理
this.finishBeanFactoryInitialization(beanFactory);
//12.完成context的刷新。主要是调⽤LifecycleProcessor的onRefresh()⽅法,并且发布事件 (ContextRefreshedEvent)。IOC容器创建完成
this.finishRefresh();
} catch (BeansException var9) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
}
this.destroyBeans();
this.cancelRefresh(var9);
throw var9;
} finally {
this.resetCommonCaches();
}
}
}
其中,Bean对象创建的⼏个关键时机点 :
下面给出了大致的流程图,其中最重要的是Bean的生命周期部分,这里额外给一个图:
下面再细说。
该阶段的目标是创建BeanFactory(DefaultListableBeanFactory),将配置文件中的信息进行定位、解析、加载为BeanDefinition并保存在BeanFactory对象中。
的过程
该流程涉及到几个关键点:
1.Resource定位:指对BeanDefinition的资源定位过程。通俗讲就是找到定义Javabean信息的XML⽂ 件,并将其封装成Resource对象。
2.BeanDefinition载⼊ :把⽤户定义好的Javabean表示为IoC容器内部的数据结构,这个容器内部的数据结构就是BeanDefinition。
3.注册BeanDefinition到 IoC 容器
该过程在整体流程图的第2步完成(obtainFreshBeanFactory)。这是第2步的简略时序图:
BeanDefinition 涉及到的部分方法:
调用refreshBeanFactory -> loadBeanDefinitions ->AbstractBeanDefinitionReader->子实现类的doLoadBeanDefinitions -> doLoadBeanDefinitions -> registerBeanDefinitions。细节不再深入。
该过程的时序图:
所谓的注册就是把封装的 XML 中定义的 Bean信息封装为 BeanDefinition 对象之后放⼊⼀个Map中,BeanFactory 是以 Map 的结构组织这些 BeanDefinition 的。
该阶段完成BeanFactory的预处理工作(对应第3步-prepareBeanFactory)
例如:
1)设置BeanFactory的类加载器、支持表达式解析器...
2)添加部分BeanPostProcessor【ApplicationContextAwareProcessor】【ApplicationListenerDetector】
3)设置忽略的自动装配的接口EnvironmentAware、EmbeddedValueResolverAware
4)注册可以解析的自动装配;我们能直接在任何组件中自动注入:
BeanFactory、ResourceLoader、ApplicationEventPublisher、ApplicationContext
5)添加编译时的AspectJ;
6)给BeanFactory中注册一些能用的组件;
environment【ConfigurableEnvironment】、
systemProperties【Map】、
systemEnvironment【Map】
第4步的 postProcessBeanFactory可以在BeanFactory准备工作完成后进行一些定制的后置处理工作,默认没有实现,子类通过重写这个方法来在BeanFactory创建并预准备完成后做进一步的设置。
该阶段实例化并调用实现了BeanFactoryPostProcessor以及BeanDefinitionRegistryPostProcessor的Bean,后置处理器可以有多个。(对应第5步-invokeBeanFactoryPostProcessors)
注意:
这些后置处理器可以实现PriorityOrdered-优先级接口/Ordered-顺序接口,执行时是按照优先级顺序执行的。 (PriorityOrdered -> Ordered -> 无)
这里先执行BeanDefinitionRegistryPostProcessor再执行BeanFactoryPostProcessor。
BeanDefinitionRegistryPostProcessor 是 BeanFactoryPostProcessor的子接口,二者都是后置处理器。
BeanDefinitionRegistryPostProcessor 侧重于创建自定义的bd,而BeanFactoryPostProcessor侧重于对已有bd属性的修改。
该阶段注册BeanPostProcessor(Bean的后置处理器)
不同接口类型的BeanPostProcessor在Bean创建前后的执行时机是不一样的。(对应第6步-registerBeanPostProcessors)
例如:
BeanPostProcessor、
DestructionAwareBeanPostProcessor、
InstantiationAwareBeanPostProcessor、
SmartInstantiationAwareBeanPostProcessor、
注意:
这些后置处理器可以实现PriorityOrdered-优先级接口/Ordered-顺序接口,执行时是按照优先级顺序执行的。 (PriorityOrdered -> Ordered -> 无)
最后还会注册一个ApplicationListenerDetector:
该
BeanPostProcessor
检测那些实现了ApplicationListener
接口的bean
,在它们创建并初始化之后将它们添加到ApplicationContext的事件多播器上;并在这些ApplicationListener bean
销毁之前将它们从ApplicationContext的事件多播器上移除。
这里对应第7、8、9、10步。
initMessageSource();初始化MessageSource组件(做国际化功能;消息绑定,消息解析)
initApplicationEventMulticaster();初始化事件派发器;
1)从BeanFactory中获取ApplicationEventMulticaster;
3)如果没有配置就创建一个SimpleApplicationEventMulticaster 添加到BeanFactory中
onRefresh();留给子容器(子类)子类重写这个方法,在容器刷新的时候可以自定义逻辑;
registerListeners();给容器中将所有项目里面的ApplicationListener注册进来;
1、从容器中拿到所有的ApplicationListener
2、将每个监听器添加到事件派发器中
3、派发之前步骤产生的事件;
这里对应第11步(finishBeanFactoryInitialization)
该方法会实例化所有剩余的非懒加载单例 bean。除了一些内部 bean、实现了 BeanFactoryPostProcessor 接口的 bean、实现了 BeanPostProcessor 接口的 bean,其他的非懒加载单例 bean 都会在这个方法中被实例化,并且 BeanPostProcessor 的触发也是在这个方法中。
主要用到的就是 beanFactory.preInstantiateSingletons() 方法,其主要任务是进行初始化,首先获取Bean的定义信息-RootBeanDefinition,然后是一系列判断,如是否是懒加载的,是否是单实例的,是否是factorybean,最后调用getBean()->dogetBean()方法。
dogetBean()的大致流程:
1. 转换对应 beanName
这里传入的参数可能是别名,也可能是 FactoryBean,所以需要进行一系列的解析。
2.尝试从缓存中加载单例
主要目的就是为了解决单例模式下的循环依赖。
3.Bean 的实例化
返回对应实例,这里的作用就是判断对应的Bean 是不是FactoryBean类型,如果是 就通过调用对应的getObject() 方法,返回对应的实例,如果不是FactoryBean类型,直接返回4.原型模型的依赖检查
只有在单例情况下才会尝试解决循环依赖,原型模式下如果存在循环依赖就直接报错5.检查parentBeanFactory
递归的调用父工厂的 getBean 方法
6.根据beanName获取合并过的对应的RootBeanDefinition
所有的bean后续处理都是针对于RootBeanDefinition的,所以这里需要进行一个转换,转换的同时如果父类bean不为空的话,则会一并合并父类的属性。
7.寻找依赖
判断是否存在依赖,如果存在依赖,那就先创建对应的依赖的Bean
8.针对不同的 scope 进行 bean 的创建
根据不同的配置进行不同的初始化策略。(回到3)
9.类型检验并转换
具体的实例化过程在createBean()方法中,我们看createBean()->docreateBean()方法。
1.判断是否是单例模式,若是就从缓存 factoryBeanInstanceCache 获取,并将缓存清除
2.如果不是单例模式,或者缓存没有对应的value,就开始创建实例,调用createBeanInstance 方法(利用工厂方法或者对象的构造器创建出Bean实例)
3.修改合并之后的 BeanDefinition,bean 合并后的处理, Autowired 注解正是通过此方法实现诸如类型的预解析
4.依赖处理 ,这里有一个放入缓存,以便其他的依赖此bean的 bean可以提前获取到, 这里调用了getEarlyBeanReference 进行提前曝光
5.属性填充, 调用了 populateBean方法,将各个属性值注入
6.初始化Bean ,这里对各种Aware 设置、前置、后置 等相关的处理- initialzingBean
7.循环检查,检查是否还有依赖的bean没有创建
8.注册到disposableBeans 里面,以便在销毁bean 的时候 后置处理也生效
关于populateBean:
1)拿到InstantiationAwareBeanPostProcessor后置处理器;
2)注入Bean属性的值
关于initializeBean:
1)【执行Aware接口方法】invokeAwareMethods(beanName, bean);
2)【执行后置处理器-初始化之前】
applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
3)【执行初始化方法】invokeInitMethods(beanName, wrappedBean, mbd);
1)是否是InitializingBean接口的实现;执行接口规定的初始化;
2)是否自定义初始化方法;
4)、【执行后置处理器-初始化之后】
applyBeanPostProcessorsAfterInitialization
这里就对应了该流程的第一个图。
1)initLifecycleProcessor() - 初始化和生命周期有关的后置处理器;
2)拿到前面定义的生命周期后置处理器(BeanFactory),回调onRefresh();
3)publishEvent(new ContextRefreshedEvent(this)) - 发布容器刷新完成事件;
我们首先要了解Spring创建IOC容器过程中用到的三种缓存:
singletonObjects
,一级缓存,存储的是所有创建好了的单例Bean
earlySingletonObjects
,完成实例化,但是还未进行属性注入及初始化的对象
singletonFactories
,提前暴露的一个单例工厂,二级缓存中存储的就是从这个工厂中获取到的对象
循环依赖的意思就是A依赖了B,B又依赖A。依赖方式有构造器参数注入、setter属性注入
A、B相互依赖的方式都是构造器注入时,不能解决!
A、B不是单例,不能解决!
A、B 不都是用构造器注入,有可能解决!
A、B 都是Setter注入,可以解决!
为什么第三种情况是有可能解决,而不是一定呢?
因为Spring在创建Bean时默认会根据自然排序进行创建,所以A会先于B进行创建。
A使用构造器属性注入B时,依赖属性B的getBean调用提前了,这时A还没有放入三级缓存。此时依赖属性B进行实例化后想使用setter注入属性A,或者用构造器注入参数A都是不可能的了!因为A在缓存里找不到!!
假如A注入B的方式是setter,则A在注入属性B之前就已经将自己放到缓存中了,那B即无论使用构造器注入A,还是setter注入A都是可行的,因为B在缓存里能找到A。
首先,我们要知道Spring在创建Bean的时候默认是按照自然排序来进行创建的,所以第一步Spring会去创建A。Spring在创建Bean的过程中分为三步
实例化,对应方法:createBeanInstance(
简单理解就是new了一个对象)
属性注入,对应方法:populateBean(
为实例化中new出来的对象填充属性)
初始化,对应方法:initializeBean(
执行aware接口的方法,初始化方法,AOP
代理)
在完成Bean的实例化后,属性注入之前Spring将Bean包装成一个工厂添加进了三级缓存中。这里只是添加了一个工厂,通过这个工厂(ObjectFactory
)的getObject
方法可以得到一个对象。
当A完成了实例化并添加进了三级缓存后,就要开始为A进行属性注入了,在注入时发现A依赖了B,那么这个时候Spring又会去getBean(b)
,然后反射调用setter方法完成属性注入。
这时B进行实例化,然后注入属性A,因为A对应的工厂已经存在了三级缓存中,所以直接从缓存中取出A放进二级缓存,并移除三级缓存中A的工厂。B初始化完成。
最后再回到A属性注入的流程中,成功注入B的属性,再初始化。
初始化完成的Bean最终都会放入一级缓存中。
这种情况下这个工厂啥都没干,直接将实例化阶段创建的对象返回了!所以说在不考虑
AOP
的情况下三级缓存没什么用
从三级缓存获取对象会调用AnnotationAwareAspectJAutoProxyCreator
的getEarlyBeanReference
方法。
如果对A进行了
AOP
代理,那么此时getEarlyBeanReference
将返回一个代理后的对象,而不是实例化阶段创建的对象,这样就意味着B中注入的A将是一个代理对象而不是A的实例化阶段创建的对象。
在给B注入的时候为什么要注入一个代理对象?
答:当我们对A进行了AOP
代理时,说明我们希望从容器中获取到的就是A代理后的对象而不是A本身,因此把A当作依赖进行注入时也要注入它的代理对象(A的代理对象会替换原来的A对象)
三级缓存的作用在用处?
答:这个工厂的目的在于延迟对实例化阶段生成的对象的代理,只有真正发生循环依赖的时候,才去提前生成代理对象,否则只会创建一个工厂并将其放入到三级缓存中,但是不会去通过这个工厂去真正创建对象。
对于A,无论其是否有循环依赖,只要它被AOP代理,就一定会存在三级缓存中来提前暴露一个工厂对象。这时还不知道它是否存在循环依赖(还没注入属性呢)。
如果直接放入二级缓存,那现在就要为A创建代理(此时未初始化)。这违背了Spring在结合
AOP
跟Bean的生命周期的设计!Spring结合AOP
跟Bean的生命周期本身就是通过一个后置处理器来完成的,在这个后置处理器中对初始化后的Bean完成AOP
代理。如果出现了循环依赖,那没有办法,只有给Bean先创建代理,但是没有出现循环依赖的情况下,设计之初就是让Bean在生命周期的最后一步(初始化)完成代理而不是在实例化后就立马完成代理。
这是使用三级缓存时的情况:
这是不使用三级缓存,直接放在二级缓存时的情况:
上面两个流程的唯一区别在于为A对象创建代理的时机不同,在使用了三级缓存的情况下为A创建代理的时机是在B中需要注入A的时候,而不使用三级缓存的话在A实例化后就需要马上为A创建代理然后放入到二级缓存中去。
答:Spring通过三级缓存解决了循环依赖,其中一级缓存为单例池(singletonObjects
),二级缓存为早期曝光对象earlySingletonObjects
,三级缓存为早期曝光对象工厂(singletonFactories
)。
当A、B两个类发生循环引用时,在A完成实例化后,就使用实例化后的对象去创建一个对象工厂,并添加到三级缓存中,如果A被AOP代理,那么后面通过这个工厂获取到的就是A代理后的对象;如果A没有被AOP代理,那么这个工厂获取到的就是A实例化的对象。
当A进行属性注入时,会去创建B,同时B又依赖了A,所以创建B的同时又会去调用getBean(a)来获取需要的依赖,此时的getBean(a)会从缓存中获取。第一步,先获取到三级缓存中的工厂;第二步,调用对象工工厂的getObject方法来获取到对应的对象,得到这个对象后将其注入到B中。紧接着B会走完它的生命周期流程,包括初始化、后置处理器等。当B创建完后,会将B再注入到A中,此时A再完成它的整个生命周期。至此,循环依赖结束!
答:如果要使用二级缓存解决循环依赖,意味着所有Bean在实例化后就要完成AOP代理,这样违背了Spring设计的原则,Spring在设计之初是通过AnnotationAwareAspectJAutoProxyCreator
这个后置处理器来在Bean生命周期的最后一步来完成AOP代理,而不是在实例化后就立马进行AOP代理。
注:三级缓存并不提高效率,只是为了尽可能不违背Spring的Bean生命周期的原则。