Principle analysis of Spring circular dependency
1. What is circular dependency#
When we use Spring, we inject another object into one object, but another object also contains the object. As shown in the figure:
Student contains an attribute of teacher;
The Teacher contains the attribute of student. This creates a circular dependency.
2. Code description#
xml configuration file
testCycle.java
private static void testCycle() { ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("cycle.xml"); Teacher teacher = applicationContext.getBean(Teacher.class); System.out.println(teacher); Student student = applicationContext.getBean(Student.class); System.out.println(student); } public static void main(String[] args) { testCycle(); }
Student.java
public class Student { private Teacher teacher; public Teacher getTeacher() { return teacher; } public void setTeacher(Teacher teacher) { this.teacher = teacher; } }
Teacher.java
public class Teacher { private Student student; public Student getStudent() { return student; } public void setStudent(Student student) { this.student = student; } }
3. Test results#
The teacher output here contains the student object, and the student object also contains the teacher object, and the included objects are not null.
4. Why can circular dependency be explained#
Give a picture first
When Spring creates bean s, they must be created one by one. First of all, I will definitely go through a Teacher/Student life cycle. Take teacher as an example. When Spring goes to getBean(teacher), it will first get it from the container. If it can't get it, it will create the teacher. When the teacher is created, it will assign a value to the teacher's attribute (student). In fact, there is no student object in the container. At this time, it will also create the student object, When the student is created, it will assign a value to the teacher attribute in the student. The teacher has been created before. At this time, it can be obtained by going to getBean(teacher) (Note: the student attribute in the teacher is not assigned at this time). In this way, the student is created, and then it will return to the step of assigning the student attribute of the teacher, At this time, the student has been created and can be obtained with getBean(), so that the teacher object is created. Then go back to the first step to create the student object, where the student object has been created when the teacher is created, and can be obtained directly using getBean(). The same is true when assigning values to the attributes in student. You can get the teacher directly. Since then, the circular dependency has ended.
5. Question#
- How do I go to getBean() when I assign a value to the Teacher attribute student?
- When assigning a value to the attribute teacher in the student, why can getBean() get the teacher?
- Why is the obtained teacher attribute injected for completion?
6. Source code interpretation#
Overall method line#
First look at the source code:
getBean()->doGetBean()#
Getbean() - > doGetBean() is actually doGetBean going to get the bean object
public <T> T getBean(String name, @Nullable Class<T> requiredType, @Nullable Object... args) throws BeansException { return doGetBean(name, requiredType, args, false); } /** * Return an instance, which may be shared or independent, of the specified bean. * Returns an instance of the specified bean, which can be shared or independent. */@SuppressWarnings("unchecked")protected <T> T doGetBean( String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { // When converting beanName to FactoryBean, beanName is & beanName. Here is to remove the & symbol string beanName = transformaedbeanname (name); Object beanInstance; // Eagerly check singleton cache for manually registered singletons. // Check whether the bean instance created in the bean instance exists manually Object sharedInstance = getSingleton(beanName); if (sharedInstance != null && args == null) { if (logger.isTraceEnabled()) { if (isSingletonCurrentlyInCreation(beanName)) { ... Omit code... } beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null); } else { // Fail if we're already creating this bean instance: // We're assumably within a circular reference. // No, if we have created an instance of the bean, we are in a circular reference. Is the current bean being created if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } // Check if bean definition exists in this factory. // Check whether there is a bean definition in the factory BeanFactory parentBeanFactory = getParentBeanFactory(); ... Omit code... if (!typeCheckOnly) { // Tag bean has been created, creating markBeanAsCreated(beanName); } StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate") .tag("beanName", name); try { if (requiredType != null) { beanCreation.tag("beanType", requiredType::toString); } RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); checkMergedBeanDefinition(mbd, beanName, args); // Guarantee initialization of beans that the current bean depends on. // Ensure that the bean on which the current bean depends has been initialized String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) { for (String dep : dependsOn) { if (isDependent(beanName, dep)) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'"); } registerDependentBean(dep, beanName); try { getBean(dep); } catch (NoSuchBeanDefinitionException ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", ex); } } } // Create bean instance. // Create an instance of a bean 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. // Delete the instance from the singleton cache. It may already be here // Through the creation process - allow circular reference resolution // Delete the temporary bean destroysingleton (beanname) that receives any reference to the bean; throw ex; } }); beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } else if (mbd.isPrototype()) { // It's a prototype -> create a new instance. Object prototypeInstance = null; try { beforePrototypeCreation(beanName); prototypeInstance = createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); } ... Omit code... finally { beanCreation.end(); } } return adaptBeanInstance(name, beanInstance, requiredType); }
The bean name passed in here is teacher
doGetBean()->createBean()#
Look separately
// Create bean instance.// Create an instance of the bean 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. // Delete the instance from the singleton cache. It may already be here // Through the creation process - allow circular reference resolution // Delete the temporary bean destroysingleton (beanname) that receives any reference to the bean; throw ex; } }); beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);}
The important point here is that when the bean is not obtained, the createBean method will be called to create the bean. Finally, the doCreateBean method will be used to create the bean
createBean()->doCreateBean()#
Here comes the fourth part of the method line above
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // Instantiate the bean. // BeanWrapper: hold the created BeanWrapper instancewrapper = null; if (mbd.isSingleton()) { instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { /** * Create an instance of the bean * instantiate but not initialize, that is, the bean's properties are not copied */ instanceWrapper = createBeanInstance(beanName, mbd, args); } Object bean = instanceWrapper.getWrappedInstance(); Class<?> beanType = instanceWrapper.getWrappedClass(); if (beanType != NullBean.class) { mbd.resolvedTargetType = beanType; } // Allow post-processors to modify the merged bean definition. // Allow the enhancer 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. // Beans that cache singletons can resolve circular references // Even if the lifecycle interface triggers something like BeanFactoryAware, // Judge whether the current bean needs to be exposed in advance (add singletonFactories cache): the bean is a singleton & cyclic dependency is allowed & the bean is being created 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"); } // Add bean s to singletonFactories, that is, the third level cache, but the properties in this place are not assigned addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } // At this point, the bean has been instantiated and put into the singleton factories cache // Initialize the bean instance. // Initializing an instance of a bean Object exposedObject = bean; try { /** * Fill in the bean and fill in the properties of the bean */ populateBean(beanName, mbd, instanceWrapper); /** * To execute* BeanPostProcessor postProcessBeforeInitialization method for* */ 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 " + " 'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example."); } } } } // Register bean as disposable. // Register bean s as one-time try { registerDisposableBeanIfNecessary(beanName, bean, mbd); } catch (BeanDefinitionValidationException ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); } return exposedObject; }
Explain the doCreateBean method separately
// Instantiate the bean. // BeanWrapper: holds the created Bean BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { /** * Create an instance of the bean * instantiate but not initialize, that is, the bean's properties are not copied */ instanceWrapper = createBeanInstance(beanName, mbd, args); }
Initialize the bean, and this place starts to call the createBeanInstance method to create an instance of the bean
// Eagerly cache singletons to be able to resolve circular references// even when triggered by lifecycle interfaces like BeanFactoryAware.// The bean of the cache singleton can resolve the circular reference / / even if the lifecycle interface triggers beanfactoryaware, // Judge whether the current bean needs to be exposed in advance (add singletonFactories cache): the bean is singleton & circular dependency is allowed & the bean is creating 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"); } // Add beans to singletonFactories, that is, the level-3 cache, but the attribute of this place is addsingletonfactory (beanname, () - > getearlybeanreference (beanname, MBD, bean));}
Remember the addSingletonFactory() method, which is the core of circular dependency
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(singletonFactory, "Singleton factory must not be null"); synchronized (this.singletonObjects) { if (!this.singletonObjects.containsKey(beanName)) { // Put beanname and singletonfactory into the cache of the singleton factory [beanname singletonfactory] this singletonFactories. put(beanName, singletonFactory); // Remove [beanname bean instance] this from the early singleton object cache earlySingletonObjects. remove(beanName); // Add beanname to the registered instance this registeredSingletons. add(beanName); } }}
The singletonFactory stored here is a lambda expression, and ObjectFactory is a function interface. When the getObject method is executed, the stored getEarlyBeanReference(beanName, mbd, bean) will be called
doCreateBean() -> createBeanInstance()#
There's nothing to say here, but to create Teacher objects through reflection
createBeanInstance() -> populateBean()#
Here is the start of assigning values to the created Teacher attribute student
/** * Populate the bean instance in the given BeanWrapper with the property values * from the bean definition. * @param beanName the name of the bean * @param mbd the bean definition for the bean * @param bw the BeanWrapper with bean instance * Allow attribute values to populate Bean instances in BeanWrapper */ @SuppressWarnings("deprecation") // for postProcessPropertyValues post process property values protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) { ... Omit code// Give any instantiaawarebeanpostprocessors the opportunity to modify the // state of the bean before properties are set. This can be used, for example, // to support styles of field injection. // The state of the bean before setting the property, for example // Support field injection if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) { if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) { return; } } } PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null); int resolvedAutowireMode = mbd.getResolvedAutowireMode(); if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) { MutablePropertyValues newPvs = new MutablePropertyValues(pvs); // Add property values based on autowire by name if applicable. Automatically inject the value of the parameter by name if (resolvedAutowireMode == AUTOWIRE_BY_NAME) { autowireByName(beanName, mbd, bw, newPvs); } // Add property values based on autowire by type if applicable. Inject the value of the parameter by type if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) { autowireByType(beanName, mbd, bw, newPvs); } pvs = newPvs; } /** * Is there an instantiated AwareBeanPostProcessor */ boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors(); /** * Whether depth inspection is required */ boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE); if (hasInstAwareBpps) { if (pvs == null) { pvs = mbd.getPropertyValues(); } for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) { PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName); if (pvsToUse == null) { return; } pvs = pvsToUse; } } if (needsDepCheck) { PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); checkDependencies(beanName, mbd, filteredPds, pvs); } if (pvs != null) { // Apply the given property value to resolve any references to the bean when the bean factory runs. You must use deep copy. Therefore, this property will not be permanently modified applyPropertyValues(beanName, mbd, bw, pvs); } }
Separate analysis
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) { if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) { return; } } }
The method postprocessafterinstance of the InstantiationAwareBeanPostProcessor returns boolean. If true is returned, nothing will be done. If false is returned, this class will not be automatically assembled (property filling). Here we can control some beans without property filling through the postprocessor. Obviously, if we don't make special treatment, the return of the innermost if will not be executed.
if (pvs != null) { // Apply the given property value to resolve any references to the bean when the bean factory runs. You must use deep copy. Therefore, this property will not be permanently modified applyPropertyValues(beanName, mbd, bw, pvs); }
Here is the assignment to the student attribute of Teacher
/** * Apply the given property values, resolving any runtime references * to other beans in this bean factory. Must use deep copy, so we * don't permanently modify this property. * @param beanName the bean name passed for better exception information * @param mbd the merged bean definition * @param bw the BeanWrapper wrapping the target object * @param pvs the new property values * Apply the given property value to resolve any runtime references to other beans in this bean factory. You must use deep copy, so we won't permanently change this property */protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) { if (pvs.isEmpty()) { // If PVS does not have propertyValues, end the return directly;} MutablePropertyValues mpvs = null; List<PropertyValue> original; if (pvs instanceof MutablePropertyValues) { mpvs = (MutablePropertyValues) pvs; if (mpvs.isConverted()) { // Shortcut: use the pre-converted values as-is. try { bw.setPropertyValues(mpvs); return; } catch (BeansException ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Error setting property values", ex); } } original = mpvs. getPropertyValueList(); } else { original = Arrays.asList(pvs.getPropertyValues()); } TypeConverter converter = getCustomTypeConverter(); if (converter == null) { converter = bw; } BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter); // Create a deep copy, resolving any references for values. Create a deep copy and resolve any reference value list < propertyvalue > deepcopy = new ArrayList < > (original. Size()); boolean resolveNecessary = false; for (PropertyValue pv : original) { if (pv.isConverted()) { deepCopy.add(pv); } Else {/ / get the name of the property String propertyName = pv.getName(); / / get the value of the property object originalvalue = PV. Getvalue(); if (originalvalue = = autowiredpropertymarker. Instance) {method writemethod = BW. Getpropertydescriptor (propertyname). Getwritemethod(); if (writemethod = = null) { throw new IllegalArgumentException("Autowire marker for property without write method: " + pv); } originalValue = new DependencyDescriptor(new MethodParameter(writeMethod, 0), true); } // Resolve attribute value object resolvedvalue = valueresolver resolveValueIfNecessary(pv, originalValue); Object convertedValue = resolvedValue; boolean convertible = bw. isWritableProperty(propertyName) && ! PropertyAccessorUtils. isNestedOrIndexedProperty(propertyName); if (convertible) { convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter); } // Possibly store converted value in merged bean definition, // in order to avoid re-conversion for every created bean instance. if (resolvedValue == originalValue) { if (convertible) { pv.setConvertedValue(convertedValue); } deepCopy. add(pv); } else if (convertible && originalValue instanceof TypedStringValue && !((TypedStringValue) originalValue). isDynamic() && ! (convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) { pv.setConvertedValue(convertedValue); deepCopy.add(pv); } else { resolveNecessary = true; deepCopy.add(new PropertyValue(pv, convertedValue)); } } } if (mpvs != null && !resolveNecessary) { mpvs.setConverted(); } // Set our (possibly massaged) deep copy. try { bw.setPropertyValues(new MutablePropertyValues(deepCopy)); } catch (BeansException ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Error setting property values", ex); }}
Resolve attribute values
Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
[image upload failed... (image-d0fd53-1646299617684)]
Here we will go directly to the resolveReference method
/** * Resolve a reference to another bean in the factory.Resolve a reference to another bean */@Nullableprivate Object resolveReference(Object argName, RuntimeBeanReference ref) { try { // Used to store the instantiated bean Object bean; // Get bean type class <? > beanType = ref.getBeanType(); If (ref.istoparent()) {beanfactory parent = this. Beanfactory. Getparentbeanfactory(); if (parent = = null) {... Omit code...} Else {string resolvedname; if (beantype! = null) {... Omit code...} Else {resolvedname = string. Valueof (doevaluate (ref.getbeanname()); / / get the bean object of resolvedname bean = this.beanFactory.getBean(resolvedName);}// Register the dependent bean this beanFactory. registerDependentBean(resolvedName, this.beanName); } if (bean instanceof NullBean) { bean = null; } return bean; } catch (BeansException ex) { throw new BeanCreationException( this.beanDefinition.getResourceDescription(), this.beanName, "Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, ex); }}
The method will go here. The getBean before getBean() hasn't finished yet. Does it go to getBean(), which is a dolly from here.
resolvedName = String.valueOf(doEvaluate(ref.getBeanName())); // Get the bean object of resolvedname bean = this beanFactory. getBean(resolvedName); }// Register dependent beans
Here, you will find the instance of student and go through the previous method. However, when you go to the pupolate() method, you will assign a value to the teacher attribute of student and get a teacher from the container. Remember the teacher in singletonFactories before? When you get it here, you will directly get the previous stored teacher. Let's have a look
Omit the operation of creating a logic before and directly to the assignment
Here we start to get the teacher object. Let's see how the getSingleton() method gets it;
protected Object getSingleton(String beanName, boolean allowEarlyReference) { // Example: get the object from the single object cache singletonObjects. get(beanName); // If the singleton object is not found, And the bean is being created. If (singletonobject = = null & & issingletoncurrent yincreation (beanname)) {/ / get the singleton object from the early singleton object cache (the reason why it becomes an early singleton object is that the objects / / in earlysingletonobjects are created through the objectfactory exposed in advance. No operations such as property filling have been performed yet) singletonObject = this.earlySingletonObjects.get(beanName); // If (singletonobject = = null & & alloweearlyreference) {/ / if it is empty, the global variable will be locked for processing. Synchronized (this. Singletonobjects) {/ / consistent creation of early reference within full singleton lock / / the early reference singletonobject = this.singletonobjects.get (beanname); if (singletonobject = = null) {singletonObject = this.earlySingletonObjects.get(beanName); if (singletonobject = = null) {/ / when some methods need to be initialized in advance, the addSingletonFactory method will be called to store the corresponding objectfactory initialization policy in singletonfactories. Objectfactory <? > singletonfactory = this.singletonfactories.get (beanname); if (singletonfactory! = null) {/ / if there is a singleton object factory, use the factory to create a singleton object singletonObject = singletonFactory.getObject(); / / the singleton object created is placed in this.earlySingletonObjects.put(beanName, singletonObject) in the earlier singleton object cache ; // Remove the corresponding singleton object factory this singletonFactories. remove(beanName); } } } } } } return singletonObject;}
Here we get the lambda expression stored in singletonFactoriesMap before, and call getObject() method to execute getEarlyBeanReference method
/** * 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 * * Getting a reference to an earlier access to a specified bean is often used to resolve circular dependencies */protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) { // By default, the final exposed object is a bean. The common object created by createBeanInstance is Object exposedObject = bean; // mbd's synthetic attribute: sets whether the bean definition is synthetic. Generally, it means that only the pointCut configuration or advice configuration related to AOP can set synthetic to true // If mbd is not synthetic and this factory has instantiaawarebeanpostprocessor if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { // Traverse all post processors of the factory and get smartinstantiationaware ArrayList for (smartinstantiationawarebeanpostprocessor BP: getbeanpostprocessorcache() smartInstantiationAware) { // Let the exposedObject object be reported and installed through each smartInstantiationAwareBeanPostProcessor exposedObject = bp.getEarlyBeanReference(exposedObject, beanName); } } // Returns the object after the final hierarchical loading return exposedObject;}
There is nothing to explain about this method. The annotation clearly shows the function of the method
After getting the teacher, assign a value to the teacher attribute in the Student
resolveNecessary = true;deepCopy.add(new PropertyValue(pv, convertedValue));
After the student object is created, the created Student object will be placed in the
try { // Get the bean object from the container singletonObject = singletonFactory.getObject(); newSingleton = true;} catch (IllegalStateException ex) { // Has the singleton object implicitly appeared in the meantime -> // if yes, proceed with it since the exception indicates that state. singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { throw ex; } } catch (BeanCreationException ex) { if (recordSuppressedExceptions) { for (Exception suppressedException : this.suppressedExceptions) { ex.addRelatedCause(suppressedException); } } throw ex;}finally { if (recordSuppressedExceptions) { this.suppressedExceptions = null; } afterSingletonCreation(beanName); } if (newSingleton) { // Add the mapping relationship between beanName and singletonObject to the singleton cache of the factory addSingleton(beanName, singletonObject); }
addSingleton(beanName, singletonObject); Put in cache
At this point, you will return to assign a value to the student attribute in the Teacher. At this point, the one-time circular dependency is completed. Spring also goes back to creating the student object, but this time there is something in the container. Just take it out directly.
Question answer#
Why do you need to create a Student object at the last time? Because you need to use an instance of Student when creating a Teacher object, you need to create a Student object at the beginning. But the Student object will not be created at the last time. You can get it directly from the cache singletonObjects.