Precursors:
DoCreateBean creates a bean with no attributes
DoCreateBean handles @autoWired and @Value tags
DoCreateBean complete source code as follows:
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean.
// The bean instance wraps the class
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
// Never finish cleaning up the created wrapper Bean cache and get the associated wrapper Bean instance. After all, it is a singleton and can only be stored in one copy
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
When creating a bean, there are three ways to create an instance of the bean
// 1. Factory method creation
// 2. Constructor method for injection
// 3. No arguments constructor injection
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
finalObject bean = instanceWrapper.getWrappedInstance(); Class<? > beanType = instanceWrapper.getWrappedClass();if(beanType ! = NullBean.class) { mbd.resolvedTargetType = beanType; }// Allow post-processors to modify the merged bean definition.
// Call the BeanPostProcessor after the BeanDefinition attribute is merged
synchronized (mbd.postProcessingLock) {
if(! mbd.postProcessed) {try {
// Attributes tagged by @autoWired and @Value are retrieved here
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.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);
}
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<>(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;
}
Copy the code
Following the position analyzed in the article, proceed with:
// Cache singleton Bean objects in the container to prevent circular references
// Determine if it is an earlier referenced bean and, if so, allow it to expose the reference ahead of time
// There are three main logic in this judgment:
// 1. Check whether it is a singleton
// 2. Whether to allow circular references
// 3. Whether the bean is being created
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
Copy the code
And then it will come
// This is an anonymous inner class that holds references to objects as early as possible to prevent circular references
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
Copy the code
Go to addSingletonFactory:
protected void addSingletonFactory(String beanName, ObjectFactory
singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
// Add to level 3 cache
this.singletonFactories.put(beanName, singletonFactory);
// Remove the Bean from the level 2 cache
this.earlySingletonObjects.remove(beanName);
// Register the order of the singleton
this.registeredSingletons.add(beanName); }}}Copy the code
Remove the Bean from the level 2 cache and add it to the level 3 cache as a singletonFactory instance
An important point here is that the truth about Spring’s solution to loop dependencies is in this source code: Here the beanFactory is put in
singletonFactories
At this time, the bean is only the bean that has completed the initial construction. The bean that has not been injected with set or annotation is an intermediate state of the bean, but it can be recognized by people. Therefore, Spring exposes this object in advance for people to know and use.
Go back to the doCreateBean and go to the getEarlyBeanReference
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if(! mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {for (BeanPostProcessor bp : getBeanPostProcessors()) {
/ / if it is SmartInstantiationAwareBeanPostProcessor type, processing,
// Return the default instance if there is no related processing.
// The AbstractautoXyCreator class is the key to the rest of AOP
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
// Where to AOP wrap and return a singleton!exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName); }}}return exposedObject;
}
Copy the code
Note that this method is not in doCreateBean
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
Copy the code
This line is just registered with the getEarlyBeanReference, and is actually executed in the doCreateBean method of AbstractBeanFactory where we tried to fetch the bean from the cache.
Back to doCreateBean, the bean is retrieved and stored only in the level 3 cache. And then it will execute
populateBean(beanName, mbd, instanceWrapper);
Copy the code
To actually inject the bean’s properties into it, and then call initializeBean for a complete initialization
// Initialize the bean instance.
// Initialization of the Bean object, where dependency injection is triggered
// This exposedObject is returned as the dependency injection completed Bean after initialization
Object exposedObject = bean;
try {
// Populates the properties of the bean instance
populateBean(beanName, mbd, instanceWrapper);
// Initialize the bean as follows:
// 1. Determine whether the BeanNameAware, BeanClassLoaderAware, BeanFactoryAware methods are implemented, if so, set the relevant attributes
// 2. Invoke the BeanPostProcessor operation for bean initialization
// 3. Execute the initialization method
// afterPropertiesSet is called if there is an initializingBean
// If InitMethod is present, the initial method is called
// 4. Invoke the BeanPostProcessor operation for bean initialization
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
Copy the code
Then determine whether the bean is allowed to be exposed in advance,
if (earlySingletonExposure) {
// Gets the registered singleton Bean object with the specified name
Object earlySingletonReference = getSingleton(beanName, false);
if(earlySingletonReference ! =null) {
// If the bean returned after initializeBean execution is the same (not the proxy object instance, i.e. not enhanced)
if (exposedObject == bean) {
// Ensure that the registered Bean obtained by name is the same as the Bean being instantiatedexposedObject = earlySingletonReference; }... }}Copy the code
The getSingleton method is called in if:
@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;
}
Copy the code
This is the same method as before, since the bean is already in the level 3 cache, it will be fetched from the level 3 cache
singletonObject = singletonFactory.getObject();
Copy the code
Once the bean instance is acquired, it is removed from the level 3 cache
this.singletonFactories.remove(beanName);
Copy the code
Back to the doCreateBean,
Object earlySingletonReference = getSingleton(beanName, false);
Copy the code
Judge after you execute
if (exposedObject == bean) {
// Ensure that the registered Bean obtained by name is the same as the Bean being instantiated
exposedObject = earlySingletonReference;
}
Copy the code
exposedObject
Because I called it beforeinitializeBean
Method, so it might be assigned to a new object, and once it’s assigned to a new object, if inside is false, goes to the previous oneinitializeBean
In the methodFocus on theapplyBeanPostProcessorsAfterInitialization
Method, into method:The focus on implementation postProcessAfterInitialization method AbstractAutoProxyCreator classes
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if(bean ! =null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) ! = bean) {returnwrapIfNecessary(bean, beanName, cacheKey); }}return bean;
}
Copy the code
This method attempts to fetch the bean instance from the cache and return it, that is, if it was previously processed, ensuring that the processed bean instance is the same as the one previously processed.
Back to the doCreateBean,
if (exposedObject == bean) {
// Ensure that the registered Bean obtained by name is the same as the Bean being instantiated
exposedObject = earlySingletonReference;
}else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
// Get the Bean instance that depends on the current Bean
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."); }}Copy the code
If it is not the same bean, it is possible that the same bean instance is not returned after initializeBean. This problem occurs because postProcessor is also used by users, who may have modified the bean instance in their custom logic. User behavior is unpredictable to the system.
If not, the populate method checks if any other beans depend on the populate bean. If it does, an error is reported. The populate method indicates that the other bean instances went away and set up the instance.
After validation, the destruction callback logic is registered
registerDisposableBeanIfNecessary(beanName, bean, mbd);
Copy the code