转载:
浅谈一下SpringIOC解决循环依赖_ioc循环依赖-CSDN博客
循环以来解决办法主要流程图
三级缓存源码
类:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry
/** Cache of singleton objects: bean name to bean instance. */
一级缓存:完整的初始化完成并且设置完属性的对象
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
三级缓存:ObjectFactory代理对象
/** 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 HashMap<>(16);
保存所有单例bean的名字
/** Set of registered singletons, containing the bean names in registration order. */
private final Set<String> registeredSingletons = new LinkedHashSet<>(256);
根据源码从创建并初始化对象开始分析
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, java.lang.Object[])
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
热切地缓存singleton,以便即使在由BeanFactoryAware等生命周期接口触发时也能够解析循环引用。
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
// 如果默认的作用域是单例 && 允许循环以来 && 当前的bean对象在创建中
// 应该是可以配置是否开启二级缓存的
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");
}
//getEarlyBeanReference获取早期对象,早期——也就是说,在目标bean实例完全初始化之前。
//传入一个lambd表达式
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
/**
获取对指定bean的早期访问的引用,通常用于解析循环引用。
* Obtain a reference for early access to the specified bean,
* typically for the purpose of resolving a circular reference.
* @param beanName the name of the bean (for error handling purposes)
* @param mbd the merged bean definition for the bean
* @param bean the raw bean instance
* @return the object to expose as bean reference 返回 暴露出当前对象的引用
*/
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
//不是合成的类 && 此工厂是否持有InstantiationAwareBeanPostProcessor
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
//遍历bean的后置处理器
for (BeanPostProcessor bp : getBeanPostProcessors()) {
//如果是SmartInstantiationAwareBeanPostProcessor类的对象,并且返回对应的后置处理器的对象
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
这里说明首先要将该对象加入到三级缓存中。
//beanName,lambd表达式
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
//如果一级缓存不包含当前的对象,那么就将当前对象add到三级缓存中singletonFactories
//也要清空一下earlySingletonObjects 二级缓存
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
Object earlySingletonReference = getSingleton(beanName, false);
/** 返回当前的对象
* Return the (raw) singleton object registered under the given name.
* <p>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) {
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;
}
答疑
参考:springIoc依赖注入循环依赖三级缓存_springioc三级缓存-CSDN博客
为什么三级缓存可以解决aop下的循环依赖问题?三级缓存到底有什么作用?
1 在同一个容器中,能否出现同名的不同对象?
不能,id是唯一标识
2,在同一个容器中,按照标准的生命周期,先创建了原始对象,后续又创建了代理对象,那时会怎么办?
当创建了代理对象之后,应该要使用代理对象的,但是原始对象已经存在,那么应该将原始对象给覆盖掉。getEarlyBeanReference()方法执行的逻辑是一样的
3 三级缓存到底有什么作用,为什么存在代理对象的时候就要使用三级缓存呢?
在标准的bean的生命周期中,要先创建出原始对象,创建出原始对象之后要使用populateBean方法来完成属性的赋值,此时赋值的对象是原始对象,因为代理对象还没有创建,代理对象的创建步骤是在BeanPostProcessor的后置处理方法中(也就是getEarlyBeanReference中的逻辑),也就是说已经完成赋值之后代理对象才创建出来,所以会报错(this means that said other beans do not use the final version of the bean),如何解决这个问题呢?
需要将代理对象的创建前置,也就是说在对象赋值的那一刻,要唯一性的确定出到底是原始对象还是代理对象,所以会优先把所有的bean都放到三级缓存中,在需要进行对象赋值的时候,从三级缓存中取出lambda表达式,lambda表达式的执行逻辑就是确定原始对象还是代理对象。如果是原始对象就赋值原始对象,如果是代理对象就赋值代理对象。
所以说populateBean 执行逻辑在addSingletonFactory方法和 getEarlyBeanReference方法之后
// Initialize the bean instance.
Object exposedObject = bean;
try {
//执行逻辑在addSingletonFactory方法之后
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
4 @Resource注解在Spring中的应用问题
@Resource
注解通常用于注入 Spring 容器中的资源。它不能直接处理循环依赖,因为 Spring 容器在创建 bean 时使用的是懒加载策略,即在实际使用 bean 时才会创建和初始化它。如果两个 bean 相互依赖对方,就会形成循环依赖,这在没有额外配置的情况下是无法解决的。
Spring 提供了 ApplicationContextAware
接口或 @Autowired
注解来处理循环依赖,前提是你需要在 bean 的 setter 方法或构造函数中显式声明依赖。
例如,使用 @Autowired
注解:
javapublic class A {
private B b;
@Autowired
public A(B b) {
this.b = b;
}
// ...
}
public class B {
private A a;
@Autowired
public B(A a) {
this.a = a;
}
// ...
}
在这个例子中,@Autowired
注解告诉 Spring 容器在创建 A
或 B
的实例时,需要注入对方的依赖,这样就可以在构造函数执行时解决循环依赖。
但是,如果你坚持要使用 @Resource
注解来处理循环依赖,这在语义上是不正确的,因为 @Resource
注解主要用于按名称或类型注入资源,而不是用来解决循环依赖。