加载spring的配置文件
核心方法:loadBeanDefinitions,有一系列同名的方法
<bean>
<context>
<aop>
<tx>
每个标签包含不同元素属性,需要独特的处理器来进行处理
无论是xml或者json的配置文件,都是需要java的io流,只不过读取完之后对不同的文件进行相应的解析,非常清晰的有个reader的方法
读取多个配置文件
跟着同名方法loadBeanDefinition,终究还是需要一条流
进入到核心方法doLoadBeanDefinitions,接着将流转为document,接着registerBeanDefinitions
一直跟着register和doregister方法
preProcessXml(root);
// 解析document的方法
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
// 对属性进行分类,看是不是默认的命名空间
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
根据不同的标签进行解析
实际的调用栈
循环依赖的解决
spring中,对象bean默认都是单例的,意味着一个容器只有一个相同对象,那么如果A对象应用了类B的对象b,而B对象引用了类A的对象a,就会出现循环依赖,也就是先有鸡和先有蛋的故事的重现。
也就是在赋值的过程会导致这种情况发生,赋值主要分为构造器和set方法,构造器方法是无法解决循环依赖的,而set方法则可以通过三级缓存来解决
实际一个对象从未赋值到形成一个最终完整的对象才是一个完整的过程,但实际上这个未赋值的对象也已经在堆空间有了一个内存地址,这样一来,比如说A对象引用B对象时,我们可以先引用未赋值的B对象(半成品),因为也知道B对象的内存地址,可以等对象创建完毕之后再赋值。于是乎,我们可以先把半成品放入map缓存,A对象引用时直接从map缓存中找,而不需要去容器找避免了”闭环“的产生,而成品对象也可以放入另一个缓存当中
实际源码当中是有三级的
问题1:为什么要有三级?
singletonObjects =》1
earlySingletonObject =〉 2
singletonFactories(函数式接口) =》3
1、首先是创建对象A,第一次和第二次调用getBean是没有缓存;
2、第三次调用getBean是存在对象a和b于三级缓存并且是在创建对象A过程中,所以从从缓存中拿到对象a(拿的过程实际是执行了lambda表达式的方法),但这次value是一个准确的地址1556,bean对象a就有了,放入二级缓存;
3、接着对象b执行lambda表达式,由于已经有对象a在二级缓存,所以b对象也可以创建出来
4、由于是在创建A对象,所以最后将b对象设置为A对象的属性
5、接着创建对象B,发现三级缓存中有对象b,直接就赋给对象B了
先放入三级缓存:第一次 key:对象a,value:lambda表达式
第二次 key:对象b,value:lambda表达式
二级缓存:第三次 key:对象a,value:地址1556(半成品对象,还没属性b=null)
一级缓存:第四次:key:对象b,value:地址2999 (成品对象)
第五次:key:对象a,value:地址1556 (成品对象)(调用setProperties方法)
每放入一级缓存,移除上一级缓存
面试官:”Spring是如何解决的循环依赖?“
答: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代理。
问题2:只用一级缓存行不行?
不行,可能取到半成品或者成品对象
问题3:只用2层缓存行不行?
两层缓存可以解决循环依赖的问题,添加aop之后,会报错没有使用最终版本的bean对象
问题4:第三级缓存到底做了什么?
区分代理对象和非代理对象的
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
// 暴露出来的对象,条件判断这个对象是不是代理对象
Object exposedObject = bean;
if (bean != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
// 重新赋值
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
if (exposedObject == null) {
return null;
}
}
}
}
return exposedObject;
}
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 一级缓存
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
// 二级
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
// 三级
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}