欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 国际 > Spring源码-循环依赖

Spring源码-循环依赖

2024/10/23 18:19:38 来源:https://blog.csdn.net/m0_45164511/article/details/142720584  浏览:    关键词:Spring源码-循环依赖

核心流程永:

A创建的时候,发现依赖了B,那就创建B.

B在创建的过程中,需要注入A,那就去单例池找A,如果找不到,那就去creatingSet中找A,如果存在A,那就说明存在循环依赖。判断A需不需要进行AOP,如果需要AOP,那就提前进行AOP;如果不需要,那就返回原始对象。

如果提前AOP,那就创建代理对象,并将创建出来的代理对象放到二级缓存中,二级缓存保证了代理对象只会被创建一次。

三级缓存

  1. singletonObjects 单例池(完整Bean生命周期)
  2. earlySingletonObjects (半成品)早期的单例池(普通对象或者代理对象),存放到是三级缓存执行后的结果
  3. singletonFactory (ObjectFactory)临时存放Lambda表达式<普通对象>,可以生成代理对象,放到二级缓存(生成后就清空)

使用到的核心方法:

1.addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)):三级缓存存放beanName和lambda表达式,lambda表达式存放原始对象,并且通过该lambda表达式可以生成AOP的代理对象

2.getSingleton():找bean--涉及一级缓存、二级缓存、三级缓存

说明:@EnableAOP就是生成一个BeanPostProcessor,然后AOP依赖BeanPostProcessor来生成代理对象

例子:

A a= new A();a.b = b;B b = new B();b.a = a;

说明:

1.A在创建的时候,在属性填充的时候,会调用getSingleton找B,此时B还没有创建,那这个方法就直接返回,去创建B

2.B在属性填充的时候,也会进到这个方法(getSingleton),去一级缓存,二级缓存拿,拿不到就加锁,去三级缓存拿,拿到lambda表达式,执行lambda表达式,拿到A的原始对象或者创建A的代理对象或者,完成B的创建,将B放到单例池。

3.然后A从单例池拿到B,完成后续创建流程。

如果不存在代理的逻辑,那么两级缓存就能解决循环依赖的问题

如果在初始化后要进行AOP,那么就需要在初始化后生成代理对象,如果存在循环依赖的问题,就需要提前进行AOP。

我们怎么知道出现了循环依赖呢?

在一开始就记录了creatingSet<>(正在创建中的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));// isSingletonCurrentlyInCreation正在创建中if (earlySingletonExposure) {if (logger.isTraceEnabled()) {logger.trace("Eagerly caching bean '" + beanName +"' to allow for resolving potential circular references");}// 循环依赖-添加到三级缓存addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));}

1.Lambda表达式的执行

DefaultSingletonBeanRegistry#getSingleton

@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {// Quick check for existing instance without full singleton lockObject 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 locksingletonObject = 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();// 执行三级缓存的lambda表达式this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);// 用完 lambda表达式就移除}}}}}}return singletonObject;
}

执行lambda表达式,那就会调用getEarlyBeanReference(在类AbstractAutoProxyCreator)

在这个方法中,earlyProxyReferences.put:记录是否进行了AOP,如果进行了AOP,就放到这个Map中

然后调用wrapIfNecessary:判断是否需要AOP,需要就createProxy(创建代理对象)

正常进行AOP的地方(初始化后):AbstractAutoProxyCreator#postProcessAfterInitialization()

如果提前进行了AOP,那么这个方法不会再AOP,直接返回原始对象;之后去二级缓存找到代理对象,完成A的创建。

 

@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {if (bean != null) {Object cacheKey = getCacheKey(bean.getClass(), beanName);if (this.earlyProxyReferences.remove(cacheKey) != bean) {// 如果AOP后发现bean发生改变return wrapIfNecessary(bean, beanName, cacheKey);}}return bean;
}

2.几种循环依赖的情况

Enable注解就是生成一个BeanPostProcessor(不完全正确),比如@EnableTransactional没有新生成beanPostProcessor,添加的是Advisor。

2.1@Async会导致暴露的对象改变

if (earlySingletonExposure) {Object earlySingletonReference = getSingleton(beanName, false);if (earlySingletonReference != null) {if (exposedObject == bean) { // 如果有些postProcessor直接创建了bean,那这里就不一样exposedObject = earlySingletonReference;}else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {// beanName被哪些bean依赖了,现在发现beanName所对应的bean对象发生了改变,那么则会报错String[] dependentBeans = getDependentBeans(beanName);Set<String> 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 " +"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");}}}
}

(AOP和@Async都存在的情况下,并且存在循环依赖,那就会抛异常)

@Async的大致执行原理:创建代理对象,异步开一个线程去完成操作的。和AOP是不同的

@EnableAsync就会新增一个postProcessor,这个postProcessor在AOP之后执行

Async会生成一个新的代理对象,那么此时exposedObject就发生改变了。这样就会导致赋值给其他Bean的和放到单例池的不是同一个Bean

解决办法:加了lazy注解之后,会给bService直接生成一个代理对象给A赋值。之后真正用到bService的时候,才会去找A,或者创建B的bean,比如这里的test用到bService

说明:A中加Async导致出错,是因为A依赖B,需要创建B,B在创建的时候用到了A的代理对象,赋值给B的这个代理对象和A最终生成的对象(A最终放到单例池的对象)不是同一个,所以报错

说明:把Async写到bService,就不会报错,此时是因为,B需要拿到完整的A,拿到A之后,就可以进行初始化、AOP以及Async,之后会生成完整的B。

2.2两个原型Bean产生的循环依赖

两个都是原型bean,那他们产生的循环依赖没有办法解决;如果只有一个原型Bean,那么不会报错

两个原型bean的原因:他们会 互相 一直创建彼此,导致死循环

2.3实例化使用构造方法产生的循环依赖

解决办法:通过@Lazy注解来解决,Spring会创建一个aService的代理对象来给B的构造方法使用

2.4@Transactional

不会报错,@Transactional因为没有新生成beanPostProcessor,添加的是Advisor,所以最后暴露的对象是同一个

2.5自己注入自己

不会出现问题

执行流程:

1.实例化A,存到三级缓存

2.注入A,先去一级缓存找A,再去二级缓存找A,再去三级缓存找A,此时肯定能找到A(或者是A的普通对象,或者是A的代理对象),那么完成A的注入

3.继续创建,完成A的创建

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com