共计 4449 个字符,预计需要花费 12 分钟才能阅读完成。
前言
在钻研『Spring 是如何解决循环依赖的』的时候,理解到 Spring 是借助 三级缓存 来解决循环依赖的。
同样在上一节留下了疑难:
- 循环依赖为什么要应用三级缓存?而不是应用二级缓存?
- AOP 动静代理对循环依赖的有没有什么影响?
本篇文章也是围绕下面的内容进行开展。
笔记也在一直整顿,之前可能会有点芜杂。
循序渐进,看一看什么是循环依赖?
开始先简略回顾一下 Bean 的创立过程,当然小伙伴也能够间接浏览『单例 Bean 的创立』这篇文章。
不过思考到浏览本文前再浏览上一篇文章、Debug 等等,会比拟耗时,所以本篇文章后面一小部分会先对之前的文章内容做简要概括,也相当于对我本人学习的常识进行一个总结。
先来回顾一下三级缓存的概念。
singletonObjects: 一级缓存,存储单例对象,Bean 曾经实例化,初始化实现。
earlySingletonObjects: 二级缓存,存储 singletonObject,这个 Bean 实例化了,还没有初始化。
singletonFactories: 三级缓存,存储 singletonFactory。
Bean 的创立过程
@Service
public class CircularServiceA {private String fieldA = "字段 A";}
通过下面的流程,能够看出 Spring 在创立 Bean 的过程中重点是在 AbstractAutowireCapableBeanFactory 中的以下三个步骤:
- 实例化 createBeanInstance: 其中实例化 Bean 并对 Bean 进行赋值,像例子中的
fieldA
字段在这里就会赋值。 - 属性注入 populateBean: 能够了解为对 Bean 外面的属性进行赋值。(会依赖其余 Bean)
- 初始化 initializeBean: 执行初始化和 Bean 的后置处理器。
实例化赋值源码能够浏览:
BeanUtils.instantiateClass(constructorToUse)
如果要依赖其余 Bean 呢?
那如果 CircularServiceA 依赖了其余 Bean 呢?
@Service
public class CircularServiceA {
private String fieldA = "字段 A";
@Autowired
private CircularServiceB circularServiceB;
}
@Service
public class CircularServiceB {}
当 A 依赖了 B 的时候,在 createBeanInstance
这一步,并不会对 B 进行属性赋值。
而是在 populatedBean
这里查找依赖项,并创立 B。
循环依赖下的创立过程
循环依赖的场景,在上一篇文章曾经有所解说,这里仅仅画图阐明一下。
@Service
public class CircularServiceA {
private String fieldA = "字段 A";
@Autowired
private CircularServiceB circularServiceB;
}
@Service
public class CircularServiceB {
@Autowired
private CircularServiceA circularServiceA;
}
在 A 和 B 循环依赖的场景中:
B populatedBean
查找依赖项 A 的时候,从一级缓存中尽管未获取到 A,然而发现 A 在创立中。
此时,从三级缓存中获取 A 的 singletonFactory
调用工厂办法,创立 getEarlyBeanReference
A 的晚期援用并返回。
B 援用到 A,B 就能够初始化结束,而后 A 同样也能够初始化结束了。
二级缓存是否解决循环依赖
通过下面的图,仔细分析一下,其实把二级缓存拿掉,在 B 尝试获取 A 的时候间接返回 A 的实例,是不是也是能够的?
答案是:能够的!
然而为什么还是用三级缓存呢?
网上的很多材料说是和动静代理有关系,那就从动静代理的方面持续往下剖析剖析。
动静代理的场景
在 JavaConfig(配置类)上增加 @EnableAspectJAutoProxy
注解,开启 AOP,通过 Debug 循序渐进看一看动静代理对循环依赖的影响。
动静代理下,Bean 的创立过程
@Service
public class CircularServiceA {
private String fieldA = "字段 A";
public void methodA() {System.out.println("办法 A 执行");
}
}
@Aspect
@Component
public class AspectA {@Before("execution(public void com.liuzhihang.circular.CircularServiceA.methodA())")
public void beforeA() {System.out.println("beforeA 执行");
}
}
只有 A 的状况下,给 A 增加切面,开始 Debug。
后面的流程都雷同,在 initializeBean 开始呈现差别。
这一步须要初始化 Bean 并执行 Bean 的后置处理器。
其中有一个处理器为:AnnotationAwareAspectJAutoProxyCreator
其实就是加的注解切面,会跳转到 AbstractAutoProxyCreator 类的 postProcessAfterInitialization 办法
如图所示:wrapIfNecessary 办法会判断是否满足代理条件,是的话返回一个代理对象,否则返回以后 Bean。
后续调用 getProxy
、createAopProxy
等等,最终执行到上面一部分。
最终会执行到这里,AOP 代理相干的就不细看了。
一路放行,直到 initializeBean 执行完结。
此时发现:A 被替换为了代理对象。
所以 doCreateBean 返回,以及前面放到一级缓存中的都是代理对象。
有循环依赖的动静代理
这一次把循环依赖关上:
@Service
public class CircularServiceA {
private String fieldA = "字段 A";
@Autowired
private CircularServiceB circularServiceB;
public void methodA() {System.out.println("办法 A 执行");
}
}
@Aspect
@Component
public class AspectA {@Before("execution(public void com.liuzhihang.circular.CircularServiceA.methodA())")
public void beforeA() {System.out.println("beforeA 执行");
}
}
@Service
public class CircularServiceB {
@Autowired
private CircularServiceA circularServiceA;
public void methodB() {}
}
@Aspect
@Component
public class AspectB {@Before("execution(public void com.liuzhihang.circular.CircularServiceB.methodB())")
public void beforeB() {System.out.println("beforeB 执行");
}
}
开始 Debug,后面的一些列流程,都和失常的没有什么区别。而惟一的区别在于,创立 B 的时候,须要从三级缓存获取 A。
此时在 getSingleton
办法中会调用:singletonObject = singletonFactory.getObject();
有时会比拟纳闷 singletonFactory.getObject()
调用的是哪里?
所以这一块调用的是 getEarlyBeanReference
,开始遍历执行 BeanPostProcessor
。
看到 wrapIfNecessary
就明确了吧!这块会获取一个 代理对象
。
也就是说此时返回,并放到二级缓存的是一个 A 的代理对象。
这样 B 就创立结束了!
到 A 开始初始化并执行后置处理器了!因为 A 也有代理,所以 A 也会执行到 postProcessAfterInitialization
这一部分!
然而在执行 wrapIfNecessary
之前,会先判断代理对象缓存是否有 A 了。
this.earlyProxyReferences.remove(cacheKey) != bean
然而这块获取到的是 A 的代理对象。必定是 false。所以不会再生成一次 A 的代理对象。
总结
能够看到,循环依赖下,有没有代理状况下的区别就在:
singletonObject = singletonFactory.getObject();
在循环依赖产生的状况下 B 中的 A 赋值时:
- 无代理:getObject 间接返回原来的 Bean
- 有代理:getObject 返回的是代理对象
而后都放到 二级缓存。
为什么要三级缓存?
- 假如去掉三级缓存
去掉三级缓存之后,Bean 间接创立 earlySingletonObjects
,看着如同也能够。
如果有代理的时候,在 earlySingletonObjects
间接放代理对象就行了。
然而会导致一个问题:在实例化阶段就得执行后置处理器,判断有 AnnotationAwareAspectJAutoProxyCreator 并创立代理对象。
这么一想,是不是会对 Bean 的生命周期有影响。
同样,先创立 singletonFactory
的益处就是:在真正须要实例化的时候,再应用 singletonFactory.getObject() 获取 Bean 或者 Bean 的代理。相当于是提早实例化。
- 假如去掉二级缓存
如果去掉了二级缓存,则须要间接在 singletonFactory.getObject()
阶段初始化结束,并放到一级缓存中。
那有这么一种场景,B 和 C 都依赖了 A。
要晓得在有代理的状况下 singletonFactory.getObject()
获取的是代理对象。
而屡次调用 singletonFactory.getObject()
返回的代理对象是不同的,就会导致 B 和 C 依赖了不同的 A。
那如果获取 B 到之后间接放到一级缓存,而后 C 再获取呢?
???? ……
一级缓存放的是曾经初始化结束的 Bean,要晓得 A 依赖了 B 和 C,A 这时候还没有初始化结束。
小结
循环依赖的场景有很多,本文只是通过 Debug,来理解到循环依赖和 AOP 之间的关系,以及理解到为什么要用三级缓存。
当然,Spring 设计之初是什么样子的?如何一步一步倒退成当初这种的?
必定是不能缓缓去钻研了,所以只能以当初的版本,去推测作者的用意。
不足之处,多多斧正。
相干举荐
- Spring 是如何解决循环依赖的?
- Spring 源码学习 16:单例 Bean 创立
- Spring 源码学习 15:finishBeanFactoryInitialization(重点)