前言
本文基于大佬 文章并结合个人理解进行了一个简单的流程整理,仅供参考,欢迎交流。
一、Bean的三级缓存及简单bean的实例化过程
在bean的实例化过程中,spring使用了3个缓存,这3个缓存是互斥的,即其中一个存有bean的缓存则其他2两个便没有。具体定义如下:
/** 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 ConcurrentHashMap<>(16);
/** Set of registered singletons, containing the bean names in registration order. */
private final Set<String> registeredSingletons = new LinkedHashSet<>(256);
按个人理解,我将singletonObjects称为3级缓存、earlySingletonObjects称为2级缓存、singletonFactories称为1级缓存。后文将按此定义进行描述。当bean被完全实例化后其将被存入三级缓存。源码如下:
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);
}
}
当用容器初始化完成后会去对非延迟加载的bean进行注册,刚开始时会尝试从这三个缓存中获取bean的实例。源码如下:
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
Object 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 lock
singletonObject = 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();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
如源码所示,其会根据一定的条件依次从3、2、1级缓存中去获取bean。当第一次尝试获取的时候getSingleton(beanName, true)肯定是拿不到bean的,那么它会进入创建bean的流程。在进入bean创建前,还有一个重要步骤就是将这个beanName加入singletonsCurrentlyInCreation供后续getSingleton使用。接着核心流程将会为bean 创建一个1级缓存对象ObjectFactory供后续使用,改2级缓存对象将不会被添加到1级缓存,而是直接使用。源码如下:
// Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
这里的lamda表达式就是ObjectFactory的匿名实现。接着核心流程将调用这个匿名类——singletonFactory.getObject(),并进入doCreateBean流程,该方法是真正创建bean的地方。在此方法中,首先会创建bean的实例,此时的bean实例的属性还未初始化,还是一个比较原始的bean,然后再为bean创建一个1级缓存对象ObjectFactory并将其添加1级缓存中,这个1级缓存对象在解决循环依赖的时候才会被取出并执行它的getObject方法。源码如下:
// 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));
}
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);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
随后便是初始化bean的属性,如果没有循环依赖在后面调用getSingleton(beanName, false)时将还是拿不到bean,也就不会继续后面的循环依赖检测。源码如下:
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
Object singletonObject = this.singletonObjects.get(beanName);// null
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {// true
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {// false
...
return null;
if (earlySingletonExposure) {// true
Object earlySingletonReference = getSingleton(beanName, false); // null
if (earlySingletonReference != null) {// false 循环依赖检测
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
后面便是对bean的实例化完成并进行bean的注册,也即最开始的addSingleton方法。根据个人理解对总的流程进行了如下总结:
二、循环依赖
按个人理解spirng能解决循环依赖的巧妙设计在于2阶段的bean实例化和3级缓存。现假设存在A->B,B->A两个对象相互依赖。A定义在前,A实例化后去初始化属性的时候会去找B的实例,接着B又开始实例化,B实例化的时候去找A的引用。如上文所述A初始化时会添加一个1级缓存对象ObjectFactory,这个对象在B初始化时又去获取A的时候就会被获取到并调用getObject方法。这个时候,获取到A的原始版本并被添加到了2级缓存中。B初始化完成后,那么就能被赋值到A的属性中,A的属性被初始化后就进入A初始化后期对循环依赖的检测流程。在对A进行循环依赖检测的时候,会再次调用getSingleton(beanName, false),此时就能拿到2级缓存里面的A,如果A在前面的被initializeBean代理增强过那么循环依赖检测就会抛异常,如果没有则接着后面的流程完成bean的实例化。
循环依赖检测的代码如下:
if (earlySingletonExposure) {// true
Object earlySingletonReference = getSingleton(beanName, false);// 循环依赖时 2级缓存中取出A
if (earlySingletonReference != null) {
if (exposedObject == bean) {// 增强后 false
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {// 增强后抛异常
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.");
}
}
}
}
spring只支持setter注入的singleton循环依赖:1、prototype不是单例不使用三级缓存,只缓存了beanName作为循环依赖判断。2、构造器注入在第一阶段createWrappedInstance时发生,到达不了三级缓存循环依赖的处理的流程。
发生循环依赖,spring的大致处理流程如下: