在AbstractBeanFactory中
getSingleton方法中,
调beforeSingletonCreation方法,
将beanName添加到defaultSingletonBeanRegistry的成员变量singletonCurrentlyInCreation集合中。
然后调singletonFactory.getObject方法,调到匿名beanFactory实例,
AbstractBeanFactory
getBean
doGetBean
{匿名beanFactory实例:
lambda.doGetBean {
AbstractAutowireCapableBeanFactory.createBean
}
}
创建过程:【
AbstractAutowireCapableBeanFactory.doCreateBean
// 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);
}
createBeanInstance
instantiateBean
SimpleInstantiationStrategy.instantiate
BeanUtils.instantiateClass
】
DefaultSingletonBeanRegistry.addSingleton方法
将已创建的Bean加入到容器。
三级缓存中的创建的Bean的移动
/** 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 HashMap<>(16);
在不存在循环依赖的时候, Bean创建过程的最后一步从singletonFactories中移除,添加到singletonObjects
/**
* Add the given singleton object to the singleton cache of this factory.
* <p>To be called for eager registration of singletons.
* @param beanName the name of the bean
* @param singletonObject the singleton object
*/
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
存在循环依赖的时候,先放在三级缓存singletonFactories,
当被其他Bean调用的之后从singletonFactories移除,增加到二级缓存earlySingletonObjects,
最后从earlySingletonObjects移除,增加到三级缓存singletonObjects。
解决循环依赖的过程,主要用到了singletonFactories,不要earlySingletonObjects行不行呢?
在Spring解决循环依赖的过程中,singletonFactories
和earlySingletonObjects
都起着重要作用,通常不能完全不用earlySingletonObjects
,以下是具体分析:
singletonFactories
的作用
- 存储对象工厂:
singletonFactories
是一个ObjectFactory
类型的三级缓存,用于存储创建单例Bean的工厂对象。当一个Bean正在创建过程中,还未完全初始化完成时,会将其对应的对象工厂放入该缓存中。 - 生成早期引用:通过调用对象工厂的
getObject
方法,可以获取到Bean的早期引用。这个早期引用可以在其他Bean依赖该Bean时进行注入,从而解决循环依赖问题。
earlySingletonObjects
的作用
- 存储早期Bean实例:
earlySingletonObjects
也是一个用于解决循环依赖的缓存,它存储的是已经创建但还未完全初始化的Bean实例的早期引用。当从singletonFactories
中获取到对象工厂并调用getObject
方法生成早期引用后,会将这个早期引用放入earlySingletonObjects
缓存中。 - 避免重复创建:在后续的依赖注入过程中,如果再次需要获取该Bean的早期引用,会直接从
earlySingletonObjects
缓存中获取,而不需要再次调用对象工厂的getObject
方法,提高了性能并避免了可能的重复创建问题。
不能完全不用earlySingletonObjects
的原因
- 性能优化:如果没有
earlySingletonObjects
缓存,每次获取Bean的早期引用都需要从singletonFactories
中获取对象工厂并调用getObject
方法,这会带来一定的性能开销。尤其是在存在复杂的循环依赖关系时,频繁的工厂调用会降低系统的运行效率。 - 保证一致性:在多线程环境下,
earlySingletonObjects
可以保证在同一时间内,对于同一个正在创建的Bean,所有线程获取到的早期引用是一致的。如果没有这个缓存,可能会导致不同线程获取到不同的早期引用,从而引发数据不一致等问题。
虽然singletonFactories
在解决循环依赖中是关键,但earlySingletonObjects
也不可或缺,它们共同协作来高效、正确地处理循环依赖问题。
构造方法相互依赖是三级缓存机制都解决不了的循环依赖
class BeanA {
private BeanB b;
public BeanA(BeanB b) {
this.b = b;
}
}
class BeanB {
private BeanA a;
public BeanB(BeanA a) {
this.a = a;
}
}
你所写的这种直接在构造函数中相互依赖注入的方式在Spring中是不行的,会导致循环依赖问题无法正常解决,原因如下:
Spring解决循环依赖的机制限制
- Spring在处理循环依赖时,主要依靠三级缓存机制。当创建一个Bean时,会先将其早期引用放入缓存中,以便在其他Bean依赖它时可以获取到这个早期引用进行注入。但这种机制在构造函数相互依赖的情况下存在问题,因为在构造函数执行时,Bean还未完全创建完成,此时如果直接将未完成创建的Bean作为参数传递给另一个Bean的构造函数,就会导致依赖注入失败。
解决方案
使用@Lazy
注解
可以在其中一个Bean的依赖注入处使用@Lazy
注解,延迟该Bean的初始化,直到真正需要使用时才进行创建。例如:
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
@Component
public class BeanA {
private final BeanB b;
public BeanA(@Lazy BeanB b) {
this.b = b;
}
}
@Component
public class BeanB {
private final BeanA a;
public BeanB(BeanA a) {
this.a = a;
}
}
在上述代码中,BeanA
的构造函数中注入BeanB
时使用了@Lazy
注解,这样BeanB
在BeanA
构造函数被调用时不会立即创建,而是在第一次使用BeanB
时才进行创建,从而避免了构造函数循环依赖的问题。
使用属性注入或方法注入
将依赖注入方式从构造函数注入改为属性注入或方法注入,这样可以让Spring在处理循环依赖时有更多的灵活性。例如:
import org.springframework.stereotype.Component;
@Component
public class BeanA {
private BeanB b;
public void setB(BeanB b) {
this.b = b;
}
}
@Component
public class BeanB {
private BeanA a;
public void setA(BeanA a) {
this.a = a;
}
}
然后在配置类中通过@Bean
注解的方法来进行依赖注入的配置:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
public BeanA beanA() {
return new BeanA();
}
@Bean
public BeanB beanB() {
return new BeanB();
}
@Bean
public void configure(BeanA a, BeanB b) {
a.setB(b);
b.setA(a);
}
}
在上述代码中,BeanA
和BeanB
使用了属性注入的方式,通过AppConfig
配置类中的configure
方法来手动完成依赖注入,避免了在构造函数中直接相互依赖的问题。