Spring容器如何解决循环依赖的原理,2024年阿里Java面试题精选

2.1 一级缓存使用的map: private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);

2.2 二级缓存使用的map: private final Map<String, Object> earlySingletonObjects = new HashMap(16);

2.3 三级缓存使用的map: private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);

3、Spring容器解决循环依赖简洁概述主要有四大流程方法:获取对象 getSingleton()、 创建对象(实例化) doCreateBean()、填充属性(初始化) populateBean()、返回对象 addSingleton()

在系统启动获取配置文件后,程序是依次读取并加载的,所以上面配置文件代码,先实例化a对象,然后初始化a对象给a添加b属性,再实例化b对象,最后初始化b对象给添加属性a.

那么在代码执行过程中,先调用getSingleton()方法,我们查看源码

@Nullable
public Object getSingleton(String beanName) { // 调用下方重载方法
return this.getSingleton(beanName, true);
}

@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {       // 先从一级缓存中获取a对象的实例
Object singletonObject = this.singletonObjects.get(beanName); // 如果从一级缓存中获取不到a对象,那么检查该对象是否正在被创建,如果正在被创建,则进入if循环中
if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
synchronized(this.singletonObjects) {            // 从二级缓存中获取该对象
singletonObject = this.earlySingletonObjects.get(beanName); // 如果二级缓存中无法获取该对象,那么一定会进入如下if方法,因为allowEarlyReference传过来的时候就是true
if (singletonObject == null && allowEarlyReference) {              // 从三级缓存中获取该对象
ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
if (singletonFactory != null) { // 如果获取到了该对象,就将三级缓存中的对象放到二级缓存中,并且将三级缓存中的对象删除
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}

return singletonObject;
}

从三面的源码发现,如果a第一次获取,那么第9行的if语句为false,将直接放回为null,这时回到创建对象doCreateBean()方法,该方法使用反射的方式生成a对象,并且该对象在三级缓存中,对象生成后就需要对a对象进行属性填充:

1 protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
2 // 省略多行代码,大致就是调用各种方法,通过反射创建对象
3 try {
4 // a对象创建完成,调用属性填充方法,对a进行属性填充
5 this.populateBean(beanName, mbd, instanceWrapper);
6 exposedObject = this.initializeBean(beanName, exposedObject, mbd);
7 } catch (Throwable var18) {
8 if (var18 instanceof BeanCreationException && beanName.equals(((BeanCreationException)var18).getBeanName())) {
9 throw (BeanCreationException)var18;
10 }
11
12 throw new BeanCreationException(mbd.getResourceDescription(), beanName, “Initialization of bean failed”, var18);
13 }
14
15 if (earlySingletonExposure) {
16 Object earlySingletonReference = this.getSingleton(beanName, false);
17 if (earlySingletonReference != null) {
18 // 省略多行代码
19 }
20 }
22 // 省略多行代码
23 }

在上面代码**doCreateBean()方法中先创建a对象,创建完成后会调用this.populateBean(beanName, mbd, instanceWrapper)**方法对a进行属性填出,这个时候会获取配置文件中所有里面的所有属性,发现会存在一个b属性,下面贴出部分源码

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
if (bw == null) {
if (mbd.hasPropertyValues()) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, “Cannot apply property values to null instance”);
}
} else {
boolean continueWithPropertyPopulation = true;
if (!mbd.isSynthetic() && this.hasInstantiationAwareBeanPostProcessors()) {
// 删除大量代码
}

if (continueWithPropertyPopulation) {
// 删除大量源代码,applyPropertyValues方法中beanName为a,pvs为状态各种属性的PropertyValues对象,pvs就装有b这个属性
if (pvs != null) {
this.applyPropertyValues(beanName, mbd, bw, (PropertyValues)pvs);
}

}
}
}

继续跟进applyPropertyValues方法的源码

protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
if (!pvs.isEmpty()) {
if (System.getSecurityManager() != null && bw instanceof BeanWrapperImpl) {
((BeanWrapperImpl)bw).setSecurityContext(this.getAccessControlContext());
}
MutablePropertyValues mpvs = null;
List original;
if (pvs instanceof MutablePropertyValues) {
// 省略大量代码
} else {
original = Arrays.asList(pvs.getPropertyValues());
}
// 省略大量代码 大致过程是将属性对象pvs 转化成original List对象,然后在使用迭代器在下面进行迭代
Iterator var11 = original.iterator();
while(true) {
while(var11.hasNext()) {
PropertyValue pv = (PropertyValue)var11.next();
if (pv.isConverted()) {
deepCopy.add(pv);
} else {
String propertyName = pv.getName();
Object originalValue = pv.getValue();
// 通过下面方法解决依赖的b,整个方法在迭代器中,外层在while(true)中,可能有多个属性,循环直到所有属性都解决了就return;或者抛出异常
Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
// 省略大量代码
}
}

if (mpvs != null && !resolveNecessary) {
mpvs.setConverted();
}

try {
bw.setPropertyValues(new MutablePropertyValues(deepCopy));
return;
} catch (BeansException var19) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, “Error setting property values”, var19);
}
}
}
}

继续跟进上面红色方法

public Object resolveValueIfNecessary(Object argName, @Nullable Object value) {
if (value instanceof RuntimeBeanReference) {
RuntimeBeanReference ref = (RuntimeBeanReference)value;
return this.resolveReference(argName, ref);
} else if (value instanceof RuntimeBeanNameReference) {
// 省略多行代码
}
// 省略多行代码
}

继续跟进红色部分的代码

private Object resolveReference(Object argName, RuntimeBeanReference ref) {
try {
String refName = ref.getBeanName();
refName = String.valueOf(this.doEvaluate(refName));
Object bean;
if (ref.isToParent()) {
// 省略多行代码
} else {
// 通过refName的值b又去工厂找b对象
bean = this.beanFactory.getBean(refName);

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
img

Java高频面试专题合集解析:

阿里Java岗面试百题:Spring 缓存 JVM 微服务 数据库 RabbitMQ等

当然在这还有更多整理总结的Java进阶学习笔记和面试题未展示,其中囊括了Dubbo、Redis、Netty、zookeeper、Spring cloud、分布式、高并发等架构资料和完整的Java架构学习进阶导图!

阿里Java岗面试百题:Spring 缓存 JVM 微服务 数据库 RabbitMQ等

更多Java架构进阶资料展示

阿里Java岗面试百题:Spring 缓存 JVM 微服务 数据库 RabbitMQ等

阿里Java岗面试百题:Spring 缓存 JVM 微服务 数据库 RabbitMQ等

阿里Java岗面试百题:Spring 缓存 JVM 微服务 数据库 RabbitMQ等
片转存中…(img-G3haekiy-1712113159280)]

更多Java架构进阶资料展示

[外链图片转存中…(img-xTLGUzCS-1712113159280)]

[外链图片转存中…(img-tdbQKxBL-1712113159281)]

[外链图片转存中…(img-Qj6CvE8n-1712113159281)]

  • 24
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值