spring to resolve circular dependencies mainly through three cache to complete.
/** Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
/** Cache of singleton factories: bean name --> ObjectFactory */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
/** Cache of early singleton objects: bean name --> bean instance */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
In doGetBean, which will first attempt by getSingleton way to get bean.
Object sharedInstance = getSingleton(beanName);
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
/** 首先从singletonObjects获取bean实例 */
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
/**
* 如果singletonObjects还没有此bean,有两种情况
* 1.这个bean正在创建状态,先从earlySingletonObjects获取
* 2.这个bean还没开始创建
*/
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
/**
* 如果earlySingletonObjects还没有此bean,有两种情况
* 1.说明还未被其他bean注入,正在创建状态,先从singletonFactories获取
* 2.该bean还没开始创建
*/
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
//将此bean放到提前缓存到earlySingletonObjects中
this.earlySingletonObjects.put(beanName, singletonObject);
//从singletonObject删除bean
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
The way to do such a thing, and gradually get three bean from the cache:
- First singletonObjects acquired, indicating if instantiated has been completed;
- Otherwise earlySingletonObjects to get, if there is already represented bean, and there is a circular dependency, this bean injected as a property
- Otherwise singletonFactories to obtain, if there is a circular dependency, and this property is the first time the other properties as bean injection
There are so appropriate in a string of code doCreateBean:
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
throws BeanCreationException {
// Instantiate the bean.
//BeanWrapper是用来持有创建出来的Bean对象的
BeanWrapper instanceWrapper = null;
//如果是Singleton先把缓存中同名的清除
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
//这是创建Bean的地方由createBeanInstance来完成
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
mbd.resolvedTargetType = beanType;
// Allow post-processors to modify the merged bean definition.
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
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.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
return getEarlyBeanReference(beanName, mbd, bean);
}
});
}
// Initialize the bean instance.
//这里是对Bean的初始化,依赖注入往往在这里发生,
// 这个exposedObject在初始化处理完后会返回作为依赖注入完成后的bean
Object exposedObject = bean;
try {
populateBean(beanName, mbd, instanceWrapper);
if (exposedObject != null) {
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<String> actualDependentBeans = new LinkedHashSet<String>(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;
}
The core here:
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
return 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)) {
//将beanName存放入singletonFactories中
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
This string of code when creating the bean itself into singletonFactories in registration. So imagine when two bean rely on each other, A will register itself into singletonFactories, it can get when it creates B directly from singletonFactories in so B can successfully create a reference of A, B when A is successfully created that can be get a reference from singletonObjects B, and finally a successfully created.
to sum up
Spring solve the problem through three cache cycle dependent, it will be created when the bean will be registered into a singletonFactory in itself, then create it depends bean will get from this cache when it makes a reference to the bean to be dependent on the creation of finalize their dependence.
For Liezi:
- When creating a single embodiment A, into the cache, and then create it depends Singleton B.
- When creating a single embodiment B, A single injection was found to rely embodiment, a single case and then to load A, this time from the discovery cache A single embodiment being created, thereby obtaining a single embodiment A directly from the cache, complete its dependency injection.
- After completion dependent B single embodiment, A is returned to a single embodiment.
- A single B Example singleton then injected into the completion own domain dependency injection.