SpringloC容器的依赖注入源码解析(7)—— doCreateBean之剩余逻辑(解决循环依赖的源头)

// 1.工厂方法创建

// 2.构造方法的方式注入

// 3.无参构造方法注入

instanceWrapper = createBeanInstance(beanName, mbd, args);

}

final Object bean = instanceWrapper.getWrappedInstance();

Class<?> beanType = instanceWrapper.getWrappedClass();

if (beanType != NullBean.class) {

mbd.resolvedTargetType = beanType;

}

// Allow post-processors to modify the merged bean definition.

// 调用BeanDefinition属性合并完成后的BeanPostProcessor后置处理器

synchronized (mbd.postProcessingLock) {

if (!mbd.postProcessed) {

try {

// 被@Autowired、@Value标记的属性在这里获取

applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);

}

catch (Throwable ex) {

throw new BeanCreationException(mbd.getResourceDescription(), beanName,

“Post-processing of merged bean definition failed”, ex);

}

mbd.postProcessed = true;

}

}

// 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));

}

// Initialize the bean instance.

Object exposedObject = bean;

try {

populateBean(beanName, mbd, instanceWrapper);

exposedObject = initializeBean(beanName, exposedObject, mbd);

}

catch (Throwable ex) {

if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {

throw (BeanCreationException) ex;

}

else {

throw new BeanCreationException(

mbd.getResourceDescription(), beanName, “Initialization of bean failed”, ex);

}

}

if (earlySingletonExposure) {

Object earlySingletonReference = getSingleton(beanName, false);

if (earlySingletonReference != null) {

if (exposedObject == bean) {

exposedObject = earlySingletonReference;

}

else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {

String[] dependentBeans = getDependentBeans(beanName);

Set actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);

for (String dependentBean : dependentBeans) {

if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {

actualDependentBeans.add(dependentBean);

}

}

if (!actualDependentBeans.isEmpty()) {

throw new BeanCurrentlyInCreationException(beanName,

“Bean with name '” + beanName + “’ has been injected into other beans [” +

StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +

"] in its raw version as part of a circular reference, but has eventually been " +

"wrapped. This means that said other beans do not use the final version of the " +

"bean. This is often the result of over-eager type matching - consider using " +

“‘getBeanNamesOfType’ with the ‘allowEagerInit’ flag turned off, for example.”);

}

}

}

}

// Register bean as disposable.

try {

registerDisposableBeanIfNecessary(beanName, bean, mbd);

}

catch (BeanDefinitionValidationException ex) {

throw new BeanCreationException(

mbd.getResourceDescription(), beanName, “Invalid destruction signature”, ex);

}

return exposedObject;

}

接上文分析到的位置,接下来执行:

// 向容器中缓存单例模式的Bean对象,以防循环引用

// 判断是否是早期引用的bean,如果是,则允许其提前暴露引用

// 这里判断的逻辑主要有三个:

// 1.是否为单例

// 2.是否允许循环引用

// 3.是否是在创建中的bean

boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&

isSingletonCurrentlyInCreation(beanName));

之后会来到

// 这里是一个匿名内部类,为了防止循环引用,尽早持有对象的引用

addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

进入到addSingletonFactory:

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {

Assert.notNull(singletonFactory, “Singleton factory must not be null”);

synchronized (this.singletonObjects) {

if (!this.singletonObjects.containsKey(beanName)) {

// 往三级缓存里添加

this.singletonFactories.put(beanName, singletonFactory);

// 消除此Bean在二级缓存里的缓存信息

this.earlySingletonObjects.remove(beanName);

// 这里为了记录注册单例的顺序

this.registeredSingletons.add(beanName);

}

}

}

消除此Bean在二级缓存里的缓存信息,将其包装成singletonFactory实例往三级缓存里添加

这里有一个重点就是Spring解决循环依赖的真相就在这一段源码中:在这里beanFactory被put进了singletonFactories,此时的bean只是完成了初始化构造的bean,还没有进行set或者注解注入的bean,是bean的一个中间状态,但是已经能被人认出来了,所以Spring此时将这个对象提前曝光出来让大家认识、使用。


回到doCreateBean,进入到getEarlyBeanReference

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {

Object exposedObject = bean;

if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {

for (BeanPostProcessor bp : getBeanPostProcessors()) {

// 如果是SmartInstantiationAwareBeanPostProcessor类型,就进行处理,

// 如果没有相关处理内容,就返回默认的实例。

// 里面的AbstractAutoProxyCreator类是后续AOP的关键

if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {

SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;

// 对单例进行AOP包装并返回的地方!

exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);

}

}

}

return exposedObject;

}

注意这个方法并不是在doCreateBean的

addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

这一行执行的,这里只是将getEarlyBeanReference注册进去,实际执行的地方是前面尝试从缓存里获取bean的地方,即AbstractBeanFactory的doCreateBean方法里。


回到doCreateBean,此时获取到了bean,并且只在三级缓存里面保存。

之后会执行

populateBean(beanName, mbd, instanceWrapper);

来将bean的属性真正注入到里面,然后再调用initializeBean进行彻底的初始化

// Initialize the bean instance.

// Bean对象的初始化,依赖注入在此触发

// 这个exposedObject在初始化完成之后返回作为依赖注入完成后的Bean

Object exposedObject = bean;

try {

// 填充bean实例的属性

populateBean(beanName, mbd, instanceWrapper);

// 初始化bean,过程如下:

// 1. 判断是否实现了BeanNameAware, BeanClassLoaderAware, BeanFactoryAware方法,如果有,则设置相关的属性

// 2. 调用bean初始化的前置(BeanPostProcessor)操作

// 3. 执行初始化的方法

// 如果有initializingBean,则调用afterPropertiesSet

// 如果有InitMethod,则调用初始方法

// 4. 调用bean初始化的后置(BeanPostProcessor)操作

exposedObject = initializeBean(beanName, exposedObject, mbd);

}

之后判断该bean是否允许提前暴露,

if (earlySingletonExposure) {

// 获取指定名称的已注册的单例模式Bean对象

Object earlySingletonReference = getSingleton(beanName, false);

if (earlySingletonReference != null) {

// 如果经过initializeBean执行后返回的bean还是同一个(不是代理对象实例,即没有被增强)

if (exposedObject == bean) {

// 确保根据名称获取到的的已注册的Bean和正在实例化的Bean是同一个

exposedObject = earlySingletonReference;

}

}

}

if 里面调用了getSingleton方法,进入该方法:

@Nullable

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;

}

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

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

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

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

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

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

总结

三个工作日收到了offer,头条面试体验还是很棒的,这次的头条面试好像每面技术都问了我算法,然后就是中间件、MySQL、Redis、Kafka、网络等等。

  • 第一个是算法

关于算法,我觉得最好的是刷题,作死的刷的,多做多练习,加上自己的理解,还是比较容易拿下的。

而且,我貌似是将《算法刷题LeetCode中文版》、《算法的乐趣》大概都过了一遍,尤其是这本

《算法刷题LeetCode中文版》总共有15个章节:编程技巧、线性表、字符串、栈和队列、树、排序、查找、暴力枚举法、广度优先搜索、深度优先搜索、分治法、贪心法、动态规划、图、细节实现题

最新出炉,头条三面技术四面HR,看我如何一步一步攻克面试官?

《算法的乐趣》共有23个章节:

最新出炉,头条三面技术四面HR,看我如何一步一步攻克面试官?

最新出炉,头条三面技术四面HR,看我如何一步一步攻克面试官?

  • 第二个是Redis、MySQL、kafka(给大家看下我都有哪些复习笔记)

基本上都是面试真题解析、笔记和学习大纲图,感觉复习也就需要这些吧(个人意见)

最新出炉,头条三面技术四面HR,看我如何一步一步攻克面试官?

  • 第三个是网络(给大家看一本我之前得到的《JAVA核心知识整理》包括30个章节分类,这本283页的JAVA核心知识整理还是很不错的,一次性总结了30个分享的大知识点)

最新出炉,头条三面技术四面HR,看我如何一步一步攻克面试官?

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

[外链图片转存中…(img-rZRRUFS9-1713330503379)]

《算法的乐趣》共有23个章节:

[外链图片转存中…(img-WSJCvzCO-1713330503379)]

[外链图片转存中…(img-N7DVgggN-1713330503379)]

  • 第二个是Redis、MySQL、kafka(给大家看下我都有哪些复习笔记)

基本上都是面试真题解析、笔记和学习大纲图,感觉复习也就需要这些吧(个人意见)

[外链图片转存中…(img-SG4Xk3HA-1713330503380)]

  • 第三个是网络(给大家看一本我之前得到的《JAVA核心知识整理》包括30个章节分类,这本283页的JAVA核心知识整理还是很不错的,一次性总结了30个分享的大知识点)

[外链图片转存中…(img-qedaf8QB-1713330503380)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 26
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值