ServiceB serviceB;
public void test() {
}
}
@Service
class ServiceB {
@Autowired
ServiceA serviceA;
public void test() {
}
}
复制代码
简单的说就是你中有我,我中有你,
当然也不排除你中有我,我中有她,她中有你这种复杂,甚至是更复杂的关系。
总之循环依赖就是会形成一个闭合的环形关系。
2. 循环依赖带来什么问题?
循环依赖会导致创建对象,进入死循环状态,无法创建成功。
在实例化对象A的时候,需要注入对象B,这时我们需要检查缓存中是否有对象B,如果缓存中没有对象B,这时我们需要实例化对象B,开始实例化对象B,对象B中又需要依赖A,但是这时缓存又没有构建好的A,又需要实例化A,实例化A又依赖B,B又依赖A,形成死循环,永远都不能成功创建A,B。
先实例化对象B于先实例化对象A是一样的。
3 . 如何解决循环依赖问题?
基于Java引用传递的特性,我们是可以通过缓存来解决循环依赖,我们可以将构建一半的对象放入缓存中,先进行下边的步骤,当下边的步骤创建完成后,缓存中的对象自然是完整的对象,因为缓存中我们只是存的引用地址,当对象创建好之后,半成品的对象就会变成成品对象。
3.1 一级缓存解决循环依赖
我们只通过一级缓存也是可以解决循环依赖问题的,但是会有很多问题,不够严谨,比如缓存中的对象有半成品,如果我们的对象没有打对应的标签,就可能会获取到半成品对象,引发一些诡异的问题。
-
无法很好的解决增强问题,即使用代理(必要时才提前创建代理对象)
-
无法清晰明确的区分出,哪个对象已经构建完成
3.2 二级缓存解决循环依赖问题
通过二级缓存其实我们已经可以很好的解决循环依赖问题,但是我们还是无法很好的解决增强问题,即使用代理(必要时才提前创建代理对象),通过缓存解决循环依赖问题,要求我们注入的对象必须是代理对象,才能实现增强的功能。
那么我们能不能在发生循环依赖的时候才去提前初始化代理对象呢?
那么我们什么时候才能知道发生了循环依赖呢?
当依赖注入时,我们从二级缓存中获取到对象,这时候就能判定发生了循环依赖,所以说我们在这时候构建代理对象。
如果没有发生循环依赖,我们是不需要从二级缓存中获取值的。
如果说我们在二级缓存中放入ObjectFactory,当从二级缓存中获取值的时候再通过工厂创建对象,这样不就可以解决循环依赖了吗?
看似这样可以解决循环依赖问题,并且可以在发生循环依赖的时候创建代理对象,但是新的问题又出现了。
新问题:如果是A依赖于B和C,而B依赖于A,同时C也依赖于A。
那么如果先创建的是A,那么创建A的时候会在二级缓存中生成A,那么创建B的时候也会去二级缓存获取A,创建C的时候也会去二级缓存中获取A获取到的
3.3 三级缓存解决循环依赖问题
Spring默认提供三级缓存解决循环依赖
4. Spring三级缓存源码分析
以下便是Spring中的源码
定义三级缓存
/** 三级缓存 */
/** 单例池 */
/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** 单例对象工厂 */
/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** 早期(半成品)对象 */
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
复制代码
获取单例对象,单例模式(懒加载双重检测锁)
/**
-
Return the (raw) singleton object registered under the given name.
-
Checks already instantiated singletons and also allows for an early
-
reference to a currently created singleton (resolving a circular reference).
-
@param beanName the name of the bean to look for
-
@param allowEarlyReference whether early references should be created or not
-
@return the registered singleton object, or {@code null} if none found
*/
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
复制代码
创建Bean的时候进行增强
public interface ObjectFactory {
/**
-
Return an instance (possibly shared or independent)
-
of the object managed by this factory.
-
@return the resulting instance
-
@throws BeansException in case of creation errors
*/
T getObject() throws BeansException;
}
复制代码
getObject 的时候会触发AbstractAutowireCapableBeanFactory类createBean -> doCreateBean方法
创建Bean的时候会判断是否支持三级缓存,支持会提前暴露对象
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace(“Eagerly caching bean '” + beanName +
“’ to allow for resolving potential circular references”);
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
复制代码
getEarlyBeanReference方法获取代理对象,其实底层是通过AbstractAutoProxyCreator类的getEarlyBeanReference生成代理对象。
/**
-
Obtain a reference for early access to the specified bean,
-
typically for the purpose of resolving a circular reference.
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
最后
小编精心为大家准备了一手资料
以上Java高级架构资料、源码、笔记、视频。Dubbo、Redis、设计模式、Netty、zookeeper、Spring cloud、分布式、高并发等架构技术
【附】架构书籍
- BAT面试的20道高频数据库问题解析
- Java面试宝典
- Netty实战
- 算法
BATJ面试要点及Java架构师进阶资料
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
500244945)]
以上Java高级架构资料、源码、笔记、视频。Dubbo、Redis、设计模式、Netty、zookeeper、Spring cloud、分布式、高并发等架构技术
【附】架构书籍
- BAT面试的20道高频数据库问题解析
- Java面试宝典
- Netty实战
- 算法
[外链图片转存中…(img-GZGCktfq-1713500244945)]
BATJ面试要点及Java架构师进阶资料
[外链图片转存中…(img-s7Y3Nrq8-1713500244946)]
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!